Merge branch 'master' into issue/397

This commit is contained in:
Igor Gassmann 2018-01-08 00:51:34 -03:00
commit 094c3b8a9f
73 changed files with 845 additions and 657 deletions

View file

@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.19.1
current_version = 0.19.4rc2
commit = True
tag = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)((?P<release>[a-z]+)(?P<candidate>\d+))?

View file

@ -1,7 +1,5 @@
{
"plugins": [
"flowtype"
],
"plugins": ["flowtype"],
"extends": [
"airbnb",
"plugin:import/electron",
@ -28,7 +26,8 @@
"app": true
},
"rules": {
"import/no-commonjs": 1,
"import/no-amd": 1
"import/no-commonjs": "warn",
"import/no-amd": "warn",
"func-names": ["warn", "as-needed"]
}
}
}

View file

@ -8,15 +8,17 @@ Web UI version numbers should always match the corresponding version of LBRY App
## [Unreleased]
### Added
* Added copy address button to the Wallet Address component on Send / Receive (#875)
* Video state tracking in redux - developer only (#890)
*
### Changed
*
* Improved text content in app (#921)
*
### Fixed
*
* Right click works in the app again (#928)
* Icons are now the rights size (#925)
* Fixed tip sending error (#918)
*
### Deprecated
@ -27,6 +29,40 @@ Web UI version numbers should always match the corresponding version of LBRY App
*
*
## [0.19.3] - 2017-12-30
### Changed
* Improved internal code structuring by adding linting integration -- developers only (#891)
* Improved developer documentation (#910)
### Removed
* Removed email verification reward (#914)
### Fixed
* Added snackbar text in place where it was coming up blank (#902)
## [0.19.2] - 2017-12-22
### Added
* Added copy address button to the Wallet Address component on Send / Receive (#875)
* Link to creators channels on homepage (#869)
* Pause playing video when file is opened (#880)
* Add captcha to verification process (#897)
### Changed
* Contributor documentation (#879)
### Fixed
* Linux app categorization (#877)
## [0.19.1] - 2017-12-13
### Added

View file

@ -1,57 +1,165 @@
# Contributing to LBRY
# Contribute to LBRY
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.
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
* [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 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
## Choose an Issue
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.
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!
Since LBRY is based on a decentralized community, we believe that the app will be stronger 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:
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.
* 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
| 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.
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.
Although all contributions should have good UX, the [UX label, when applied in conjunction with Help Wanted](https://github.com/lbryio/lbry-app/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+label%3Aux+no%3Aassignee), indicates that the contributor ought to implement the feature in a creative way that specifically focuses on providing a good user experience. These issues often have no set instruction for how the experience should be and leave it to the contributor to figure out. This may be challenging for people who do not like UX, but also more fun and rewarding for those who do.
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.
## Develop
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.
The project comes with diverse tools for simplifying the development process and for providing
better code quality. It's recommended to make use of them thoroughly during ongoing development.
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.
We follow the well-known [Airbnb JavaScript Style Guide](http://airbnb.io/javascript/) for defining
our styling rules and code best practices.
### Run
LBRY app can be run for development by using the command:
`yarn dev`
This will launch the app and provide HMR (Hot Module Replacement). Any change made to the sources
will automatically reload the app without losing its state.
### Lint
Code linting is ensured by [ESLint](https://eslint.org/).
You can lint all the project's sources at any time by running:
`yarn lint`
If you desire to lint a specific file or directory you can use `yarn eslint 'glob/pattern'`.
In addition to those commands, staged files are automatically linted before commit. Please take the
time to fix all staged files' linting problems before committing or suppress them if necessary.
If you want the linting problems to show up on your IDE or text editor, check out
[ESLint integrations](https://eslint.org/docs/user-guide/integrations).
### Code Formatting
Project's sources are formatted using [Prettier](https://prettier.io/).
Staged files are automatically formatted before commit.
You can also use the following command:
`yarn format`
for applying formatting rules to all project's code sources. For formatting a specific file or
directory use `yarn prettier 'glob/pattern'`.
Editor integrations are available [here](https://prettier.io/docs/en/editors.html).
### Debug
There are a few tools integrated to the project that will ease the process of debugging:
* [Chrome DevTools](https://developer.chrome.com/devtools)
* Also available for the main process as a [remote target](chrome://inspect/#devices).
* [Electron Devtron](https://electronjs.org/devtron)
* [React DevTools](https://github.com/facebook/react-devtools)
* [Redux DevTools](https://github.com/gaearon/redux-devtools)
## Submit a Pull Request
* 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.
[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
# Get in Touch
Join our discord [here](https://chat.lbry.io/).
| 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

108
README.md
View file

@ -1,77 +1,119 @@
# LBRY App
This is a graphical browser for the decentralized content marketplace provided by the [LBRY](https://lbry.io) protocol. It is essentially the [lbry daemon](https://github.com/lbryio/lbry) bundled with a UI using [Electron](http://electron.atom.io/).
The LBRY app is a graphical browser for the decentralized content marketplace provided by the
[LBRY](https://lbry.io) protocol. It is essentially the
[lbry daemon](https://github.com/lbryio/lbry) bundled with a UI using
[Electron](http://electron.atom.io/).
![App Screenshot](https://lbry.io/img/lbry-ui.png)
![App screenshot](https://lbry.io/img/lbry-ui.png)
## Installing
We provide installers for Windows, macOS, and Debian-based Linux.
| | Windows | macOS | Linux |
| --- | --- | --- | --- |
| Latest Stable Release | [Download](https://lbry.io/get/lbry.exe) | [Download](https://lbry.io/get/lbry.dmg) | [Download](https://lbry.io/get/lbry.deb) |
| Latest Prerelease | [Download](https://lbry.io/get/lbry.pre.exe) | [Download](https://lbry.io/get/lbry.pre.dmg) | [Download](https://lbry.io/get/lbry.pre.deb) |
| | Windows | macOS | Linux |
| --------------------- | -------------------------------------------- | -------------------------------------------- | -------------------------------------------- |
| Latest Stable Release | [Download](https://lbry.io/get/lbry.exe) | [Download](https://lbry.io/get/lbry.dmg) | [Download](https://lbry.io/get/lbry.deb) |
| Latest Prerelease | [Download](https://lbry.io/get/lbry.pre.exe) | [Download](https://lbry.io/get/lbry.pre.dmg) | [Download](https://lbry.io/get/lbry.pre.deb) |
Our [releases page](https://github.com/lbryio/lbry-app/releases/latest) also contains the latest release, pre-releases, and past builds.
Our [releases page](https://github.com/lbryio/lbry-app/releases/latest) also contains the latest
release, pre-releases, and past builds.
To install from source or make changes to the application, continue reading below.
## Development on Linux and macOS
## Getting Started
These instructions will get you a copy of the project up and running on your local machine for
development and testing purposes.
### Prerequisites
* [Git](https://git-scm.com/downloads)
* [Node.js](https://nodejs.org/en/download/)
* [Yarn](https://yarnpkg.com/en/docs/install)
* `yarn --add-python-to-path install --global --production windows-build-tools` (Windows only)
### One-time Setup
1. Clone this repo
2. `DEPS=true ./build.sh`
This will download and install the LBRY app and its dependencies, including [the LBRY daemon](https://github.com/lbryio/lbry) and command line utilities like `node` and `yarn`. The LBRY app requires Node >= 6; if you have an earlier version of Node installed and want to keep it, you can use [nvm](https://github.com/creationix/nvm) to switch back and forth.
This will download and install the LBRY app and its dependencies, including
[the LBRY daemon](https://github.com/lbryio/lbry) and command line utilities like `node` and `yarn`.
The LBRY app requires Node >= 6; if you have an earlier version of Node installed and want to keep
it, you can use [nvm](https://github.com/creationix/nvm) to switch back and forth.
### Ongoing Development
Run `yarn dev`
#### Arch Linux and Other Non-Debian Distributions
This will set up a server that will automatically compile any changes made inside `src\` folder and automatically reload the app without losing the state.
Running the build script with `DEPS=true` triggers a bash script with `apt-get` specific commands.
If you are using a distribution without `apt-get`, try running the script as:
### Build
`./build.sh`
Run `yarn build`
You may also have to install the package [libsecret](https://wiki.gnome.org/Projects/Libsecret) if
it is not already installed.
We use [electron-builder](https://github.com/electron-userland/electron-builder)
to create distributable packages.
### Running
## Development on Windows
The app can be run from the sources using the following command:
### Windows Dependency
1. Download and install `git` from <a href="https://git-for-windows.github.io/">github.io<a> (configure to use command prompt integration)
2. Download and install `npm` and `node` from <a href="https://nodejs.org/en/download/current/">nodejs.org<a>
3. Download and install `python 2.7` from <a href="https://www.python.org/downloads/windows/">python.org</a>
4. Download and Install `Microsoft Visual C++ Compiler for Python 2.7` from <a href="https://www.microsoft.com/en-us/download/confirmation.aspx?id=44266">Microsoft<a>
5. Download and install `.NET Framework 2.0 Software Development Kit (SDK) (x64)` from <a href="https://www.microsoft.com/en-gb/download/details.aspx?id=15354">Microsoft<a> (may need to extract setup.exe and install manually by running install.exe as Administrator)
`yarn dev`
### On Windows
#### Windows Dependency
1. Download and install `git` from <a href="https://git-for-windows.github.io/">github.io<a>
(configure to use command prompt integration)
2. Download and install `npm` and `node` from
<a href="https://nodejs.org/en/download/current/">nodejs.org<a>
3. Download and install `python 2.7` from
<a href="https://www.python.org/downloads/windows/">python.org</a>
4. Download and Install `Microsoft Visual C++ Compiler for Python 2.7` from
<a href="https://www.microsoft.com/en-us/download/confirmation.aspx?id=44266">Microsoft<a>
5. Download and install `.NET Framework 2.0 Software Development Kit (SDK) (x64)` from
<a href="https://www.microsoft.com/en-gb/download/details.aspx?id=15354">Microsoft<a> (may need
to extract setup.exe and install manually by running install.exe as Administrator)
#### One-time Setup
1. Open a command prompt as administrator and run the following:
### One-time Setup
1. Open command prompt as adminstrator and run the following:
```
npm install --global --production windows-build-tools
exit
```
2. Open command prompt in the root of the project and run the following:
2. Open a command prompt in the root of the project and run the following:
```
python -m pip install -r build\requirements.txt
npm install -g yarn
yarn install
yarn build
```
3. Download the lbry daemon and cli [binaries](https://github.com/lbryio/lbry/releases) and place them in `static\daemon`
### Ongoing Development
Run `yarn dev`
3. Download the lbry daemon and CLI [binaries](https://github.com/lbryio/lbry/releases) and place
them in `static\daemon`.
### Build
Run `yarn build`
This will set up a server that will automatically compile any changes made inside `src\` folder and automatically reload the app without losing the state.
Run `yarn build`.
We use [electron-builder](https://github.com/electron-userland/electron-builder) to create
distributable packages.
## Contributing
Please read [our contributing manual](CONTRIBUTING.md) for details on how to develop for the
project and the process of submitting pull requests.
## Internationalization
If you want to help translating the lbry-app, you can copy the `en.json` file in `/dist/locales/` and modify the values while leaving the keys as their original English strings. An example for this would be: `"Skip": "Überspringen",` Translations should automatically show up in options.
If you want to help to translate the lbry-app, you can copy the `en.json` file in `/dist/locales/`
and modify the values while leaving the keys as their original English strings. An example for this
would be: `"Skip": "Überspringen",` Translations should automatically show up in options.
## License
[MIT © LBRY](LICENSE)

View file

@ -1,6 +1,6 @@
{
"name": "lbry-app",
"version": "0.19.1",
"version": "0.19.4rc2",
"description": "A browser for the LBRY network, a digital marketplace controlled by its users.",
"homepage": "https://lbry.io/",
"bugs": {
@ -23,7 +23,7 @@
"postinstall": "electron-builder install-app-deps",
"precommit": "lint-staged",
"lint": "eslint 'src/**/*.{js,jsx}' --fix",
"pretty-print": "prettier 'src/**/*.{js,jsx,scss,json}' --write"
"format": "prettier 'src/**/*.{js,jsx,scss,json}' --write"
},
"keywords": [
"lbry"

View file

@ -10,9 +10,9 @@ import ChildProcess from 'child_process';
import assert from 'assert';
import { app, BrowserWindow, globalShortcut, ipcMain, Menu, Tray } from 'electron';
import mainMenu from './menu/mainMenu';
import contextMenu from './menu/contextMenu';
const localVersion = app.getVersion();
export { contextMenu as Default } from './menu/contextMenu';
// Debug configs
const isDevelopment = process.env.NODE_ENV === 'development';
@ -95,7 +95,6 @@ function openItem(fullPath) {
// Causes child process reference to be garbage collected, allowing main process to exit
child.unref();
}
/*
* Quits by first killing the daemon, the calling quitting for real.
*/
@ -167,7 +166,7 @@ function createWindow() {
if (openURI) {
// We stored and received a URI that an external app requested before we had a window object
window.webContents.on('did-finish-load', () => {
window.webContents.send('open-uri-requested', openURI);
window.webContents.send('open-uri-requested', openURI, true);
});
}
@ -527,3 +526,5 @@ ipcMain.on('get-auth-token', event => {
ipcMain.on('set-auth-token', (event, token) => {
keytar.setPassword('LBRY', 'auth_token', token ? token.toString().trim() : null);
});
export { contextMenu };

View file

@ -1,18 +1,8 @@
import { Menu } from 'electron';
const contextMenuTemplate = [
{
role: 'cut',
},
{
role: 'copy',
},
{
role: 'paste',
},
];
const contextMenuTemplate = [{ role: 'cut' }, { role: 'copy' }, { role: 'paste' }];
export default function contextMenu(win, posX, posY, showDevItems) {
export default (win, posX, posY, showDevItems) => {
const template = contextMenuTemplate.slice();
if (showDevItems) {
template.push({
@ -27,4 +17,4 @@ export default function contextMenu(win, posX, posY, showDevItems) {
}
Menu.buildFromTemplate(template).popup(win);
}
};

View file

@ -3,23 +3,6 @@ import PropTypes from 'prop-types';
import { formatCredits, formatFullPrice } from 'util/formatCredits';
import lbry from '../lbry.js';
// component/icon.js
export class Icon extends React.PureComponent {
static propTypes = {
icon: PropTypes.string.isRequired,
className: PropTypes.string,
fixed: PropTypes.bool,
};
render() {
const { fixed, className } = this.props;
const spanClassName = `icon ${'fixed' in this.props ? 'icon-fixed-width ' : ''}${
this.props.icon
} ${this.props.className || ''}`;
return <span className={spanClassName} />;
}
}
export class TruncatedText extends React.PureComponent {
static propTypes = {
lines: PropTypes.number,

View file

@ -87,8 +87,9 @@ class FileCard extends React.PureComponent {
</div>
<div className="card__subtitle">
<span className="card__indicators card--file-subtitle">
<FilePrice uri={uri} /> {isRewardContent && <Icon icon={icons.FEATURED} />}{' '}
{fileInfo && <Icon icon={icons.LOCAL} />}
<FilePrice uri={uri} />{' '}
{isRewardContent && <Icon icon={icons.FEATURED} leftPad />}{' '}
{fileInfo && <Icon icon={icons.LOCAL} leftPad />}
</span>
<span className="card--file-subtitle">
<UriIndicator uri={uri} link span smallCard />

View file

@ -9,7 +9,7 @@ import { makeSelectCostInfoForUri } from 'redux/selectors/cost_info';
import { doFetchAvailability } from 'redux/actions/availability';
import { doOpenFileInShell } from 'redux/actions/file_info';
import { doPurchaseUri, doStartDownload } from 'redux/actions/content';
import { setVideoPause } from 'redux/actions/video';
import { doPause } from 'redux/actions/media';
import FileDownloadLink from './view';
const select = (state, props) => ({
@ -25,7 +25,7 @@ const perform = dispatch => ({
openInShell: path => dispatch(doOpenFileInShell(path)),
purchaseUri: uri => dispatch(doPurchaseUri(uri)),
restartDownload: (uri, outpoint) => dispatch(doStartDownload(uri, outpoint)),
setVideoPause: val => dispatch(setVideoPause(val)),
doPause: () => dispatch(doPause()),
});
export default connect(select, perform)(FileDownloadLink);

View file

@ -1,5 +1,6 @@
import React from 'react';
import { Icon, BusyMessage } from 'component/common';
import { BusyMessage } from 'component/common';
import Icon from 'component/icon';
import Link from 'component/link';
class FileDownloadLink extends React.PureComponent {
@ -42,12 +43,12 @@ class FileDownloadLink extends React.PureComponent {
purchaseUri,
costInfo,
loading,
setVideoPause,
doPause,
} = this.props;
const openFile = () => {
openInShell(fileInfo.download_path);
setVideoPause(true);
doPause();
};
if (loading || downloading) {

View file

@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import FormField from 'component/formField';
import { Icon } from 'component/common.js';
import Icon from 'component/icon';
let formFieldCounter = 0;

View file

@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import * as icons from 'constants/icons';
import classnames from 'classnames';
export default class Icon extends React.PureComponent {
static propTypes = {
@ -30,10 +31,19 @@ export default class Icon extends React.PureComponent {
}
render() {
const className = this.getIconClass(),
title = this.getIconTitle();
const { icon, fixed, className, leftPad } = this.props;
const iconClass = this.getIconClass();
const title = this.getIconTitle();
const spanClassName = `icon ${className}${this.props.fixed ? ' icon-fixed-width ' : ''}`;
const spanClassName = classnames(
'icon',
iconClass,
{
'icon-fixed-width': fixed,
'icon--left-pad': leftPad,
},
className
);
return <span className={spanClassName} title={title} />;
}

View file

@ -1,5 +1,5 @@
import React from 'react';
import { Icon } from 'component/common';
import Icon from 'component/icon';
import RewardLink from 'component/rewardLink';
import rewards from 'rewards.js';

View file

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Icon } from './common.js';
import Icon from 'component/icon';
import Link from 'component/link';
export class DropDownMenuItem extends React.PureComponent {

View file

@ -1,5 +1,5 @@
import React from 'react';
import { Icon } from 'component/common';
import Icon from 'component/icon';
import Link from 'component/link';
import lbryuri from 'lbryuri';
import classnames from 'classnames';

View file

@ -3,15 +3,10 @@ import { connect } from 'react-redux';
import { doUserEmailNew, doUserInviteNew } from 'redux/actions/user';
import { selectEmailNewIsPending, selectEmailNewErrorMessage } from 'redux/selectors/user';
import UserEmailNew from './view';
import rewards from 'rewards';
import { makeSelectRewardAmountByType } from 'redux/selectors/rewards';
const select = state => ({
isPending: selectEmailNewIsPending(state),
errorMessage: selectEmailNewErrorMessage(state),
rewardAmount: makeSelectRewardAmountByType()(state, {
reward_type: rewards.TYPE_CONFIRM_EMAIL,
}),
});
const perform = dispatch => ({

View file

@ -1,5 +1,4 @@
import React from 'react';
import { CreditAmount } from 'component/common';
import { Form, FormRow, Submit } from 'component/form.js';
class UserEmailNew extends React.PureComponent {
@ -23,18 +22,12 @@ class UserEmailNew extends React.PureComponent {
}
render() {
const { cancelButton, errorMessage, isPending, rewardAmount } = this.props;
const { cancelButton, errorMessage, isPending } = this.props;
return (
<div>
<p>
Let us know your email and you'll receive{' '}
<CreditAmount amount={rewardAmount} label="LBC" />, the blockchain token used by LBRY.
</p>
<p>
{__(
"We'll also let you know about LBRY updates, security issues, and great new content."
)}
{__("We'll let you know about LBRY updates, security issues, and great new content.")}
</p>
<p>{__("We'll never sell your email, and you can unsubscribe at any time.")}</p>
<Form onSubmit={this.handleSubmit.bind(this)}>

View file

@ -1,26 +1,22 @@
import React from 'react';
import { connect } from 'react-redux';
import { doUserEmailVerify } from 'redux/actions/user';
import { doUserEmailVerify, doUserEmailVerifyFailure } from 'redux/actions/user';
import {
selectEmailVerifyIsPending,
selectEmailToVerify,
selectEmailVerifyErrorMessage,
} from 'redux/selectors/user';
import UserEmailVerify from './view';
import rewards from 'rewards';
import { makeSelectRewardAmountByType } from 'redux/selectors/rewards';
const select = state => ({
isPending: selectEmailVerifyIsPending(state),
email: selectEmailToVerify(state),
errorMessage: selectEmailVerifyErrorMessage(state),
rewardAmount: makeSelectRewardAmountByType()(state, {
reward_type: rewards.TYPE_CONFIRM_EMAIL,
}),
});
const perform = dispatch => ({
verifyUserEmail: code => dispatch(doUserEmailVerify(code)),
verifyUserEmail: (code, recaptcha) => dispatch(doUserEmailVerify(code, recaptcha)),
verifyUserEmailFailure: error => dispatch(doUserEmailVerifyFailure(error)),
});
export default connect(select, perform)(UserEmailVerify);

View file

@ -1,6 +1,5 @@
import React from 'react';
import Link from 'component/link';
import { CreditAmount } from 'component/common';
import { Form, FormRow, Submit } from 'component/form.js';
class UserEmailVerify extends React.PureComponent {
@ -20,11 +19,16 @@ class UserEmailVerify extends React.PureComponent {
handleSubmit() {
const { code } = this.state;
this.props.verifyUserEmail(code);
try {
const verification = JSON.parse(atob(code));
this.props.verifyUserEmail(verification.token, verification.recaptcha);
} catch (error) {
this.props.verifyUserEmailFailure('Invalid Verification Token');
}
}
render() {
const { cancelButton, errorMessage, email, isPending, rewardAmount } = this.props;
const { cancelButton, errorMessage, email, isPending } = this.props;
return (
<Form onSubmit={this.handleSubmit.bind(this)}>
<p>Please enter the verification code emailed to {email}.</p>

View file

@ -3,8 +3,8 @@ import { connect } from 'react-redux';
import { doChangeVolume } from 'redux/actions/app';
import { selectVolume } from 'redux/selectors/app';
import { doPlayUri, doSetPlayingUri } from 'redux/actions/content';
import { doPlay, doPause, savePosition } from 'redux/actions/media';
import { makeSelectMetadataForUri, makeSelectContentTypeForUri } from 'redux/selectors/claims';
import { setVideoPause } from 'redux/actions/video';
import {
makeSelectFileInfoForUri,
makeSelectLoadingForUri,
@ -12,11 +12,13 @@ import {
} from 'redux/selectors/file_info';
import { makeSelectCostInfoForUri } from 'redux/selectors/cost_info';
import { selectShowNsfw } from 'redux/selectors/settings';
import { selectVideoPause } from 'redux/selectors/video';
import { selectMediaPaused, makeSelectMediaPositionForUri } from 'redux/selectors/media';
import Video from './view';
import { selectPlayingUri } from 'redux/selectors/content';
import { makeSelectClaimForUri } from 'redux/selectors/claims';
const select = (state, props) => ({
claim: makeSelectClaimForUri(props.uri)(state),
costInfo: makeSelectCostInfoForUri(props.uri)(state),
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
metadata: makeSelectMetadataForUri(props.uri)(state),
@ -26,14 +28,17 @@ const select = (state, props) => ({
playingUri: selectPlayingUri(state),
contentType: makeSelectContentTypeForUri(props.uri)(state),
volume: selectVolume(state),
videoPause: selectVideoPause(state),
mediaPaused: selectMediaPaused(state),
mediaPosition: makeSelectMediaPositionForUri(props.uri)(state),
});
const perform = dispatch => ({
play: uri => dispatch(doPlayUri(uri)),
cancelPlay: () => dispatch(doSetPlayingUri(null)),
changeVolume: volume => dispatch(doChangeVolume(volume)),
setVideoPause: val => dispatch(setVideoPause(val)),
doPlay: () => dispatch(doPlay()),
doPause: () => dispatch(doPause()),
savePosition: (claimId, position) => dispatch(savePosition(claimId, position)),
});
export default connect(select, perform)(Video);

View file

@ -21,16 +21,24 @@ class VideoPlayer extends React.PureComponent {
this.togglePlayListener = this.togglePlay.bind(this);
}
componentWillReceiveProps(nextProps) {
if (nextProps.videoPause) {
this.refs.media.children[0].pause();
this.props.setVideoPause(false);
}
componentWillReceiveProps(next) {
const el = this.refs.media.children[0];
if (!this.props.paused && next.paused && !el.paused) el.pause();
}
componentDidMount() {
const container = this.refs.media;
const { contentType, downloadPath, mediaType, changeVolume, volume } = this.props;
const {
contentType,
downloadPath,
mediaType,
changeVolume,
volume,
position,
claim,
uri,
} = this.props;
const loadedMetadata = e => {
this.setState({ hasMetadata: true, startedPlaying: true });
this.refs.media.children[0].play();
@ -61,6 +69,12 @@ class VideoPlayer extends React.PureComponent {
document.addEventListener('keydown', this.togglePlayListener);
const mediaElement = this.refs.media.children[0];
if (mediaElement) {
mediaElement.currentTime = position || 0;
mediaElement.addEventListener('play', () => this.props.doPlay());
mediaElement.addEventListener('pause', () => this.props.doPause());
mediaElement.addEventListener('timeupdate', () =>
this.props.savePosition(claim.claim_id, mediaElement.currentTime)
);
mediaElement.addEventListener('click', this.togglePlayListener);
mediaElement.addEventListener('loadedmetadata', loadedMetadata.bind(this), {
once: true,
@ -79,6 +93,7 @@ class VideoPlayer extends React.PureComponent {
if (mediaElement) {
mediaElement.removeEventListener('click', this.togglePlayListener);
}
this.props.doPause();
}
renderAudio(container, autoplay) {

View file

@ -51,9 +51,13 @@ class Video extends React.PureComponent {
contentType,
changeVolume,
volume,
claim,
uri,
videoPause,
setVideoPause,
doPlay,
doPause,
savePosition,
mediaPaused,
mediaPosition,
} = this.props;
const isPlaying = playingUri === uri;
@ -103,8 +107,13 @@ class Video extends React.PureComponent {
downloadCompleted={fileInfo.completed}
changeVolume={changeVolume}
volume={volume}
videoPause={videoPause}
setVideoPause={setVideoPause}
doPlay={doPlay}
doPause={doPause}
savePosition={savePosition}
claim={claim}
uri={uri}
paused={mediaPaused}
position={mediaPosition}
/>
))}
{!isPlaying && (

View file

@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import lbryuri from 'lbryuri.js';
import { Icon } from 'component/common.js';
import Icon from 'component/icon';
import { parseQueryParams } from 'util/query_params';
class WunderBar extends React.PureComponent {
@ -160,7 +160,7 @@ class WunderBar extends React.PureComponent {
onChange={this.onChange}
onKeyPress={this.onKeyPress}
value={this.state.address}
placeholder={__('Find movies, music, games, and more')}
placeholder={__('Find videos, music, games, and more')}
/>
</div>
);

View file

@ -157,3 +157,8 @@ export const HAS_FETCHED_SUBSCRIPTIONS = 'HAS_FETCHED_SUBSCRIPTIONS';
// Video controls
export const SET_VIDEO_PAUSE = 'SET_VIDEO_PAUSE';
// Media controls
export const MEDIA_PLAY = 'MEDIA_PLAY';
export const MEDIA_PAUSE = 'MEDIA_PAUSE';
export const MEDIA_POSITION = 'MEDIA_POSITION';

View file

@ -1,36 +1,47 @@
import amplitude from 'amplitude-js';
import App from 'component/app';
import SnackBar from 'component/snackBar';
import SplashScreen from 'component/splash';
import * as ACTIONS from 'constants/action_types';
import { ipcRenderer, remote, shell } from 'electron';
import lbry from 'lbry';
/* eslint-disable react/jsx-filename-extension */
import React from 'react';
import ReactDOM from 'react-dom';
import App from 'component/app';
import SnackBar from 'component/snackBar';
import { Provider } from 'react-redux';
import store from 'store';
import SplashScreen from 'component/splash';
import { doDaemonReady } from 'redux/actions/app';
import { doConditionalAuthNavigate, doDaemonReady, doShowSnackBar } from 'redux/actions/app';
import { doNavigate } from 'redux/actions/navigation';
import { doDownloadLanguages } from 'redux/actions/settings';
import * as ACTIONS from 'constants/action_types';
import amplitude from 'amplitude-js';
import lbry from 'lbry';
import { doUserEmailVerify } from 'redux/actions/user';
import 'scss/all.scss';
import { ipcRenderer, remote, shell } from 'electron';
import store from 'store';
import app from './app';
const { contextMenu } = remote.require('./main.js');
window.addEventListener('contextmenu', event => {
contextMenu.showContextMenu(
remote.getCurrentWindow(),
event.x,
event.y,
app.env === 'development'
);
contextMenu(remote.getCurrentWindow(), event.x, event.y, app.env === 'development');
event.preventDefault();
});
ipcRenderer.on('open-uri-requested', (event, uri) => {
ipcRenderer.on('open-uri-requested', (event, uri, newSession) => {
if (uri && uri.startsWith('lbry://')) {
app.store.dispatch(doNavigate('/show', { uri }));
if (uri.startsWith('lbry://?verify=')) {
let verification = {};
try {
verification = JSON.parse(atob(uri.substring(15)));
} catch (error) {
console.log(error);
}
if (verification.token && verification.recaptcha) {
app.store.dispatch(doConditionalAuthNavigate(newSession));
app.store.dispatch(doUserEmailVerify(verification.token, verification.recaptcha));
} else {
app.store.dispatch(doShowSnackBar({ message: 'Invalid Verification URI' }));
}
} else {
app.store.dispatch(doNavigate('/show', { uri }));
}
}
});
@ -48,10 +59,10 @@ ipcRenderer.on('window-is-focused', () => {
dock.setBadge('');
});
(function(history, ...args) {
((history, ...args) => {
const { replaceState } = history;
const newHistory = history;
newHistory.replaceState = function(_, __, path) {
newHistory.replaceState = (_, __, path) => {
amplitude.getInstance().logEvent('NAVIGATION', { destination: path ? path.slice(1) : path });
return replaceState.apply(history, args);
};
@ -85,7 +96,7 @@ document.addEventListener('click', event => {
}
});
const init = function initializeReactApp() {
const init = () => {
app.store.dispatch(doDownloadLanguages());
function onDaemonReady() {

View file

@ -1,13 +1,13 @@
const jsonrpc = {};
jsonrpc.call = function callJsonRpc(
jsonrpc.call = (
connectionString,
method,
params,
callback,
errorCallback,
connectFailedCallback
) {
) => {
function checkAndParse(response) {
if (response.status >= 200 && response.status < 300) {
return response.json();

View file

@ -1,5 +1,5 @@
import jsonrpc from 'jsonrpc';
import { ipcRenderer } from 'electron';
import jsonrpc from 'jsonrpc';
const CHECK_DAEMON_STARTED_TRY_NUMBER = 200;
@ -19,11 +19,10 @@ const lbryProxy = new Proxy(Lbry, {
return target[name];
}
return function(params = {}) {
return new Promise((resolve, reject) => {
return (params = {}) =>
new Promise((resolve, reject) => {
apiCall(name, params, resolve, reject);
});
};
},
});
@ -76,7 +75,7 @@ function removePendingPublishIfNeeded({ name, channelName, outpoint }) {
* Gets the current list of pending publish attempts. Filters out any that have timed out and
* removes them from the list.
*/
Lbry.getPendingPublishes = function() {
Lbry.getPendingPublishes = () => {
const pendingPublishes = getLocal('pendingPublishes') || [];
const newPendingPublishes = pendingPublishes.filter(
pub => Date.now() - pub.time <= Lbry.pendingPublishTimeout
@ -110,7 +109,7 @@ function pendingPublishToDummyFileInfo({ name, outpoint, claimId }) {
// core
Lbry.connectPromise = null;
Lbry.connect = function() {
Lbry.connect = () => {
if (Lbry.connectPromise === null) {
Lbry.connectPromise = new Promise((resolve, reject) => {
let tryNum = 0;
@ -144,7 +143,7 @@ Lbry.connect = function() {
* This currently includes a work-around to cache the file in local storage so that the pending
* publish can appear in the UI immediately.
*/
Lbry.publishDeprecated = function(params, fileListedCallback, publishedCallback, errorCallback) {
Lbry.publishDeprecated = (params, fileListedCallback, publishedCallback, errorCallback) => {
// Give a short grace period in case publish() returns right away or (more likely) gives an error
const returnPendingTimeout = setTimeout(
() => {
@ -173,11 +172,9 @@ Lbry.publishDeprecated = function(params, fileListedCallback, publishedCallback,
);
};
Lbry.imagePath = function(file) {
return `${staticResourcesPath}/img/${file}`;
};
Lbry.imagePath = file => `${staticResourcesPath}/img/${file}`;
Lbry.getMediaType = function(contentType, fileName) {
Lbry.getMediaType = (contentType, fileName) => {
if (contentType) {
return /^[^/]+/.exec(contentType)[0];
} else if (fileName) {
@ -199,14 +196,13 @@ Lbry.getMediaType = function(contentType, fileName) {
return 'unknown';
};
Lbry.getAppVersionInfo = function() {
return new Promise(resolve => {
Lbry.getAppVersionInfo = () =>
new Promise(resolve => {
ipcRenderer.once('version-info-received', (event, versionInfo) => {
resolve(versionInfo);
});
ipcRenderer.send('version-info-requested');
});
};
/**
* Wrappers for API methods to simulate missing or future behavior. Unlike the old-style stubs,
@ -217,8 +213,8 @@ Lbry.getAppVersionInfo = function() {
* Returns results from the file_list API method, plus dummy entries for pending publishes.
* (If a real publish with the same name is found, the pending publish will be ignored and removed.)
*/
Lbry.file_list = function(params = {}) {
return new Promise((resolve, reject) => {
Lbry.file_list = (params = {}) =>
new Promise((resolve, reject) => {
const { name, channel_name: channelName, outpoint } = params;
/**
@ -252,10 +248,9 @@ Lbry.file_list = function(params = {}) {
reject
);
});
};
Lbry.claim_list_mine = function(params = {}) {
return new Promise((resolve, reject) => {
Lbry.claim_list_mine = (params = {}) =>
new Promise((resolve, reject) => {
apiCall(
'claim_list_mine',
params,
@ -274,10 +269,9 @@ Lbry.claim_list_mine = function(params = {}) {
reject
);
});
};
Lbry.resolve = function(params = {}) {
return new Promise((resolve, reject) => {
Lbry.resolve = (params = {}) =>
new Promise((resolve, reject) => {
apiCall(
'resolve',
params,
@ -292,6 +286,5 @@ Lbry.resolve = function(params = {}) {
reject
);
});
};
export default lbryProxy;

View file

@ -1,6 +1,6 @@
import { ipcRenderer } from 'electron';
import Lbry from 'lbry';
import querystring from 'querystring';
import { ipcRenderer } from 'electron';
const Lbryio = {
enabled: true,
@ -15,7 +15,7 @@ const CONNECTION_STRING = process.env.LBRY_APP_API_URL
const EXCHANGE_RATE_TIMEOUT = 20 * 60 * 1000;
Lbryio.getExchangeRates = function() {
Lbryio.getExchangeRates = () => {
if (
!Lbryio.exchangeLastFetched ||
Date.now() - Lbryio.exchangeLastFetched > EXCHANGE_RATE_TIMEOUT
@ -33,7 +33,7 @@ Lbryio.getExchangeRates = function() {
return Lbryio.exchangePromise;
};
Lbryio.call = function(resource, action, params = {}, method = 'get') {
Lbryio.call = (resource, action, params = {}, method = 'get') => {
if (!Lbryio.enabled) {
console.log(__('Internal API disabled'));
return Promise.reject(new Error(__('LBRY internal API is disabled')));
@ -54,7 +54,7 @@ Lbryio.call = function(resource, action, params = {}, method = 'get') {
} else {
error = new Error('Unknown API error signature');
}
error.response = response; // this is primarily a hack used in actions/user.js
error.response = response; // This is primarily a hack used in actions/user.js
return Promise.reject(error);
});
}
@ -109,7 +109,7 @@ Lbryio.setAuthToken = token => {
Lbryio.getCurrentUser = () => Lbryio.call('user', 'me');
Lbryio.authenticate = function() {
Lbryio.authenticate = () => {
if (!Lbryio.enabled) {
return new Promise(resolve => {
resolve({

View file

@ -28,7 +28,7 @@ Lbryuri.REGEXP_ADDRESS = /^b(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/;
* - contentName (string): For anon claims, the name; for channel claims, the path
* - channelName (string, if present): Channel name without @
*/
Lbryuri.parse = function(uri, requireProto = false) {
Lbryuri.parse = (uri, requireProto = false) => {
// Break into components. Empty sub-matches are converted to null
const componentsRegex = new RegExp(
'^((?:lbry://)?)' + // protocol
@ -147,7 +147,7 @@ Lbryuri.parse = function(uri, requireProto = false) {
*
* The channelName key will accept names with or without the @ prefix.
*/
Lbryuri.build = function(uriObj, includeProto = true) {
Lbryuri.build = (uriObj, includeProto = true) => {
const { claimId, claimSequence, bidPosition, contentName, channelName } = uriObj;
let { name, path } = uriObj;
@ -192,14 +192,14 @@ Lbryuri.build = function(uriObj, includeProto = true) {
/* Takes a parseable LBRY URI and converts it to standard, canonical format (currently this just
* consists of adding the lbry:// prefix if needed) */
Lbryuri.normalize = function(uri) {
Lbryuri.normalize = uri => {
if (uri.match(/pending_claim/)) return uri;
const { name, path, bidPosition, claimSequence, claimId } = Lbryuri.parse(uri);
return Lbryuri.build({ name, path, claimSequence, bidPosition, claimId });
};
Lbryuri.isValid = function(uri) {
Lbryuri.isValid = uri => {
let parts;
try {
parts = Lbryuri.parse(Lbryuri.normalize(uri));
@ -209,12 +209,12 @@ Lbryuri.isValid = function(uri) {
return parts && parts.name;
};
Lbryuri.isValidName = function(name, checkCase = true) {
Lbryuri.isValidName = (name, checkCase = true) => {
const regexp = new RegExp('^[a-z0-9-]+$', checkCase ? '' : 'i');
return regexp.test(name);
};
Lbryuri.isClaimable = function(uri) {
Lbryuri.isClaimable = uri => {
let parts;
try {
parts = Lbryuri.parse(Lbryuri.normalize(uri));

View file

@ -1,28 +1,18 @@
import React from 'react';
import { connect } from 'react-redux';
import { doCloseModal } from 'redux/actions/app';
import { doNavigate } from 'redux/actions/navigation';
import { doSetClientSetting } from 'redux/actions/settings';
import { selectUserIsRewardApproved } from 'redux/selectors/user';
import { selectBalance } from 'redux/selectors/wallet';
import {
makeSelectHasClaimedReward,
makeSelectRewardByType,
selectUnclaimedRewardValue,
} from 'redux/selectors/rewards';
import { selectUnclaimedRewardValue } from 'redux/selectors/rewards';
import * as settings from 'constants/settings';
import ModalCreditIntro from './view';
const select = (state, props) => {
const selectHasClaimed = makeSelectHasClaimedReward(),
selectReward = makeSelectRewardByType();
return {
currentBalance: selectBalance(state),
isRewardApproved: selectUserIsRewardApproved(state),
totalRewardValue: selectUnclaimedRewardValue(state),
};
};
const select = state => ({
currentBalance: selectBalance(state),
isRewardApproved: selectUserIsRewardApproved(state),
totalRewardValue: selectUnclaimedRewardValue(state),
});
const perform = dispatch => () => ({
addBalance: () => {

View file

@ -9,7 +9,7 @@ const select = (state, props) => {
const selectReward = makeSelectRewardByType();
return {
reward: selectReward(state, { reward_type: rewards.TYPE_CONFIRM_EMAIL }),
reward: selectReward(state, { reward_type: rewards.TYPE_NEW_USER }),
};
};

View file

@ -2,7 +2,8 @@ import React from 'react';
import ReactDOM from 'react-dom';
import lbryuri from 'lbryuri';
import FileCard from 'component/fileCard';
import { Icon, BusyMessage } from 'component/common.js';
import { BusyMessage } from 'component/common.js';
import Icon from 'component/icon';
import ToolTip from 'component/tooltip.js';
import SubHeader from 'component/subHeader';
import classnames from 'classnames';

View file

@ -3,7 +3,8 @@ import React from 'react';
import lbry from 'lbry.js';
import Link from 'component/link';
import SubHeader from 'component/subHeader';
import { BusyMessage, Icon } from 'component/common';
import { BusyMessage } from 'component/common';
import Icon from 'component/icon';
class HelpPage extends React.PureComponent {
constructor(props) {

View file

@ -1,22 +1,24 @@
/* eslint-disable import/no-commonjs */
import * as ACTIONS from 'constants/action_types';
import * as MODALS from 'constants/modal_types';
import { ipcRenderer, remote } from 'electron';
import Lbry from 'lbry';
import Path from 'path';
import { doFetchRewardedContent } from 'redux/actions/content';
import { doFetchFileInfosAndPublishedClaims } from 'redux/actions/file_info';
import { doAuthNavigate } from 'redux/actions/navigation';
import { doFetchDaemonSettings } from 'redux/actions/settings';
import { doAuthenticate } from 'redux/actions/user';
import { doBalanceSubscribe } from 'redux/actions/wallet';
import {
selectUpdateUrl,
selectUpgradeDownloadPath,
selectUpgradeDownloadItem,
selectUpgradeFilename,
selectCurrentModal,
selectIsUpgradeSkipped,
selectRemoteVersion,
selectCurrentModal,
selectUpdateUrl,
selectUpgradeDownloadItem,
selectUpgradeDownloadPath,
selectUpgradeFilename,
} from 'redux/selectors/app';
import { doFetchDaemonSettings } from 'redux/actions/settings';
import { doBalanceSubscribe } from 'redux/actions/wallet';
import { doAuthenticate } from 'redux/actions/user';
import { doFetchFileInfosAndPublishedClaims } from 'redux/actions/file_info';
import * as MODALS from 'constants/modal_types';
import { doFetchRewardedContent } from 'redux/actions/content';
import { ipcRenderer, remote } from 'electron';
import Path from 'path';
const { download } = remote.require('electron-dl');
const Fs = remote.require('fs');
@ -56,7 +58,7 @@ export function doSkipUpgrade() {
}
export function doStartUpgrade() {
return function(dispatch, getState) {
return (dispatch, getState) => {
const state = getState();
const upgradeDownloadPath = selectUpgradeDownloadPath(state);
@ -65,7 +67,7 @@ export function doStartUpgrade() {
}
export function doDownloadUpgrade() {
return function(dispatch, getState) {
return (dispatch, getState) => {
const state = getState();
// Make a new directory within temp directory so the filename is guaranteed to be available
const dir = Fs.mkdtempSync(remote.app.getPath('temp') + Path.sep);
@ -104,7 +106,7 @@ export function doDownloadUpgrade() {
}
export function doCancelUpgrade() {
return function(dispatch, getState) {
return (dispatch, getState) => {
const state = getState();
const upgradeDownloadItem = selectUpgradeDownloadItem(state);
@ -127,7 +129,7 @@ export function doCancelUpgrade() {
}
export function doCheckUpgradeAvailable() {
return function(dispatch, getState) {
return (dispatch, getState) => {
const state = getState();
dispatch({
type: ACTIONS.CHECK_UPGRADE_START,
@ -170,7 +172,7 @@ export function doCheckUpgradeAvailable() {
Initiate a timer that will check for an app upgrade every 10 minutes.
*/
export function doCheckUpgradeSubscribe() {
return function(dispatch) {
return dispatch => {
const checkUpgradeTimer = setInterval(
() => dispatch(doCheckUpgradeAvailable()),
CHECK_UPGRADE_INTERVAL
@ -183,7 +185,7 @@ export function doCheckUpgradeSubscribe() {
}
export function doCheckDaemonVersion() {
return function(dispatch) {
return dispatch => {
Lbry.version().then(({ lbrynet_version: lbrynetVersion }) => {
dispatch({
type:
@ -196,7 +198,7 @@ export function doCheckDaemonVersion() {
}
export function doAlertError(errorList) {
return function(dispatch) {
return dispatch => {
dispatch({
type: ACTIONS.OPEN_MODAL,
data: {
@ -208,7 +210,7 @@ export function doAlertError(errorList) {
}
export function doDaemonReady() {
return function(dispatch, getState) {
return (dispatch, getState) => {
const state = getState();
dispatch(doAuthenticate());
@ -238,7 +240,7 @@ export function doRemoveSnackBarSnack() {
}
export function doClearCache() {
return function() {
return () => {
window.cacheStore.purge();
return Promise.resolve();
@ -246,13 +248,13 @@ export function doClearCache() {
}
export function doQuit() {
return function() {
return () => {
remote.app.quit();
};
}
export function doChangeVolume(volume) {
return function(dispatch) {
return dispatch => {
dispatch({
type: ACTIONS.VOLUME_CHANGED,
data: {
@ -261,3 +263,12 @@ export function doChangeVolume(volume) {
});
};
}
export function doConditionalAuthNavigate(newSession) {
return (dispatch, getState) => {
const state = getState();
if (newSession || selectCurrentModal(state) !== 'email_collection') {
dispatch(doAuthNavigate());
}
};
}

View file

@ -1,6 +1,6 @@
// eslint-disable-next-line import/prefer-default-export
export function doFetchAvailability() {
return function() {
return () => {
/*
this is disabled atm - Jeremy
*/

View file

@ -1,31 +1,31 @@
import { ipcRenderer } from 'electron';
import * as ACTIONS from 'constants/action_types';
import * as MODALS from 'constants/modal_types';
import * as SETTINGS from 'constants/settings';
import { ipcRenderer } from 'electron';
import Lbry from 'lbry';
import Lbryio from 'lbryio';
import Lbryuri from 'lbryuri';
import { makeSelectClientSetting } from 'redux/selectors/settings';
import { doAlertError, doOpenModal } from 'redux/actions/app';
import { doClaimEligiblePurchaseRewards } from 'redux/actions/rewards';
import { selectBadgeNumber } from 'redux/selectors/app';
import { selectMyClaimsRaw } from 'redux/selectors/claims';
import { selectBalance } from 'redux/selectors/wallet';
import { selectResolvingUris } from 'redux/selectors/content';
import { makeSelectCostInfoForUri } from 'redux/selectors/cost_info';
import {
makeSelectFileInfoForUri,
selectDownloadingByOutpoint,
selectTotalDownloadProgress,
} from 'redux/selectors/file_info';
import { selectResolvingUris } from 'redux/selectors/content';
import { makeSelectCostInfoForUri } from 'redux/selectors/cost_info';
import { doAlertError, doOpenModal } from 'redux/actions/app';
import { doClaimEligiblePurchaseRewards } from 'redux/actions/rewards';
import { selectBadgeNumber } from 'redux/selectors/app';
import { makeSelectClientSetting } from 'redux/selectors/settings';
import { selectBalance } from 'redux/selectors/wallet';
import batchActions from 'util/batchActions';
import setBadge from 'util/setBadge';
import setProgressBar from 'util/setProgressBar';
import batchActions from 'util/batchActions';
import * as MODALS from 'constants/modal_types';
const DOWNLOAD_POLL_INTERVAL = 250;
export function doResolveUris(uris) {
return function(dispatch, getState) {
return (dispatch, getState) => {
const normalizedUris = uris.map(Lbryuri.normalize);
const state = getState();
@ -70,7 +70,7 @@ export function doResolveUri(uri) {
}
export function doFetchFeaturedUris() {
return function(dispatch) {
return dispatch => {
dispatch({
type: ACTIONS.FETCH_FEATURED_CONTENT_STARTED,
});
@ -108,7 +108,7 @@ export function doFetchFeaturedUris() {
}
export function doFetchRewardedContent() {
return function(dispatch) {
return dispatch => {
const success = nameToClaimId => {
dispatch({
type: ACTIONS.FETCH_REWARD_CONTENT_COMPLETED,
@ -134,7 +134,7 @@ export function doFetchRewardedContent() {
}
export function doUpdateLoadStatus(uri, outpoint) {
return function(dispatch, getState) {
return (dispatch, getState) => {
Lbry.file_list({
outpoint,
full_status: true,
@ -196,7 +196,7 @@ export function doUpdateLoadStatus(uri, outpoint) {
}
export function doStartDownload(uri, outpoint) {
return function(dispatch, getState) {
return (dispatch, getState) => {
const state = getState();
if (!outpoint) {
@ -223,7 +223,7 @@ export function doStartDownload(uri, outpoint) {
}
export function doDownloadFile(uri, streamInfo) {
return function(dispatch) {
return dispatch => {
dispatch(doStartDownload(uri, streamInfo.outpoint));
Lbryio.call('file', 'view', {
@ -237,7 +237,7 @@ export function doDownloadFile(uri, streamInfo) {
}
export function doSetPlayingUri(uri) {
return function(dispatch) {
return dispatch => {
dispatch({
type: ACTIONS.SET_PLAYING_URI,
data: { uri },
@ -246,7 +246,7 @@ export function doSetPlayingUri(uri) {
}
export function doLoadVideo(uri) {
return function(dispatch) {
return dispatch => {
dispatch({
type: ACTIONS.LOADING_VIDEO_STARTED,
data: {
@ -289,7 +289,7 @@ export function doLoadVideo(uri) {
}
export function doPurchaseUri(uri) {
return function(dispatch, getState) {
return (dispatch, getState) => {
const state = getState();
const balance = selectBalance(state);
const fileInfo = makeSelectFileInfoForUri(uri)(state);
@ -348,7 +348,7 @@ export function doPurchaseUri(uri) {
}
export function doFetchClaimsByChannel(uri, page) {
return function(dispatch) {
return dispatch => {
dispatch({
type: ACTIONS.FETCH_CHANNEL_CLAIMS_STARTED,
data: { uri, page },
@ -371,7 +371,7 @@ export function doFetchClaimsByChannel(uri, page) {
}
export function doFetchClaimCountByChannel(uri) {
return function(dispatch) {
return dispatch => {
dispatch({
type: ACTIONS.FETCH_CHANNEL_CLAIM_COUNT_STARTED,
data: { uri },
@ -393,7 +393,7 @@ export function doFetchClaimCountByChannel(uri) {
}
export function doFetchClaimListMine() {
return function(dispatch) {
return dispatch => {
dispatch({
type: ACTIONS.FETCH_CLAIM_LIST_MINE_STARTED,
});
@ -410,14 +410,14 @@ export function doFetchClaimListMine() {
}
export function doPlayUri(uri) {
return function(dispatch) {
return dispatch => {
dispatch(doSetPlayingUri(uri));
dispatch(doPurchaseUri(uri));
};
}
export function doFetchChannelListMine() {
return function(dispatch) {
return dispatch => {
dispatch({
type: ACTIONS.FETCH_CHANNEL_LIST_MINE_STARTED,
});
@ -434,7 +434,7 @@ export function doFetchChannelListMine() {
}
export function doCreateChannel(name, amount) {
return function(dispatch) {
return dispatch => {
dispatch({
type: ACTIONS.CREATE_CHANNEL_STARTED,
});
@ -444,14 +444,14 @@ export function doCreateChannel(name, amount) {
channel_name: name,
amount: parseFloat(amount),
}).then(
channelClaim => {
const newChannelClaim = channelClaim;
newChannelClaim.name = name;
newChannelClaim => {
const channelClaim = newChannelClaim;
channelClaim.name = name;
dispatch({
type: ACTIONS.CREATE_CHANNEL_COMPLETED,
data: { newChannelClaim },
data: { channelClaim },
});
resolve(newChannelClaim);
resolve(channelClaim);
},
error => {
reject(error);
@ -462,8 +462,8 @@ export function doCreateChannel(name, amount) {
}
export function doPublish(params) {
return function(dispatch) {
return new Promise((resolve, reject) => {
return dispatch =>
new Promise((resolve, reject) => {
const success = claim => {
resolve(claim);
@ -477,11 +477,10 @@ export function doPublish(params) {
Lbry.publishDeprecated(params, null, success, failure);
});
};
}
export function doAbandonClaim(txid, nout) {
return function(dispatch, getState) {
return (dispatch, getState) => {
const state = getState();
const myClaims = selectMyClaimsRaw(state);
const { claim_id: claimId, name } = myClaims.find(

View file

@ -4,7 +4,7 @@ import { selectClaimsByUri } from 'redux/selectors/claims';
// eslint-disable-next-line import/prefer-default-export
export function doFetchCostInfoForUri(uri) {
return function(dispatch, getState) {
return (dispatch, getState) => {
const state = getState();
const claim = selectClaimsByUri(state)[uri];

View file

@ -1,25 +1,25 @@
import * as ACTIONS from 'constants/action_types';
import { shell } from 'electron';
import Lbry from 'lbry';
import { doFetchClaimListMine, doAbandonClaim } from 'redux/actions/content';
import { doCloseModal } from 'redux/actions/app';
import { doAbandonClaim, doFetchClaimListMine } from 'redux/actions/content';
import { doHistoryBack } from 'redux/actions/navigation';
import {
selectClaimsByUri,
selectIsFetchingClaimListMine,
selectMyClaimsOutpoints,
} from 'redux/selectors/claims';
import {
selectIsFetchingFileList,
selectFileInfosByOutpoint,
selectUrisLoading,
selectIsFetchingFileList,
selectTotalDownloadProgress,
selectUrisLoading,
} from 'redux/selectors/file_info';
import { doCloseModal } from 'redux/actions/app';
import { doHistoryBack } from 'redux/actions/navigation';
import setProgressBar from 'util/setProgressBar';
import batchActions from 'util/batchActions';
import { shell } from 'electron';
import setProgressBar from 'util/setProgressBar';
export function doFetchFileInfo(uri) {
return function(dispatch, getState) {
return (dispatch, getState) => {
const state = getState();
const claim = selectClaimsByUri(state)[uri];
const outpoint = claim ? `${claim.txid}:${claim.nout}` : null;
@ -47,7 +47,7 @@ export function doFetchFileInfo(uri) {
}
export function doFileList() {
return function(dispatch, getState) {
return (dispatch, getState) => {
const state = getState();
const isFetching = selectIsFetchingFileList(state);
@ -69,13 +69,13 @@ export function doFileList() {
}
export function doOpenFileInFolder(path) {
return function() {
return () => {
shell.showItemInFolder(path);
};
}
export function doOpenFileInShell(path) {
return function(dispatch) {
return dispatch => {
const success = shell.openItem(path);
if (!success) {
dispatch(doOpenFileInFolder(path));
@ -84,7 +84,7 @@ export function doOpenFileInShell(path) {
}
export function doDeleteFile(outpoint, deleteFromComputer, abandonClaim) {
return function(dispatch, getState) {
return (dispatch, getState) => {
const state = getState();
Lbry.file_delete({
@ -92,7 +92,7 @@ export function doDeleteFile(outpoint, deleteFromComputer, abandonClaim) {
delete_from_download_dir: deleteFromComputer,
});
// If the file is for a claim we published then also abandom the claim
// If the file is for a claim we published then also abandon the claim
const myClaimsOutpoints = selectMyClaimsOutpoints(state);
if (abandonClaim && myClaimsOutpoints.indexOf(outpoint) !== -1) {
const byOutpoint = selectFileInfosByOutpoint(state);
@ -119,7 +119,7 @@ export function doDeleteFile(outpoint, deleteFromComputer, abandonClaim) {
}
export function doDeleteFileAndGoBack(fileInfo, deleteFromComputer, abandonClaim) {
return function(dispatch) {
return dispatch => {
const actions = [];
actions.push(doCloseModal());
actions.push(doHistoryBack());
@ -129,7 +129,7 @@ export function doDeleteFileAndGoBack(fileInfo, deleteFromComputer, abandonClaim
}
export function doFetchFileInfosAndPublishedClaims() {
return function(dispatch, getState) {
return (dispatch, getState) => {
const state = getState();
const isFetchingClaimListMine = selectIsFetchingClaimListMine(state);
const isFetchingFileInfo = selectIsFetchingFileList(state);

View file

@ -0,0 +1,30 @@
// @flow
import * as actions from 'constants/action_types';
import type { Action, Dispatch } from 'redux/reducers/media';
import lbry from 'lbry';
import { makeSelectClaimForUri } from 'redux/selectors/claims';
export const doPlay = () => (dispatch: Dispatch) =>
dispatch({
type: actions.MEDIA_PLAY,
});
export const doPause = () => (dispatch: Dispatch) =>
dispatch({
type: actions.MEDIA_PAUSE,
});
export function savePosition(claimId: String, position: Number) {
return function(dispatch: Dispatch, getState: Function) {
const state = getState();
const claim = state.claims.byId[claimId];
const outpoint = `${claim.txid}:${claim.nout}`;
dispatch({
type: actions.MEDIA_POSITION,
data: {
outpoint,
position,
},
});
};
}

View file

@ -1,9 +1,9 @@
import * as ACTIONS from 'constants/action_types';
import { selectHistoryStack, selectHistoryIndex } from 'redux/selectors/navigation';
import { selectHistoryIndex, selectHistoryStack } from 'redux/selectors/navigation';
import { toQueryString } from 'util/query_params';
export function doNavigate(path, params = {}, options = {}) {
return function(dispatch) {
return dispatch => {
if (!path) {
return;
}
@ -23,7 +23,7 @@ export function doNavigate(path, params = {}, options = {}) {
}
export function doAuthNavigate(pathAfterAuth = null, params = {}) {
return function(dispatch) {
return dispatch => {
if (pathAfterAuth) {
dispatch({
type: ACTIONS.CHANGE_AFTER_AUTH_PATH,
@ -47,19 +47,15 @@ export function doHistoryTraverse(dispatch, state, modifier) {
}
export function doHistoryBack() {
return function(dispatch, getState) {
return doHistoryTraverse(dispatch, getState(), -1);
};
return (dispatch, getState) => doHistoryTraverse(dispatch, getState(), -1);
}
export function doHistoryForward() {
return function(dispatch, getState) {
return doHistoryTraverse(dispatch, getState(), 1);
};
return (dispatch, getState) => doHistoryTraverse(dispatch, getState(), 1);
}
export function doRecordScroll(scroll) {
return function(dispatch) {
return dispatch => {
dispatch({
type: ACTIONS.WINDOW_SCROLLED,
data: { scrollY: scroll },

View file

@ -1,12 +1,12 @@
import * as ACTIONS from 'constants/action_types';
import * as MODALS from 'constants/modal_types';
import Lbryio from 'lbryio';
import rewards from 'rewards';
import { selectUnclaimedRewardsByType } from 'redux/selectors/rewards';
import { selectUserIsRewardApproved } from 'redux/selectors/user';
import rewards from 'rewards';
export function doRewardList() {
return function(dispatch) {
return dispatch => {
dispatch({
type: ACTIONS.FETCH_REWARDS_STARTED,
});
@ -28,7 +28,7 @@ export function doRewardList() {
}
export function doClaimRewardType(rewardType) {
return function(dispatch, getState) {
return (dispatch, getState) => {
const state = getState();
const rewardsByType = selectUnclaimedRewardsByType(state);
const reward = rewardsByType[rewardType];
@ -60,7 +60,7 @@ export function doClaimRewardType(rewardType) {
reward: successReward,
},
});
if (successReward.reward_type === rewards.TYPE_CONFIRM_EMAIL) {
if (successReward.reward_type === rewards.TYPE_NEW_USER) {
dispatch({
type: ACTIONS.OPEN_MODAL,
data: { modal: MODALS.FIRST_REWARD },
@ -80,7 +80,7 @@ export function doClaimRewardType(rewardType) {
}
export function doClaimEligiblePurchaseRewards() {
return function(dispatch, getState) {
return (dispatch, getState) => {
const state = getState();
const rewardsByType = selectUnclaimedRewardsByType(state);
const userIsRewardApproved = selectUserIsRewardApproved(state);
@ -100,7 +100,7 @@ export function doClaimEligiblePurchaseRewards() {
}
export function doClaimRewardClearError(reward) {
return function(dispatch) {
return dispatch => {
dispatch({
type: ACTIONS.CLAIM_REWARD_CLEAR_ERROR,
data: { reward },

View file

@ -7,7 +7,7 @@ import batchActions from 'util/batchActions';
// eslint-disable-next-line import/prefer-default-export
export function doSearch(rawQuery) {
return function(dispatch, getState) {
return (dispatch, getState) => {
const state = getState();
const page = selectCurrentPage(state);

View file

@ -1,12 +1,12 @@
import * as ACTIONS from 'constants/action_types';
import * as SETTINGS from 'constants/settings';
import Lbry from 'lbry';
import Fs from 'fs';
import Http from 'http';
import Lbry from 'lbry';
export function doFetchDaemonSettings() {
return function(dispatch) {
return dispatch => {
Lbry.settings_get().then(settings => {
dispatch({
type: ACTIONS.DAEMON_SETTINGS_RECEIVED,
@ -19,15 +19,15 @@ export function doFetchDaemonSettings() {
}
export function doSetDaemonSetting(key, value) {
return function(dispatch) {
const settings = {};
settings[key] = value;
Lbry.settings_set(settings).then(settings);
Lbry.settings_get().then(remoteSettings => {
return dispatch => {
const newSettings = {};
newSettings[key] = value;
Lbry.settings_set(newSettings).then(newSettings);
Lbry.settings_get().then(settings => {
dispatch({
type: ACTIONS.DAEMON_SETTINGS_RECEIVED,
data: {
remoteSettings,
settings,
},
});
});
@ -45,14 +45,14 @@ export function doSetClientSetting(key, value) {
}
export function doGetThemes() {
return function(dispatch) {
return dispatch => {
const themes = ['light', 'dark'];
dispatch(doSetClientSetting(SETTINGS.THEMES, themes));
};
}
export function doDownloadLanguage(langFile) {
return function(dispatch) {
return dispatch => {
const destinationPath = `${app.i18n.directory}/${langFile}`;
const language = langFile.replace('.json', '');
const errorHandler = () => {
@ -105,7 +105,7 @@ export function doDownloadLanguage(langFile) {
}
export function doDownloadLanguages() {
return function() {
return () => {
// temporarily disable i18n so I can get a working build out -- Jeremy
// if (!Fs.existsSync(app.i18n.directory)) {
// Fs.mkdirSync(app.i18n.directory);
@ -135,7 +135,7 @@ export function doDownloadLanguages() {
}
export function doChangeLanguage(language) {
return function(dispatch) {
return dispatch => {
dispatch(doSetClientSetting(SETTINGS.LANGUAGE, language));
app.i18n.setLocale(language);
};

View file

@ -2,12 +2,12 @@ import * as ACTIONS from 'constants/action_types';
import * as MODALS from 'constants/modal_types';
import Lbryio from 'lbryio';
import { doOpenModal, doShowSnackBar } from 'redux/actions/app';
import { doRewardList, doClaimRewardType } from 'redux/actions/rewards';
import { doClaimRewardType, doRewardList } from 'redux/actions/rewards';
import { selectEmailToVerify } from 'redux/selectors/user';
import rewards from 'rewards';
export function doFetchInviteStatus() {
return function(dispatch) {
return dispatch => {
dispatch({
type: ACTIONS.USER_INVITE_STATUS_FETCH_STARTED,
});
@ -32,7 +32,7 @@ export function doFetchInviteStatus() {
}
export function doAuthenticate() {
return function(dispatch) {
return dispatch => {
dispatch({
type: ACTIONS.AUTHENTICATION_STARTED,
});
@ -56,7 +56,7 @@ export function doAuthenticate() {
}
export function doUserFetch() {
return function(dispatch) {
return dispatch => {
dispatch({
type: ACTIONS.USER_FETCH_STARTED,
});
@ -79,7 +79,7 @@ export function doUserFetch() {
}
export function doUserEmailNew(email) {
return function(dispatch) {
return dispatch => {
dispatch({
type: ACTIONS.USER_EMAIL_NEW_STARTED,
email,
@ -116,20 +116,31 @@ export function doUserEmailNew(email) {
};
}
export function doUserEmailVerify(verificationToken) {
return function(dispatch, getState) {
export function doUserEmailVerifyFailure(error) {
return {
type: ACTIONS.USER_EMAIL_VERIFY_FAILURE,
data: { error },
};
}
export function doUserEmailVerify(verificationToken, recaptcha) {
return (dispatch, getState) => {
const email = selectEmailToVerify(getState());
const trimmedVerificationToken = verificationToken.toString().trim();
dispatch({
type: ACTIONS.USER_EMAIL_VERIFY_STARTED,
code: trimmedVerificationToken,
code: verificationToken,
recaptcha,
});
Lbryio.call(
'user_email',
'confirm',
{ verification_token: trimmedVerificationToken, email },
{
verification_token: verificationToken,
email,
recaptcha,
},
'post'
)
.then(userEmail => {
@ -138,23 +149,17 @@ export function doUserEmailVerify(verificationToken) {
type: ACTIONS.USER_EMAIL_VERIFY_SUCCESS,
data: { email },
});
dispatch(doClaimRewardType(rewards.TYPE_CONFIRM_EMAIL));
dispatch(doUserFetch());
} else {
throw new Error('Your email is still not verified.'); // shouldn't happen
}
})
.catch(error => {
dispatch({
type: ACTIONS.USER_EMAIL_VERIFY_FAILURE,
data: { error },
});
});
.catch(error => dispatch(doUserEmailVerifyFailure(error)));
};
}
export function doUserIdentityVerify(stripeToken) {
return function(dispatch) {
return dispatch => {
dispatch({
type: ACTIONS.USER_IDENTITY_VERIFY_STARTED,
token: stripeToken,
@ -182,7 +187,7 @@ export function doUserIdentityVerify(stripeToken) {
}
export function doFetchAccessToken() {
return function(dispatch) {
return dispatch => {
const success = token =>
dispatch({
type: ACTIONS.FETCH_ACCESS_TOKEN_SUCCESS,
@ -193,7 +198,7 @@ export function doFetchAccessToken() {
}
export function doUserInviteNew(email) {
return function(dispatch) {
return dispatch => {
dispatch({
type: ACTIONS.USER_INVITE_NEW_STARTED,
});

View file

@ -1,16 +1,16 @@
import * as ACTIONS from 'constants/action_types';
import * as MODALS from 'constants/modal_types';
import Lbry from 'lbry';
import {
selectDraftTransaction,
selectDraftTransactionAmount,
selectBalance,
} from 'redux/selectors/wallet';
import { doOpenModal, doShowSnackBar } from 'redux/actions/app';
import { doNavigate } from 'redux/actions/navigation';
import * as MODALS from 'constants/modal_types';
import {
selectBalance,
selectDraftTransaction,
selectDraftTransactionAmount,
} from 'redux/selectors/wallet';
export function doUpdateBalance() {
return function(dispatch) {
return dispatch => {
Lbry.wallet_balance().then(balance =>
dispatch({
type: ACTIONS.UPDATE_BALANCE,
@ -23,14 +23,14 @@ export function doUpdateBalance() {
}
export function doBalanceSubscribe() {
return function(dispatch) {
return dispatch => {
dispatch(doUpdateBalance());
setInterval(() => dispatch(doUpdateBalance()), 5000);
};
}
export function doFetchTransactions() {
return function(dispatch) {
return dispatch => {
dispatch({
type: ACTIONS.FETCH_TRANSACTIONS_STARTED,
});
@ -47,7 +47,7 @@ export function doFetchTransactions() {
}
export function doFetchBlock(height) {
return function(dispatch) {
return dispatch => {
Lbry.block_show({ height }).then(block => {
dispatch({
type: ACTIONS.FETCH_BLOCK_SUCCESS,
@ -58,7 +58,7 @@ export function doFetchBlock(height) {
}
export function doGetNewAddress() {
return function(dispatch) {
return dispatch => {
dispatch({
type: ACTIONS.GET_NEW_ADDRESS_STARTED,
});
@ -74,7 +74,7 @@ export function doGetNewAddress() {
}
export function doCheckAddressIsMine(address) {
return function(dispatch) {
return dispatch => {
dispatch({
type: ACTIONS.CHECK_ADDRESS_IS_MINE_STARTED,
});
@ -90,7 +90,7 @@ export function doCheckAddressIsMine(address) {
}
export function doSendDraftTransaction() {
return function(dispatch, getState) {
return (dispatch, getState) => {
const state = getState();
const draftTx = selectDraftTransaction(state);
const balance = selectBalance(state);
@ -156,7 +156,7 @@ export function doSetDraftTransactionAddress(address) {
}
export function doSendSupport(amount, claimId, uri) {
return function(dispatch, getState) {
return (dispatch, getState) => {
const state = getState();
const balance = selectBalance(state);
@ -200,7 +200,7 @@ export function doSendSupport(amount, claimId, uri) {
};
Lbry.wallet_send({
claimId,
claim_id: claimId,
amount,
}).then(successCallback, errorCallback);
};

View file

@ -3,7 +3,7 @@
import * as ACTIONS from 'constants/action_types';
import * as MODALS from 'constants/modal_types';
const { remote } = require('electron');
import { remote } from 'electron';
const win = remote.BrowserWindow.getFocusedWindow();
@ -56,48 +56,42 @@ const defaultState: AppState = {
snackBar: undefined,
};
reducers[ACTIONS.DAEMON_READY] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.DAEMON_READY] = state =>
Object.assign({}, state, {
daemonReady: true,
});
};
reducers[ACTIONS.DAEMON_VERSION_MATCH] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.DAEMON_VERSION_MATCH] = state =>
Object.assign({}, state, {
daemonVersionMatched: true,
});
};
reducers[ACTIONS.DAEMON_VERSION_MISMATCH] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.DAEMON_VERSION_MISMATCH] = state =>
Object.assign({}, state, {
daemonVersionMatched: false,
modal: MODALS.INCOMPATIBLE_DAEMON,
});
};
reducers[ACTIONS.UPGRADE_CANCELLED] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.UPGRADE_CANCELLED] = state =>
Object.assign({}, state, {
downloadProgress: null,
upgradeDownloadComplete: false,
modal: null,
});
};
reducers[ACTIONS.UPGRADE_DOWNLOAD_COMPLETED] = function(state, action) {
return Object.assign({}, state, {
reducers[ACTIONS.UPGRADE_DOWNLOAD_COMPLETED] = (state, action) =>
Object.assign({}, state, {
downloadPath: action.data.path,
upgradeDownloading: false,
upgradeDownloadCompleted: true,
});
};
reducers[ACTIONS.UPGRADE_DOWNLOAD_STARTED] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.UPGRADE_DOWNLOAD_STARTED] = state =>
Object.assign({}, state, {
upgradeDownloading: true,
});
};
reducers[ACTIONS.SKIP_UPGRADE] = function(state) {
reducers[ACTIONS.SKIP_UPGRADE] = state => {
sessionStorage.setItem('upgradeSkipped', 'true');
return Object.assign({}, state, {
@ -106,46 +100,40 @@ reducers[ACTIONS.SKIP_UPGRADE] = function(state) {
});
};
reducers[ACTIONS.UPDATE_VERSION] = function(state, action) {
return Object.assign({}, state, {
reducers[ACTIONS.UPDATE_VERSION] = (state, action) =>
Object.assign({}, state, {
version: action.data.version,
});
};
reducers[ACTIONS.CHECK_UPGRADE_SUCCESS] = function(state, action) {
return Object.assign({}, state, {
reducers[ACTIONS.CHECK_UPGRADE_SUCCESS] = (state, action) =>
Object.assign({}, state, {
isUpgradeAvailable: action.data.upgradeAvailable,
remoteVersion: action.data.remoteVersion,
});
};
reducers[ACTIONS.CHECK_UPGRADE_SUBSCRIBE] = function(state, action) {
return Object.assign({}, state, {
reducers[ACTIONS.CHECK_UPGRADE_SUBSCRIBE] = (state, action) =>
Object.assign({}, state, {
checkUpgradeTimer: action.data.checkUpgradeTimer,
});
};
reducers[ACTIONS.OPEN_MODAL] = function(state, action) {
return Object.assign({}, state, {
reducers[ACTIONS.OPEN_MODAL] = (state, action) =>
Object.assign({}, state, {
modal: action.data.modal,
modalProps: action.data.modalProps || {},
});
};
reducers[ACTIONS.CLOSE_MODAL] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.CLOSE_MODAL] = state =>
Object.assign({}, state, {
modal: undefined,
modalProps: {},
});
};
reducers[ACTIONS.UPGRADE_DOWNLOAD_PROGRESSED] = function(state, action) {
return Object.assign({}, state, {
reducers[ACTIONS.UPGRADE_DOWNLOAD_PROGRESSED] = (state, action) =>
Object.assign({}, state, {
downloadProgress: action.data.percent,
});
};
reducers[ACTIONS.SHOW_SNACKBAR] = function(state, action) {
reducers[ACTIONS.SHOW_SNACKBAR] = (state, action) => {
const { message, linkText, linkTarget, isError } = action.data;
const snackBar = Object.assign({}, state.snackBar);
const snacks = Object.assign([], snackBar.snacks);
@ -164,7 +152,7 @@ reducers[ACTIONS.SHOW_SNACKBAR] = function(state, action) {
});
};
reducers[ACTIONS.REMOVE_SNACKBAR_SNACK] = function(state) {
reducers[ACTIONS.REMOVE_SNACKBAR_SNACK] = state => {
const snackBar = Object.assign({}, state.snackBar);
const snacks = Object.assign([], snackBar.snacks);
snacks.shift();
@ -178,7 +166,7 @@ reducers[ACTIONS.REMOVE_SNACKBAR_SNACK] = function(state) {
});
};
reducers[ACTIONS.DOWNLOADING_COMPLETED] = function(state) {
reducers[ACTIONS.DOWNLOADING_COMPLETED] = state => {
const { badgeNumber } = state;
// Don't update the badge number if the window is focused
@ -189,17 +177,15 @@ reducers[ACTIONS.DOWNLOADING_COMPLETED] = function(state) {
});
};
reducers[ACTIONS.WINDOW_FOCUSED] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.WINDOW_FOCUSED] = state =>
Object.assign({}, state, {
badgeNumber: 0,
});
};
reducers[ACTIONS.VOLUME_CHANGED] = function(state, action) {
return Object.assign({}, state, {
reducers[ACTIONS.VOLUME_CHANGED] = (state, action) =>
Object.assign({}, state, {
volume: action.data.volume,
});
};
export default function reducer(state: AppState = defaultState, action: any) {
const handler = reducers[action.type];

View file

@ -3,7 +3,7 @@ import * as ACTIONS from 'constants/action_types';
const reducers = {};
const defaultState = {};
reducers[ACTIONS.FETCH_AVAILABILITY_STARTED] = function(state, action) {
reducers[ACTIONS.FETCH_AVAILABILITY_STARTED] = (state, action) => {
const { uri } = action.data;
const newFetching = Object.assign({}, state.fetching);
@ -14,7 +14,7 @@ reducers[ACTIONS.FETCH_AVAILABILITY_STARTED] = function(state, action) {
});
};
reducers[ACTIONS.FETCH_AVAILABILITY_COMPLETED] = function(state, action) {
reducers[ACTIONS.FETCH_AVAILABILITY_COMPLETED] = (state, action) => {
const { uri, availability } = action.data;
const newFetching = Object.assign({}, state.fetching);

View file

@ -4,7 +4,7 @@ const reducers = {};
const defaultState = {};
reducers[ACTIONS.RESOLVE_URIS_COMPLETED] = function(state, action) {
reducers[ACTIONS.RESOLVE_URIS_COMPLETED] = (state, action) => {
const { resolveInfo } = action.data;
const byUri = Object.assign({}, state.claimsByUri);
const byId = Object.assign({}, state.byId);
@ -33,13 +33,12 @@ reducers[ACTIONS.RESOLVE_URIS_COMPLETED] = function(state, action) {
});
};
reducers[ACTIONS.FETCH_CLAIM_LIST_MINE_STARTED] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.FETCH_CLAIM_LIST_MINE_STARTED] = state =>
Object.assign({}, state, {
isFetchingClaimListMine: true,
});
};
reducers[ACTIONS.FETCH_CLAIM_LIST_MINE_COMPLETED] = function(state, action) {
reducers[ACTIONS.FETCH_CLAIM_LIST_MINE_COMPLETED] = (state, action) => {
const { claims } = action.data;
const byId = Object.assign({}, state.byId);
const pendingById = Object.assign({}, state.pendingById);
@ -72,11 +71,10 @@ reducers[ACTIONS.FETCH_CLAIM_LIST_MINE_COMPLETED] = function(state, action) {
});
};
reducers[ACTIONS.FETCH_CHANNEL_LIST_MINE_STARTED] = function(state) {
return Object.assign({}, state, { fetchingMyChannels: true });
};
reducers[ACTIONS.FETCH_CHANNEL_LIST_MINE_STARTED] = state =>
Object.assign({}, state, { fetchingMyChannels: true });
reducers[ACTIONS.FETCH_CHANNEL_LIST_MINE_COMPLETED] = function(state, action) {
reducers[ACTIONS.FETCH_CHANNEL_LIST_MINE_COMPLETED] = (state, action) => {
const { claims } = action.data;
const myChannelClaims = new Set(state.myChannelClaims);
const byId = Object.assign({}, state.byId);
@ -93,7 +91,7 @@ reducers[ACTIONS.FETCH_CHANNEL_LIST_MINE_COMPLETED] = function(state, action) {
});
};
reducers[ACTIONS.FETCH_CHANNEL_CLAIMS_STARTED] = function(state, action) {
reducers[ACTIONS.FETCH_CHANNEL_CLAIMS_STARTED] = (state, action) => {
const { uri, page } = action.data;
const fetchingChannelClaims = Object.assign({}, state.fetchingChannelClaims);
@ -104,7 +102,7 @@ reducers[ACTIONS.FETCH_CHANNEL_CLAIMS_STARTED] = function(state, action) {
});
};
reducers[ACTIONS.FETCH_CHANNEL_CLAIMS_COMPLETED] = function(state, action) {
reducers[ACTIONS.FETCH_CHANNEL_CLAIMS_COMPLETED] = (state, action) => {
const { uri, claims, page } = action.data;
const claimsByChannel = Object.assign({}, state.claimsByChannel);
@ -134,7 +132,7 @@ reducers[ACTIONS.FETCH_CHANNEL_CLAIMS_COMPLETED] = function(state, action) {
});
};
reducers[ACTIONS.ABANDON_CLAIM_STARTED] = function(state, action) {
reducers[ACTIONS.ABANDON_CLAIM_STARTED] = (state, action) => {
const { claimId } = action.data;
const abandoningById = Object.assign({}, state.abandoningById);
@ -145,7 +143,7 @@ reducers[ACTIONS.ABANDON_CLAIM_STARTED] = function(state, action) {
});
};
reducers[ACTIONS.ABANDON_CLAIM_SUCCEEDED] = function(state, action) {
reducers[ACTIONS.ABANDON_CLAIM_SUCCEEDED] = (state, action) => {
const { claimId } = action.data;
const byId = Object.assign({}, state.byId);
const claimsByUri = Object.assign({}, state.claimsByUri);
@ -164,7 +162,7 @@ reducers[ACTIONS.ABANDON_CLAIM_SUCCEEDED] = function(state, action) {
});
};
reducers[ACTIONS.CREATE_CHANNEL_COMPLETED] = function(state, action) {
reducers[ACTIONS.CREATE_CHANNEL_COMPLETED] = (state, action) => {
const { channelClaim } = action.data;
const byId = Object.assign({}, state.byId);
const myChannelClaims = new Set(state.myChannelClaims);

View file

@ -7,13 +7,12 @@ const defaultState = {
channelClaimCounts: {},
};
reducers[ACTIONS.FETCH_FEATURED_CONTENT_STARTED] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.FETCH_FEATURED_CONTENT_STARTED] = state =>
Object.assign({}, state, {
fetchingFeaturedContent: true,
});
};
reducers[ACTIONS.FETCH_FEATURED_CONTENT_COMPLETED] = function(state, action) {
reducers[ACTIONS.FETCH_FEATURED_CONTENT_COMPLETED] = (state, action) => {
const { uris, success } = action.data;
return Object.assign({}, state, {
@ -23,7 +22,7 @@ reducers[ACTIONS.FETCH_FEATURED_CONTENT_COMPLETED] = function(state, action) {
});
};
reducers[ACTIONS.FETCH_REWARD_CONTENT_COMPLETED] = function(state, action) {
reducers[ACTIONS.FETCH_REWARD_CONTENT_COMPLETED] = (state, action) => {
const { claimIds } = action.data;
return Object.assign({}, state, {
@ -31,7 +30,7 @@ reducers[ACTIONS.FETCH_REWARD_CONTENT_COMPLETED] = function(state, action) {
});
};
reducers[ACTIONS.RESOLVE_URIS_STARTED] = function(state, action) {
reducers[ACTIONS.RESOLVE_URIS_STARTED] = (state, action) => {
const { uris } = action.data;
const oldResolving = state.resolvingUris || [];
@ -48,7 +47,7 @@ reducers[ACTIONS.RESOLVE_URIS_STARTED] = function(state, action) {
});
};
reducers[ACTIONS.RESOLVE_URIS_COMPLETED] = function(state, action) {
reducers[ACTIONS.RESOLVE_URIS_COMPLETED] = (state, action) => {
const { resolveInfo } = action.data;
const channelClaimCounts = Object.assign({}, state.channelClaimCounts);
@ -69,7 +68,7 @@ reducers[ACTIONS.SET_PLAYING_URI] = (state, action) =>
playingUri: action.data.uri,
});
reducers[ACTIONS.FETCH_CHANNEL_CLAIM_COUNT_COMPLETED] = function(state, action) {
reducers[ACTIONS.FETCH_CHANNEL_CLAIM_COUNT_COMPLETED] = (state, action) => {
const channelClaimCounts = Object.assign({}, state.channelClaimCounts);
const { uri, totalClaims } = action.data;

View file

@ -3,7 +3,7 @@ import * as ACTIONS from 'constants/action_types';
const reducers = {};
const defaultState = {};
reducers[ACTIONS.FETCH_COST_INFO_STARTED] = function(state, action) {
reducers[ACTIONS.FETCH_COST_INFO_STARTED] = (state, action) => {
const { uri } = action.data;
const newFetching = Object.assign({}, state.fetching);
newFetching[uri] = true;
@ -13,7 +13,7 @@ reducers[ACTIONS.FETCH_COST_INFO_STARTED] = function(state, action) {
});
};
reducers[ACTIONS.FETCH_COST_INFO_COMPLETED] = function(state, action) {
reducers[ACTIONS.FETCH_COST_INFO_COMPLETED] = (state, action) => {
const { uri, costInfo } = action.data;
const newByUri = Object.assign({}, state.byUri);
const newFetching = Object.assign({}, state.fetching);

View file

@ -3,13 +3,12 @@ import * as ACTIONS from 'constants/action_types';
const reducers = {};
const defaultState = {};
reducers[ACTIONS.FILE_LIST_STARTED] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.FILE_LIST_STARTED] = state =>
Object.assign({}, state, {
isFetchingFileList: true,
});
};
reducers[ACTIONS.FILE_LIST_SUCCEEDED] = function(state, action) {
reducers[ACTIONS.FILE_LIST_SUCCEEDED] = (state, action) => {
const { fileInfos } = action.data;
const newByOutpoint = Object.assign({}, state.byOutpoint);
const pendingByOutpoint = Object.assign({}, state.pendingByOutpoint);
@ -27,7 +26,7 @@ reducers[ACTIONS.FILE_LIST_SUCCEEDED] = function(state, action) {
});
};
reducers[ACTIONS.FETCH_FILE_INFO_STARTED] = function(state, action) {
reducers[ACTIONS.FETCH_FILE_INFO_STARTED] = (state, action) => {
const { outpoint } = action.data;
const newFetching = Object.assign({}, state.fetching);
@ -38,7 +37,7 @@ reducers[ACTIONS.FETCH_FILE_INFO_STARTED] = function(state, action) {
});
};
reducers[ACTIONS.FETCH_FILE_INFO_COMPLETED] = function(state, action) {
reducers[ACTIONS.FETCH_FILE_INFO_COMPLETED] = (state, action) => {
const { fileInfo, outpoint } = action.data;
const newByOutpoint = Object.assign({}, state.byOutpoint);
@ -53,7 +52,7 @@ reducers[ACTIONS.FETCH_FILE_INFO_COMPLETED] = function(state, action) {
});
};
reducers[ACTIONS.DOWNLOADING_STARTED] = function(state, action) {
reducers[ACTIONS.DOWNLOADING_STARTED] = (state, action) => {
const { uri, outpoint, fileInfo } = action.data;
const newByOutpoint = Object.assign({}, state.byOutpoint);
@ -71,7 +70,7 @@ reducers[ACTIONS.DOWNLOADING_STARTED] = function(state, action) {
});
};
reducers[ACTIONS.DOWNLOADING_PROGRESSED] = function(state, action) {
reducers[ACTIONS.DOWNLOADING_PROGRESSED] = (state, action) => {
const { outpoint, fileInfo } = action.data;
const newByOutpoint = Object.assign({}, state.byOutpoint);
@ -86,7 +85,7 @@ reducers[ACTIONS.DOWNLOADING_PROGRESSED] = function(state, action) {
});
};
reducers[ACTIONS.DOWNLOADING_COMPLETED] = function(state, action) {
reducers[ACTIONS.DOWNLOADING_COMPLETED] = (state, action) => {
const { outpoint, fileInfo } = action.data;
const newByOutpoint = Object.assign({}, state.byOutpoint);
@ -101,7 +100,7 @@ reducers[ACTIONS.DOWNLOADING_COMPLETED] = function(state, action) {
});
};
reducers[ACTIONS.FILE_DELETE] = function(state, action) {
reducers[ACTIONS.FILE_DELETE] = (state, action) => {
const { outpoint } = action.data;
const newByOutpoint = Object.assign({}, state.byOutpoint);
@ -116,7 +115,7 @@ reducers[ACTIONS.FILE_DELETE] = function(state, action) {
});
};
reducers[ACTIONS.LOADING_VIDEO_STARTED] = function(state, action) {
reducers[ACTIONS.LOADING_VIDEO_STARTED] = (state, action) => {
const { uri } = action.data;
const newLoading = Object.assign({}, state.urisLoading);
@ -128,7 +127,7 @@ reducers[ACTIONS.LOADING_VIDEO_STARTED] = function(state, action) {
});
};
reducers[ACTIONS.LOADING_VIDEO_FAILED] = function(state, action) {
reducers[ACTIONS.LOADING_VIDEO_FAILED] = (state, action) => {
const { uri } = action.data;
const newLoading = Object.assign({}, state.urisLoading);
@ -140,7 +139,7 @@ reducers[ACTIONS.LOADING_VIDEO_FAILED] = function(state, action) {
});
};
reducers[ACTIONS.FETCH_DATE] = function(state, action) {
reducers[ACTIONS.FETCH_DATE] = (state, action) => {
const { time } = action.data;
if (time) {
return Object.assign({}, state, {

View file

@ -0,0 +1,41 @@
// @flow
import * as actions from 'constants/action_types';
import { handleActions } from 'util/redux-utils';
export type MediaState = {
paused: Boolean,
positions: {
[string]: number,
},
};
export type Action = any;
export type Dispatch = (action: Action) => any;
const defaultState = { paused: true, positions: {} };
export default handleActions(
{
[actions.MEDIA_PLAY]: (state: MediaState, action: Action) => ({
...state,
paused: false,
}),
[actions.MEDIA_PAUSE]: (state: MediaState, action: Action) => ({
...state,
paused: true,
}),
[actions.MEDIA_POSITION]: (state: MediaState, action: Action) => {
const { outpoint, position } = action.data;
return {
...state,
positions: {
...state.positions,
[outpoint]: position,
},
};
},
},
defaultState
);

View file

@ -14,7 +14,7 @@ const defaultState = {
stack: [],
};
reducers[ACTIONS.DAEMON_READY] = function(state) {
reducers[ACTIONS.DAEMON_READY] = state => {
const { currentPath } = state;
return Object.assign({}, state, {
@ -22,11 +22,10 @@ reducers[ACTIONS.DAEMON_READY] = function(state) {
});
};
reducers[ACTIONS.CHANGE_AFTER_AUTH_PATH] = function(state, action) {
return Object.assign({}, state, {
reducers[ACTIONS.CHANGE_AFTER_AUTH_PATH] = (state, action) =>
Object.assign({}, state, {
pathAfterAuth: action.data.path,
});
};
reducers[ACTIONS.HISTORY_NAVIGATE] = (state, action) => {
const { stack, index } = state;

View file

@ -9,13 +9,12 @@ const defaultState = {
claimErrorsByType: {},
};
reducers[ACTIONS.FETCH_REWARDS_STARTED] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.FETCH_REWARDS_STARTED] = state =>
Object.assign({}, state, {
fetching: true,
});
};
reducers[ACTIONS.FETCH_REWARDS_COMPLETED] = function(state, action) {
reducers[ACTIONS.FETCH_REWARDS_COMPLETED] = (state, action) => {
const { userRewards } = action.data;
const unclaimedRewards = {};
@ -55,13 +54,13 @@ function setClaimRewardState(state, reward, isClaiming, errorMessage = '') {
});
}
reducers[ACTIONS.CLAIM_REWARD_STARTED] = function(state, action) {
reducers[ACTIONS.CLAIM_REWARD_STARTED] = (state, action) => {
const { reward } = action.data;
return setClaimRewardState(state, reward, true, '');
};
reducers[ACTIONS.CLAIM_REWARD_SUCCESS] = function(state, action) {
reducers[ACTIONS.CLAIM_REWARD_SUCCESS] = (state, action) => {
const { reward } = action.data;
const unclaimedRewardsByType = Object.assign({}, state.unclaimedRewardsByType);
@ -83,13 +82,13 @@ reducers[ACTIONS.CLAIM_REWARD_SUCCESS] = function(state, action) {
return setClaimRewardState(newState, newReward, false, '');
};
reducers[ACTIONS.CLAIM_REWARD_FAILURE] = function(state, action) {
reducers[ACTIONS.CLAIM_REWARD_FAILURE] = (state, action) => {
const { reward, error } = action.data;
return setClaimRewardState(state, reward, false, error ? error.message : '');
};
reducers[ACTIONS.CLAIM_REWARD_CLEAR_ERROR] = function(state, action) {
reducers[ACTIONS.CLAIM_REWARD_CLEAR_ERROR] = (state, action) => {
const { reward } = action.data;
return setClaimRewardState(state, reward, state.claimPendingByType[reward.reward_type], '');

View file

@ -6,13 +6,12 @@ const defaultState = {
searching: false,
};
reducers[ACTIONS.SEARCH_STARTED] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.SEARCH_STARTED] = state =>
Object.assign({}, state, {
searching: true,
});
};
reducers[ACTIONS.SEARCH_COMPLETED] = function(state, action) {
reducers[ACTIONS.SEARCH_COMPLETED] = (state, action) => {
const { query, uris } = action.data;
return Object.assign({}, state, {
@ -21,11 +20,10 @@ reducers[ACTIONS.SEARCH_COMPLETED] = function(state, action) {
});
};
reducers[ACTIONS.SEARCH_CANCELLED] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.SEARCH_CANCELLED] = state =>
Object.assign({}, state, {
searching: false,
});
};
export default function reducer(state = defaultState, action) {
const handler = reducers[action.type];

View file

@ -1,6 +1,6 @@
import * as ACTIONS from 'constants/action_types';
import * as SETTINGS from 'constants/settings';
import LANGUAGES from 'constants/languages';
import * as SETTINGS from 'constants/settings';
function getLocalStorageSetting(setting, fallback) {
const localStorageVal = localStorage.getItem(`setting_${setting}`);
@ -27,13 +27,12 @@ const defaultState = {
languages: {},
};
reducers[ACTIONS.DAEMON_SETTINGS_RECEIVED] = function(state, action) {
return Object.assign({}, state, {
reducers[ACTIONS.DAEMON_SETTINGS_RECEIVED] = (state, action) =>
Object.assign({}, state, {
daemonSettings: action.data.settings,
});
};
reducers[ACTIONS.CLIENT_SETTING_CHANGED] = function(state, action) {
reducers[ACTIONS.CLIENT_SETTING_CHANGED] = (state, action) => {
const { key, value } = action.data;
const clientSettings = Object.assign({}, state.clientSettings);
@ -47,7 +46,7 @@ reducers[ACTIONS.CLIENT_SETTING_CHANGED] = function(state, action) {
});
};
reducers[ACTIONS.DOWNLOAD_LANGUAGE_SUCCEEDED] = function(state, action) {
reducers[ACTIONS.DOWNLOAD_LANGUAGE_SUCCEEDED] = (state, action) => {
const languages = Object.assign({}, state.languages);
const { language } = action.data;
@ -62,7 +61,7 @@ reducers[ACTIONS.DOWNLOAD_LANGUAGE_SUCCEEDED] = function(state, action) {
return Object.assign({}, state, { languages });
};
reducers[ACTIONS.DOWNLOAD_LANGUAGE_FAILED] = function(state, action) {
reducers[ACTIONS.DOWNLOAD_LANGUAGE_FAILED] = (state, action) => {
const languages = Object.assign({}, state.languages);
delete languages[action.data.language];
return Object.assign({}, state, { languages });

View file

@ -16,59 +16,52 @@ const defaultState = {
user: undefined,
};
reducers[ACTIONS.AUTHENTICATION_STARTED] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.AUTHENTICATION_STARTED] = state =>
Object.assign({}, state, {
authenticationIsPending: true,
userIsPending: true,
user: defaultState.user,
});
};
reducers[ACTIONS.AUTHENTICATION_SUCCESS] = function(state, action) {
return Object.assign({}, state, {
reducers[ACTIONS.AUTHENTICATION_SUCCESS] = (state, action) =>
Object.assign({}, state, {
authenticationIsPending: false,
userIsPending: false,
user: action.data.user,
});
};
reducers[ACTIONS.AUTHENTICATION_FAILURE] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.AUTHENTICATION_FAILURE] = state =>
Object.assign({}, state, {
authenticationIsPending: false,
userIsPending: false,
user: null,
});
};
reducers[ACTIONS.USER_FETCH_STARTED] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.USER_FETCH_STARTED] = state =>
Object.assign({}, state, {
userIsPending: true,
user: defaultState.user,
});
};
reducers[ACTIONS.USER_FETCH_SUCCESS] = function(state, action) {
return Object.assign({}, state, {
reducers[ACTIONS.USER_FETCH_SUCCESS] = (state, action) =>
Object.assign({}, state, {
userIsPending: false,
user: action.data.user,
});
};
reducers[ACTIONS.USER_FETCH_FAILURE] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.USER_FETCH_FAILURE] = state =>
Object.assign({}, state, {
userIsPending: true,
user: null,
});
};
reducers[ACTIONS.USER_EMAIL_NEW_STARTED] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.USER_EMAIL_NEW_STARTED] = state =>
Object.assign({}, state, {
emailNewIsPending: true,
emailNewErrorMessage: '',
});
};
reducers[ACTIONS.USER_EMAIL_NEW_SUCCESS] = function(state, action) {
reducers[ACTIONS.USER_EMAIL_NEW_SUCCESS] = (state, action) => {
const user = Object.assign({}, state.user);
user.primary_email = action.data.email;
return Object.assign({}, state, {
@ -78,28 +71,25 @@ reducers[ACTIONS.USER_EMAIL_NEW_SUCCESS] = function(state, action) {
});
};
reducers[ACTIONS.USER_EMAIL_NEW_EXISTS] = function(state, action) {
return Object.assign({}, state, {
reducers[ACTIONS.USER_EMAIL_NEW_EXISTS] = (state, action) =>
Object.assign({}, state, {
emailToVerify: action.data.email,
emailNewIsPending: false,
});
};
reducers[ACTIONS.USER_EMAIL_NEW_FAILURE] = function(state, action) {
return Object.assign({}, state, {
reducers[ACTIONS.USER_EMAIL_NEW_FAILURE] = (state, action) =>
Object.assign({}, state, {
emailNewIsPending: false,
emailNewErrorMessage: action.data.error,
});
};
reducers[ACTIONS.USER_EMAIL_VERIFY_STARTED] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.USER_EMAIL_VERIFY_STARTED] = state =>
Object.assign({}, state, {
emailVerifyIsPending: true,
emailVerifyErrorMessage: '',
});
};
reducers[ACTIONS.USER_EMAIL_VERIFY_SUCCESS] = function(state, action) {
reducers[ACTIONS.USER_EMAIL_VERIFY_SUCCESS] = (state, action) => {
const user = Object.assign({}, state.user);
user.primary_email = action.data.email;
return Object.assign({}, state, {
@ -109,36 +99,32 @@ reducers[ACTIONS.USER_EMAIL_VERIFY_SUCCESS] = function(state, action) {
});
};
reducers[ACTIONS.USER_EMAIL_VERIFY_FAILURE] = function(state, action) {
return Object.assign({}, state, {
reducers[ACTIONS.USER_EMAIL_VERIFY_FAILURE] = (state, action) =>
Object.assign({}, state, {
emailVerifyIsPending: false,
emailVerifyErrorMessage: action.data.error,
});
};
reducers[ACTIONS.USER_IDENTITY_VERIFY_STARTED] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.USER_IDENTITY_VERIFY_STARTED] = state =>
Object.assign({}, state, {
identityVerifyIsPending: true,
identityVerifyErrorMessage: '',
});
};
reducers[ACTIONS.USER_IDENTITY_VERIFY_SUCCESS] = function(state, action) {
return Object.assign({}, state, {
reducers[ACTIONS.USER_IDENTITY_VERIFY_SUCCESS] = (state, action) =>
Object.assign({}, state, {
identityVerifyIsPending: false,
identityVerifyErrorMessage: '',
user: action.data.user,
});
};
reducers[ACTIONS.USER_IDENTITY_VERIFY_FAILURE] = function(state, action) {
return Object.assign({}, state, {
reducers[ACTIONS.USER_IDENTITY_VERIFY_FAILURE] = (state, action) =>
Object.assign({}, state, {
identityVerifyIsPending: false,
identityVerifyErrorMessage: action.data.error,
});
};
reducers[ACTIONS.FETCH_ACCESS_TOKEN_SUCCESS] = function(state, action) {
reducers[ACTIONS.FETCH_ACCESS_TOKEN_SUCCESS] = (state, action) => {
const { token } = action.data;
return Object.assign({}, state, {
@ -146,48 +132,42 @@ reducers[ACTIONS.FETCH_ACCESS_TOKEN_SUCCESS] = function(state, action) {
});
};
reducers[ACTIONS.USER_INVITE_STATUS_FETCH_STARTED] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.USER_INVITE_STATUS_FETCH_STARTED] = state =>
Object.assign({}, state, {
inviteStatusIsPending: true,
});
};
reducers[ACTIONS.USER_INVITE_STATUS_FETCH_SUCCESS] = function(state, action) {
return Object.assign({}, state, {
reducers[ACTIONS.USER_INVITE_STATUS_FETCH_SUCCESS] = (state, action) =>
Object.assign({}, state, {
inviteStatusIsPending: false,
invitesRemaining: action.data.invitesRemaining,
invitees: action.data.invitees,
});
};
reducers[ACTIONS.USER_INVITE_NEW_STARTED] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.USER_INVITE_NEW_STARTED] = state =>
Object.assign({}, state, {
inviteNewIsPending: true,
inviteNewErrorMessage: '',
});
};
reducers[ACTIONS.USER_INVITE_NEW_SUCCESS] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.USER_INVITE_NEW_SUCCESS] = state =>
Object.assign({}, state, {
inviteNewIsPending: false,
inviteNewErrorMessage: '',
});
};
reducers[ACTIONS.USER_INVITE_NEW_FAILURE] = function(state, action) {
return Object.assign({}, state, {
reducers[ACTIONS.USER_INVITE_NEW_FAILURE] = (state, action) =>
Object.assign({}, state, {
inviteNewIsPending: false,
inviteNewErrorMessage: action.data.error.message,
});
};
reducers[ACTIONS.USER_INVITE_STATUS_FETCH_FAILURE] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.USER_INVITE_STATUS_FETCH_FAILURE] = state =>
Object.assign({}, state, {
inviteStatusIsPending: false,
invitesRemaining: null,
invitees: null,
});
};
export default function reducer(state = defaultState, action) {
const handler = reducers[action.type];

View file

@ -18,13 +18,12 @@ const defaultState = {
sendingSupport: false,
};
reducers[ACTIONS.FETCH_TRANSACTIONS_STARTED] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.FETCH_TRANSACTIONS_STARTED] = state =>
Object.assign({}, state, {
fetchingTransactions: true,
});
};
reducers[ACTIONS.FETCH_TRANSACTIONS_COMPLETED] = function(state, action) {
reducers[ACTIONS.FETCH_TRANSACTIONS_COMPLETED] = (state, action) => {
const byId = Object.assign({}, state.transactions);
const { transactions } = action.data;
@ -39,13 +38,12 @@ reducers[ACTIONS.FETCH_TRANSACTIONS_COMPLETED] = function(state, action) {
});
};
reducers[ACTIONS.GET_NEW_ADDRESS_STARTED] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.GET_NEW_ADDRESS_STARTED] = state =>
Object.assign({}, state, {
gettingNewAddress: true,
});
};
reducers[ACTIONS.GET_NEW_ADDRESS_COMPLETED] = function(state, action) {
reducers[ACTIONS.GET_NEW_ADDRESS_COMPLETED] = (state, action) => {
const { address } = action.data;
localStorage.setItem('receiveAddress', address);
@ -55,25 +53,22 @@ reducers[ACTIONS.GET_NEW_ADDRESS_COMPLETED] = function(state, action) {
});
};
reducers[ACTIONS.UPDATE_BALANCE] = function(state, action) {
return Object.assign({}, state, {
reducers[ACTIONS.UPDATE_BALANCE] = (state, action) =>
Object.assign({}, state, {
balance: action.data.balance,
});
};
reducers[ACTIONS.CHECK_ADDRESS_IS_MINE_STARTED] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.CHECK_ADDRESS_IS_MINE_STARTED] = state =>
Object.assign({}, state, {
checkingAddressOwnership: true,
});
};
reducers[ACTIONS.CHECK_ADDRESS_IS_MINE_COMPLETED] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.CHECK_ADDRESS_IS_MINE_COMPLETED] = state =>
Object.assign({}, state, {
checkingAddressOwnership: false,
});
};
reducers[ACTIONS.SET_DRAFT_TRANSACTION_AMOUNT] = function(state, action) {
reducers[ACTIONS.SET_DRAFT_TRANSACTION_AMOUNT] = (state, action) => {
const oldDraft = state.draftTransaction;
const newDraft = Object.assign({}, oldDraft, {
amount: parseFloat(action.data.amount),
@ -84,7 +79,7 @@ reducers[ACTIONS.SET_DRAFT_TRANSACTION_AMOUNT] = function(state, action) {
});
};
reducers[ACTIONS.SET_DRAFT_TRANSACTION_ADDRESS] = function(state, action) {
reducers[ACTIONS.SET_DRAFT_TRANSACTION_ADDRESS] = (state, action) => {
const oldDraft = state.draftTransaction;
const newDraft = Object.assign({}, oldDraft, {
address: action.data.address,
@ -95,7 +90,7 @@ reducers[ACTIONS.SET_DRAFT_TRANSACTION_ADDRESS] = function(state, action) {
});
};
reducers[ACTIONS.SEND_TRANSACTION_STARTED] = function(state) {
reducers[ACTIONS.SEND_TRANSACTION_STARTED] = state => {
const newDraftTransaction = Object.assign({}, state.draftTransaction, {
sending: true,
});
@ -105,13 +100,12 @@ reducers[ACTIONS.SEND_TRANSACTION_STARTED] = function(state) {
});
};
reducers[ACTIONS.SEND_TRANSACTION_COMPLETED] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.SEND_TRANSACTION_COMPLETED] = state =>
Object.assign({}, state, {
draftTransaction: buildDraftTransaction(),
});
};
reducers[ACTIONS.SEND_TRANSACTION_FAILED] = function(state, action) {
reducers[ACTIONS.SEND_TRANSACTION_FAILED] = (state, action) => {
const newDraftTransaction = Object.assign({}, state.draftTransaction, {
sending: false,
error: action.data.error,
@ -122,24 +116,21 @@ reducers[ACTIONS.SEND_TRANSACTION_FAILED] = function(state, action) {
});
};
reducers[ACTIONS.SUPPORT_TRANSACTION_STARTED] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.SUPPORT_TRANSACTION_STARTED] = state =>
Object.assign({}, state, {
sendingSupport: true,
});
};
reducers[ACTIONS.SUPPORT_TRANSACTION_COMPLETED] = function(state) {
return Object.assign({}, state, {
reducers[ACTIONS.SUPPORT_TRANSACTION_COMPLETED] = state =>
Object.assign({}, state, {
sendingSupport: false,
});
};
reducers[ACTIONS.SUPPORT_TRANSACTION_FAILED] = function(state, action) {
return Object.assign({}, state, {
reducers[ACTIONS.SUPPORT_TRANSACTION_FAILED] = (state, action) =>
Object.assign({}, state, {
error: action.data.error,
sendingSupport: false,
});
};
reducers[ACTIONS.FETCH_BLOCK_SUCCESS] = (state, action) => {
const { block, block: { height } } = action.data;

View file

@ -58,8 +58,6 @@ export const selectUpgradeDownloadItem = createSelector(selectState, state => st
export const selectModalProps = createSelector(selectState, state => state.modalProps);
export const selectDaemonReady = createSelector(selectState, state => state.daemonReady);
export const selectDaemonVersionMatched = createSelector(
selectState,
state => state.daemonVersionMatched

View file

@ -1,6 +1,6 @@
import { createSelector } from 'reselect';
import Lbryuri from 'lbryuri';
import { makeSelectCurrentParam } from 'redux/selectors/navigation';
import { createSelector } from 'reselect';
const selectState = state => state.claims || {};
@ -19,9 +19,7 @@ export const selectClaimsByUri = createSelector(selectState, selectClaimsById, (
if (claimId === null) {
claims[uri] = null;
} else {
const claim = byId[claimId];
claims[uri] = claim;
claims[uri] = byId[claimId];
}
});
@ -94,8 +92,7 @@ export const makeSelectMetadataForUri = uri =>
createSelector(makeSelectClaimForUri(uri), claim => {
const metadata = claim && claim.value && claim.value.stream && claim.value.stream.metadata;
const value = metadata || (claim === undefined ? undefined : null);
return value;
return metadata || (claim === undefined ? undefined : null);
});
export const makeSelectTitleForUri = uri =>
@ -109,7 +106,7 @@ export const makeSelectContentTypeForUri = uri =>
export const selectIsFetchingClaimListMine = createSelector(
selectState,
state => !!state.isFetchingClaimListMine
state => state.isFetchingClaimListMine
);
export const selectPendingClaims = createSelector(selectState, state =>
@ -154,7 +151,7 @@ export const selectMyClaimsOutpoints = createSelector(selectMyClaims, myClaims =
export const selectFetchingMyChannels = createSelector(
selectState,
state => !!state.fetchingMyChannels
state => state.fetchingMyChannels
);
export const selectMyChannelClaims = createSelector(

View file

@ -6,7 +6,7 @@ export const selectFeaturedUris = createSelector(selectState, state => state.fea
export const selectFetchingFeaturedUris = createSelector(
selectState,
state => !!state.fetchingFeaturedContent
state => state.fetchingFeaturedContent
);
export const selectResolvingUris = createSelector(selectState, state => state.resolvingUris || []);

View file

@ -1,10 +1,9 @@
import { createSelector } from 'reselect';
import {
selectClaimsByUri,
selectIsFetchingClaimListMine,
selectMyClaims,
selectMyClaimsOutpoints,
} from 'redux/selectors/claims';
import { createSelector } from 'reselect';
export const selectState = state => state.fileInfo || {};
@ -52,10 +51,6 @@ export const selectUrisLoading = createSelector(selectState, state => state.uris
export const makeSelectLoadingForUri = uri =>
createSelector(selectUrisLoading, byUri => byUri && byUri[uri]);
export const selectFileInfosPendingPublish = createSelector(selectState, state =>
Object.values(state.pendingByOutpoint || {})
);
export const selectFileInfosDownloaded = createSelector(
selectFileInfosByOutpoint,
selectMyClaims,
@ -71,20 +66,6 @@ export const selectFileInfosDownloaded = createSelector(
})
);
export const selectFileInfosPublished = createSelector(
selectFileInfosByOutpoint,
selectMyClaimsOutpoints,
selectFileInfosPendingPublish,
(byOutpoint, outpoints, pendingPublish) => {
const fileInfos = [];
outpoints.forEach(outpoint => {
const fileInfo = byOutpoint[outpoint];
if (fileInfo) fileInfos.push(fileInfo);
});
return [...fileInfos, ...pendingPublish];
}
);
// export const selectFileInfoForUri = (state, props) => {
// const claims = selectClaimsByUri(state),
// claim = claims[props.uri],
@ -94,26 +75,6 @@ export const selectFileInfosPublished = createSelector(
// return outpoint && fileInfos ? fileInfos[outpoint] : undefined;
// };
export const selectFileInfosByUri = createSelector(
selectClaimsByUri,
selectFileInfosByOutpoint,
(claimsByUri, byOutpoint) => {
const fileInfos = {};
const uris = Object.keys(claimsByUri);
uris.forEach(uri => {
const claim = claimsByUri[uri];
if (claim) {
const outpoint = `${claim.txid}:${claim.nout}`;
const fileInfo = byOutpoint[outpoint];
if (fileInfo) fileInfos[uri] = fileInfo;
}
});
return fileInfos;
}
);
export const selectDownloadingFileInfos = createSelector(
selectDownloadingByOutpoint,
selectFileInfosByOutpoint,

View file

@ -0,0 +1,14 @@
import * as settings from 'constants/settings';
import { createSelector } from 'reselect';
import lbryuri from 'lbryuri';
import { makeSelectClaimForUri } from 'redux/selectors/claims';
const _selectState = state => state.media || {};
export const selectMediaPaused = createSelector(_selectState, state => state.paused);
export const makeSelectMediaPositionForUri = uri =>
createSelector(_selectState, makeSelectClaimForUri(uri), (state, claim) => {
const outpoint = `${claim.txid}:${claim.nout}`;
return state.positions[outpoint] || null;
});

View file

@ -1,9 +1,9 @@
import { createSelector } from 'reselect';
import {
selectPageTitle,
selectCurrentPage,
selectCurrentParams,
selectPageTitle,
} from 'redux/selectors/navigation';
import { createSelector } from 'reselect';
export const selectState = state => state.search || {};
@ -13,7 +13,7 @@ export const selectSearchQuery = createSelector(
(page, params) => (page === 'search' ? params && params.query : null)
);
export const selectIsSearching = createSelector(selectState, state => !!state.searching);
export const selectIsSearching = createSelector(selectState, state => state.searching);
export const selectSearchUrisByQuery = createSelector(selectState, state => state.urisByQuery);

View file

@ -13,11 +13,6 @@ export const selectClientSettings = createSelector(
export const makeSelectClientSetting = setting =>
createSelector(selectClientSettings, settings => (settings ? settings[setting] : undefined));
export const selectSettingsIsGenerous = createSelector(
selectDaemonSettings,
settings => settings && settings.is_generous_host
);
// refactor me
export const selectShowNsfw = makeSelectClientSetting(SETTINGS.SHOW_NSFW);

View file

@ -6,7 +6,7 @@ function rewardMessage(type, amount) {
return {
new_developer: __('You earned %s for registering as a new developer.', amount),
new_user: __('You earned %s LBC new user reward.', amount),
confirm_email: __('You earned %s LBC for verifying your email address.', amount),
verified_email: __('You earned %s LBC for verifying your email address.', amount),
new_channel: __('You earned %s LBC for creating a publisher identity.', amount),
first_stream: __('You earned %s LBC for streaming your first video.', amount),
many_downloads: __('You earned %s LBC for downloading a bunch of things.', amount),
@ -39,7 +39,7 @@ rewards.SORT_ORDER = [
rewards.TYPE_NEW_DEVELOPER,
];
rewards.claimReward = function(type) {
rewards.claimReward = type => {
function requestReward(resolve, reject, params) {
if (!Lbryio.enabled) {
reject(new Error(__('Rewards are not enabled.')));

View file

@ -81,6 +81,10 @@ body {
text-align: center;
}
.icon--left-pad {
padding-left: 3px;
}
h2 {
font-size: 1.75em;
}

View file

@ -13,7 +13,7 @@ import userReducer from 'redux/reducers/user';
import walletReducer from 'redux/reducers/wallet';
import shapeShiftReducer from 'redux/reducers/shape_shift';
import subscriptionsReducer from 'redux/reducers/subscriptions';
import videoReducer from 'redux/reducers/video';
import mediaReducer from 'redux/reducers/media';
import { persistStore, autoRehydrate } from 'redux-persist';
import createCompressor from 'redux-persist-transform-compress';
import createFilter from 'redux-persist-transform-filter';
@ -64,7 +64,7 @@ const reducers = combineReducers({
user: userReducer,
shapeShift: shapeShiftReducer,
subscriptions: subscriptionsReducer,
video: videoReducer,
media: mediaReducer,
});
const bulkThunk = createBulkThunkMiddleware();

View file

@ -1,4 +1,4 @@
const { remote } = require('electron');
import { remote } from 'electron';
const application = remote.app;
const { dock } = application;

View file

@ -1,6 +1,6 @@
// these don't need to be exact
// shapeshift does a more thourough check on validity
// just general matches to prevent unneccesary api calls
// Shapeshift does a more thorough check on validity
// just general matches to prevent unnecessary api calls
export const coinRegexPatterns = {
BTC: /^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/,
BCH: /^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/,

View file

@ -14,7 +14,7 @@ export default function throttle(func, wait, options = {}) {
let previous = 0;
const getNow = () => new Date().getTime();
const later = function() {
const later = () => {
previous = options.leading === false ? 0 : getNow();
timeout = null;
result = func.apply(context, args);
@ -24,7 +24,7 @@ export default function throttle(func, wait, options = {}) {
}
};
const throttled = function(...funcArgs) {
const throttled = function throttled(...funcArgs) {
const now = getNow();
if (!previous && options.leading === false) previous = now;
@ -53,7 +53,7 @@ export default function throttle(func, wait, options = {}) {
return result;
};
throttled.cancel = function() {
throttled.cancel = () => {
clearTimeout(timeout);
previous = 0;
timeout = null;