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] [bumpversion]
current_version = 0.19.1 current_version = 0.19.4rc2
commit = True commit = True
tag = True tag = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)((?P<release>[a-z]+)(?P<candidate>\d+))? parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)((?P<release>[a-z]+)(?P<candidate>\d+))?

View file

@ -1,7 +1,5 @@
{ {
"plugins": [ "plugins": ["flowtype"],
"flowtype"
],
"extends": [ "extends": [
"airbnb", "airbnb",
"plugin:import/electron", "plugin:import/electron",
@ -28,7 +26,8 @@
"app": true "app": true
}, },
"rules": { "rules": {
"import/no-commonjs": 1, "import/no-commonjs": "warn",
"import/no-amd": 1 "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] ## [Unreleased]
### Added ### Added
* Added copy address button to the Wallet Address component on Send / Receive (#875) * Video state tracking in redux - developer only (#890)
* *
### Changed ### Changed
* * Improved text content in app (#921)
* *
### Fixed ### Fixed
* * Right click works in the app again (#928)
* Icons are now the rights size (#925)
* Fixed tip sending error (#918)
* *
### Deprecated ### 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 ## [0.19.1] - 2017-12-13
### Added ### 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? ## 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 * Submit a pull request and get paid in LBC
* Don't hesitate to contact us with any questions or comments * 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). * Anyone can view all issues in the system by clicking on the
* 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. [Issues](https://github.com/lbryio/lbry-app/issues) button at the top of the page. Feel free to
* 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. 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 | 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 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 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 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 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 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. 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.
* 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. ## 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 # 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 # Get in Touch
Name | Role | Discord | Email
--- | --- | --- | ---
[Liam](https://github.com/liamcardenas) | The application engineer in charge of community development. He is the person to contact with any development/contribution related questions. | liamsdouble | liam@lbry.io
[Tom](https://github.com/tzarebczan) | Community manager. He knows more than anyone about the app and all of its flaws. Reach out to him with any questions about how the app works, if a bug has been reported, or if a feature should be requested. | jiggytom | tom@lbry.io
[Sean](https://github.com/seanyesmunt) | An application engineer who focuses largely on UI/UX. If you have a design or implementation question, feel free to reach out to him. | sean | sean@lbry.io
Join our discord [here](https://chat.lbry.io/). | Name | Role | Discord | Email |
| --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------------ |
| [Liam](https://github.com/liamcardenas) | The application engineer in charge of community development. He is the person to contact with any development/contribution related questions. | liamsdouble | liam@lbry.io |
| [Tom](https://github.com/tzarebczan) | Community manager. He knows more than anyone about the app and all of its flaws. Reach out to him with any questions about how the app works, if a bug has been reported, or if a feature should be requested. | jiggytom | tom@lbry.io |
| [Sean](https://github.com/seanyesmunt) | An application engineer who focuses largely on UI/UX. If you have a design or implementation question, feel free to reach out to him. | sean | sean@lbry.io |
Join our Discord [here](https://chat.lbry.io/).
# More Information # More Information

108
README.md
View file

@ -1,77 +1,119 @@
# LBRY App # 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 ## Installing
We provide installers for Windows, macOS, and Debian-based Linux. We provide installers for Windows, macOS, and Debian-based Linux.
| | Windows | macOS | 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 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) | | 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
Our [releases page](https://github.com/lbryio/lbry-app/releases/latest) also contains the latest release, pre-releases, and past builds. release, pre-releases, and past builds.
To install from source or make changes to the application, continue reading below. 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 ### One-time Setup
1. Clone this repo 1. Clone this repo
2. `DEPS=true ./build.sh` 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 #### Arch Linux and Other Non-Debian Distributions
Run `yarn dev`
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) ### Running
to create distributable packages.
## Development on Windows The app can be run from the sources using the following command:
### Windows Dependency `yarn dev`
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> ### On Windows
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> #### Windows Dependency
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)
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 npm install --global --production windows-build-tools
exit 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 python -m pip install -r build\requirements.txt
npm install -g yarn npm install -g yarn
yarn install yarn install
yarn build yarn build
``` ```
3. Download the lbry daemon and cli [binaries](https://github.com/lbryio/lbry/releases) and place them in `static\daemon`
### Ongoing Development 3. Download the lbry daemon and CLI [binaries](https://github.com/lbryio/lbry/releases) and place
Run `yarn dev` them in `static\daemon`.
### Build ### 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 ## 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", "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.", "description": "A browser for the LBRY network, a digital marketplace controlled by its users.",
"homepage": "https://lbry.io/", "homepage": "https://lbry.io/",
"bugs": { "bugs": {
@ -23,7 +23,7 @@
"postinstall": "electron-builder install-app-deps", "postinstall": "electron-builder install-app-deps",
"precommit": "lint-staged", "precommit": "lint-staged",
"lint": "eslint 'src/**/*.{js,jsx}' --fix", "lint": "eslint 'src/**/*.{js,jsx}' --fix",
"pretty-print": "prettier 'src/**/*.{js,jsx,scss,json}' --write" "format": "prettier 'src/**/*.{js,jsx,scss,json}' --write"
}, },
"keywords": [ "keywords": [
"lbry" "lbry"

View file

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

View file

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

View file

@ -3,23 +3,6 @@ import PropTypes from 'prop-types';
import { formatCredits, formatFullPrice } from 'util/formatCredits'; import { formatCredits, formatFullPrice } from 'util/formatCredits';
import lbry from '../lbry.js'; 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 { export class TruncatedText extends React.PureComponent {
static propTypes = { static propTypes = {
lines: PropTypes.number, lines: PropTypes.number,

View file

@ -87,8 +87,9 @@ class FileCard extends React.PureComponent {
</div> </div>
<div className="card__subtitle"> <div className="card__subtitle">
<span className="card__indicators card--file-subtitle"> <span className="card__indicators card--file-subtitle">
<FilePrice uri={uri} /> {isRewardContent && <Icon icon={icons.FEATURED} />}{' '} <FilePrice uri={uri} />{' '}
{fileInfo && <Icon icon={icons.LOCAL} />} {isRewardContent && <Icon icon={icons.FEATURED} leftPad />}{' '}
{fileInfo && <Icon icon={icons.LOCAL} leftPad />}
</span> </span>
<span className="card--file-subtitle"> <span className="card--file-subtitle">
<UriIndicator uri={uri} link span smallCard /> <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 { doFetchAvailability } from 'redux/actions/availability';
import { doOpenFileInShell } from 'redux/actions/file_info'; import { doOpenFileInShell } from 'redux/actions/file_info';
import { doPurchaseUri, doStartDownload } from 'redux/actions/content'; import { doPurchaseUri, doStartDownload } from 'redux/actions/content';
import { setVideoPause } from 'redux/actions/video'; import { doPause } from 'redux/actions/media';
import FileDownloadLink from './view'; import FileDownloadLink from './view';
const select = (state, props) => ({ const select = (state, props) => ({
@ -25,7 +25,7 @@ const perform = dispatch => ({
openInShell: path => dispatch(doOpenFileInShell(path)), openInShell: path => dispatch(doOpenFileInShell(path)),
purchaseUri: uri => dispatch(doPurchaseUri(uri)), purchaseUri: uri => dispatch(doPurchaseUri(uri)),
restartDownload: (uri, outpoint) => dispatch(doStartDownload(uri, outpoint)), restartDownload: (uri, outpoint) => dispatch(doStartDownload(uri, outpoint)),
setVideoPause: val => dispatch(setVideoPause(val)), doPause: () => dispatch(doPause()),
}); });
export default connect(select, perform)(FileDownloadLink); export default connect(select, perform)(FileDownloadLink);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,5 @@
import React from 'react'; import React from 'react';
import Link from 'component/link'; import Link from 'component/link';
import { CreditAmount } from 'component/common';
import { Form, FormRow, Submit } from 'component/form.js'; import { Form, FormRow, Submit } from 'component/form.js';
class UserEmailVerify extends React.PureComponent { class UserEmailVerify extends React.PureComponent {
@ -20,11 +19,16 @@ class UserEmailVerify extends React.PureComponent {
handleSubmit() { handleSubmit() {
const { code } = this.state; 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() { render() {
const { cancelButton, errorMessage, email, isPending, rewardAmount } = this.props; const { cancelButton, errorMessage, email, isPending } = this.props;
return ( return (
<Form onSubmit={this.handleSubmit.bind(this)}> <Form onSubmit={this.handleSubmit.bind(this)}>
<p>Please enter the verification code emailed to {email}.</p> <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 { doChangeVolume } from 'redux/actions/app';
import { selectVolume } from 'redux/selectors/app'; import { selectVolume } from 'redux/selectors/app';
import { doPlayUri, doSetPlayingUri } from 'redux/actions/content'; import { doPlayUri, doSetPlayingUri } from 'redux/actions/content';
import { doPlay, doPause, savePosition } from 'redux/actions/media';
import { makeSelectMetadataForUri, makeSelectContentTypeForUri } from 'redux/selectors/claims'; import { makeSelectMetadataForUri, makeSelectContentTypeForUri } from 'redux/selectors/claims';
import { setVideoPause } from 'redux/actions/video';
import { import {
makeSelectFileInfoForUri, makeSelectFileInfoForUri,
makeSelectLoadingForUri, makeSelectLoadingForUri,
@ -12,11 +12,13 @@ import {
} from 'redux/selectors/file_info'; } from 'redux/selectors/file_info';
import { makeSelectCostInfoForUri } from 'redux/selectors/cost_info'; import { makeSelectCostInfoForUri } from 'redux/selectors/cost_info';
import { selectShowNsfw } from 'redux/selectors/settings'; import { selectShowNsfw } from 'redux/selectors/settings';
import { selectVideoPause } from 'redux/selectors/video'; import { selectMediaPaused, makeSelectMediaPositionForUri } from 'redux/selectors/media';
import Video from './view'; import Video from './view';
import { selectPlayingUri } from 'redux/selectors/content'; import { selectPlayingUri } from 'redux/selectors/content';
import { makeSelectClaimForUri } from 'redux/selectors/claims';
const select = (state, props) => ({ const select = (state, props) => ({
claim: makeSelectClaimForUri(props.uri)(state),
costInfo: makeSelectCostInfoForUri(props.uri)(state), costInfo: makeSelectCostInfoForUri(props.uri)(state),
fileInfo: makeSelectFileInfoForUri(props.uri)(state), fileInfo: makeSelectFileInfoForUri(props.uri)(state),
metadata: makeSelectMetadataForUri(props.uri)(state), metadata: makeSelectMetadataForUri(props.uri)(state),
@ -26,14 +28,17 @@ const select = (state, props) => ({
playingUri: selectPlayingUri(state), playingUri: selectPlayingUri(state),
contentType: makeSelectContentTypeForUri(props.uri)(state), contentType: makeSelectContentTypeForUri(props.uri)(state),
volume: selectVolume(state), volume: selectVolume(state),
videoPause: selectVideoPause(state), mediaPaused: selectMediaPaused(state),
mediaPosition: makeSelectMediaPositionForUri(props.uri)(state),
}); });
const perform = dispatch => ({ const perform = dispatch => ({
play: uri => dispatch(doPlayUri(uri)), play: uri => dispatch(doPlayUri(uri)),
cancelPlay: () => dispatch(doSetPlayingUri(null)), cancelPlay: () => dispatch(doSetPlayingUri(null)),
changeVolume: volume => dispatch(doChangeVolume(volume)), changeVolume: volume => dispatch(doChangeVolume(volume)),
setVideoPause: val => dispatch(setVideoPause(val)), doPlay: () => dispatch(doPlay()),
doPause: () => dispatch(doPause()),
savePosition: (claimId, position) => dispatch(savePosition(claimId, position)),
}); });
export default connect(select, perform)(Video); export default connect(select, perform)(Video);

View file

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

View file

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

View file

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

View file

@ -157,3 +157,8 @@ export const HAS_FETCHED_SUBSCRIPTIONS = 'HAS_FETCHED_SUBSCRIPTIONS';
// Video controls // Video controls
export const SET_VIDEO_PAUSE = 'SET_VIDEO_PAUSE'; 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 */ /* eslint-disable react/jsx-filename-extension */
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import App from 'component/app';
import SnackBar from 'component/snackBar';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import store from 'store'; import { doConditionalAuthNavigate, doDaemonReady, doShowSnackBar } from 'redux/actions/app';
import SplashScreen from 'component/splash';
import { doDaemonReady } from 'redux/actions/app';
import { doNavigate } from 'redux/actions/navigation'; import { doNavigate } from 'redux/actions/navigation';
import { doDownloadLanguages } from 'redux/actions/settings'; import { doDownloadLanguages } from 'redux/actions/settings';
import * as ACTIONS from 'constants/action_types'; import { doUserEmailVerify } from 'redux/actions/user';
import amplitude from 'amplitude-js';
import lbry from 'lbry';
import 'scss/all.scss'; import 'scss/all.scss';
import { ipcRenderer, remote, shell } from 'electron'; import store from 'store';
import app from './app'; import app from './app';
const { contextMenu } = remote.require('./main.js'); const { contextMenu } = remote.require('./main.js');
window.addEventListener('contextmenu', event => { window.addEventListener('contextmenu', event => {
contextMenu.showContextMenu( contextMenu(remote.getCurrentWindow(), event.x, event.y, app.env === 'development');
remote.getCurrentWindow(),
event.x,
event.y,
app.env === 'development'
);
event.preventDefault(); event.preventDefault();
}); });
ipcRenderer.on('open-uri-requested', (event, uri) => { ipcRenderer.on('open-uri-requested', (event, uri, newSession) => {
if (uri && uri.startsWith('lbry://')) { 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(''); dock.setBadge('');
}); });
(function(history, ...args) { ((history, ...args) => {
const { replaceState } = history; const { replaceState } = history;
const newHistory = history; const newHistory = history;
newHistory.replaceState = function(_, __, path) { newHistory.replaceState = (_, __, path) => {
amplitude.getInstance().logEvent('NAVIGATION', { destination: path ? path.slice(1) : path }); amplitude.getInstance().logEvent('NAVIGATION', { destination: path ? path.slice(1) : path });
return replaceState.apply(history, args); return replaceState.apply(history, args);
}; };
@ -85,7 +96,7 @@ document.addEventListener('click', event => {
} }
}); });
const init = function initializeReactApp() { const init = () => {
app.store.dispatch(doDownloadLanguages()); app.store.dispatch(doDownloadLanguages());
function onDaemonReady() { function onDaemonReady() {

View file

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

View file

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

View file

@ -1,6 +1,6 @@
import { ipcRenderer } from 'electron';
import Lbry from 'lbry'; import Lbry from 'lbry';
import querystring from 'querystring'; import querystring from 'querystring';
import { ipcRenderer } from 'electron';
const Lbryio = { const Lbryio = {
enabled: true, enabled: true,
@ -15,7 +15,7 @@ const CONNECTION_STRING = process.env.LBRY_APP_API_URL
const EXCHANGE_RATE_TIMEOUT = 20 * 60 * 1000; const EXCHANGE_RATE_TIMEOUT = 20 * 60 * 1000;
Lbryio.getExchangeRates = function() { Lbryio.getExchangeRates = () => {
if ( if (
!Lbryio.exchangeLastFetched || !Lbryio.exchangeLastFetched ||
Date.now() - Lbryio.exchangeLastFetched > EXCHANGE_RATE_TIMEOUT Date.now() - Lbryio.exchangeLastFetched > EXCHANGE_RATE_TIMEOUT
@ -33,7 +33,7 @@ Lbryio.getExchangeRates = function() {
return Lbryio.exchangePromise; return Lbryio.exchangePromise;
}; };
Lbryio.call = function(resource, action, params = {}, method = 'get') { Lbryio.call = (resource, action, params = {}, method = 'get') => {
if (!Lbryio.enabled) { if (!Lbryio.enabled) {
console.log(__('Internal API disabled')); console.log(__('Internal API disabled'));
return Promise.reject(new Error(__('LBRY internal API is disabled'))); return Promise.reject(new Error(__('LBRY internal API is disabled')));
@ -54,7 +54,7 @@ Lbryio.call = function(resource, action, params = {}, method = 'get') {
} else { } else {
error = new Error('Unknown API error signature'); 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); return Promise.reject(error);
}); });
} }
@ -109,7 +109,7 @@ Lbryio.setAuthToken = token => {
Lbryio.getCurrentUser = () => Lbryio.call('user', 'me'); Lbryio.getCurrentUser = () => Lbryio.call('user', 'me');
Lbryio.authenticate = function() { Lbryio.authenticate = () => {
if (!Lbryio.enabled) { if (!Lbryio.enabled) {
return new Promise(resolve => { return new Promise(resolve => {
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 * - contentName (string): For anon claims, the name; for channel claims, the path
* - channelName (string, if present): Channel name without @ * - 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 // Break into components. Empty sub-matches are converted to null
const componentsRegex = new RegExp( const componentsRegex = new RegExp(
'^((?:lbry://)?)' + // protocol '^((?:lbry://)?)' + // protocol
@ -147,7 +147,7 @@ Lbryuri.parse = function(uri, requireProto = false) {
* *
* The channelName key will accept names with or without the @ prefix. * 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; const { claimId, claimSequence, bidPosition, contentName, channelName } = uriObj;
let { name, path } = 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 /* Takes a parseable LBRY URI and converts it to standard, canonical format (currently this just
* consists of adding the lbry:// prefix if needed) */ * consists of adding the lbry:// prefix if needed) */
Lbryuri.normalize = function(uri) { Lbryuri.normalize = uri => {
if (uri.match(/pending_claim/)) return uri; if (uri.match(/pending_claim/)) return uri;
const { name, path, bidPosition, claimSequence, claimId } = Lbryuri.parse(uri); const { name, path, bidPosition, claimSequence, claimId } = Lbryuri.parse(uri);
return Lbryuri.build({ name, path, claimSequence, bidPosition, claimId }); return Lbryuri.build({ name, path, claimSequence, bidPosition, claimId });
}; };
Lbryuri.isValid = function(uri) { Lbryuri.isValid = uri => {
let parts; let parts;
try { try {
parts = Lbryuri.parse(Lbryuri.normalize(uri)); parts = Lbryuri.parse(Lbryuri.normalize(uri));
@ -209,12 +209,12 @@ Lbryuri.isValid = function(uri) {
return parts && parts.name; return parts && parts.name;
}; };
Lbryuri.isValidName = function(name, checkCase = true) { Lbryuri.isValidName = (name, checkCase = true) => {
const regexp = new RegExp('^[a-z0-9-]+$', checkCase ? '' : 'i'); const regexp = new RegExp('^[a-z0-9-]+$', checkCase ? '' : 'i');
return regexp.test(name); return regexp.test(name);
}; };
Lbryuri.isClaimable = function(uri) { Lbryuri.isClaimable = uri => {
let parts; let parts;
try { try {
parts = Lbryuri.parse(Lbryuri.normalize(uri)); parts = Lbryuri.parse(Lbryuri.normalize(uri));

View file

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

View file

@ -9,7 +9,7 @@ const select = (state, props) => {
const selectReward = makeSelectRewardByType(); const selectReward = makeSelectRewardByType();
return { 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 ReactDOM from 'react-dom';
import lbryuri from 'lbryuri'; import lbryuri from 'lbryuri';
import FileCard from 'component/fileCard'; 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 ToolTip from 'component/tooltip.js';
import SubHeader from 'component/subHeader'; import SubHeader from 'component/subHeader';
import classnames from 'classnames'; import classnames from 'classnames';

View file

@ -3,7 +3,8 @@ import React from 'react';
import lbry from 'lbry.js'; import lbry from 'lbry.js';
import Link from 'component/link'; import Link from 'component/link';
import SubHeader from 'component/subHeader'; 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 { class HelpPage extends React.PureComponent {
constructor(props) { constructor(props) {

View file

@ -1,22 +1,24 @@
/* eslint-disable import/no-commonjs */
import * as ACTIONS from 'constants/action_types'; import * as ACTIONS from 'constants/action_types';
import * as MODALS from 'constants/modal_types';
import { ipcRenderer, remote } from 'electron';
import Lbry from 'lbry'; 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 { import {
selectUpdateUrl, selectCurrentModal,
selectUpgradeDownloadPath,
selectUpgradeDownloadItem,
selectUpgradeFilename,
selectIsUpgradeSkipped, selectIsUpgradeSkipped,
selectRemoteVersion, selectRemoteVersion,
selectCurrentModal, selectUpdateUrl,
selectUpgradeDownloadItem,
selectUpgradeDownloadPath,
selectUpgradeFilename,
} from 'redux/selectors/app'; } 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 { download } = remote.require('electron-dl');
const Fs = remote.require('fs'); const Fs = remote.require('fs');
@ -56,7 +58,7 @@ export function doSkipUpgrade() {
} }
export function doStartUpgrade() { export function doStartUpgrade() {
return function(dispatch, getState) { return (dispatch, getState) => {
const state = getState(); const state = getState();
const upgradeDownloadPath = selectUpgradeDownloadPath(state); const upgradeDownloadPath = selectUpgradeDownloadPath(state);
@ -65,7 +67,7 @@ export function doStartUpgrade() {
} }
export function doDownloadUpgrade() { export function doDownloadUpgrade() {
return function(dispatch, getState) { return (dispatch, getState) => {
const state = getState(); const state = getState();
// Make a new directory within temp directory so the filename is guaranteed to be available // 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); const dir = Fs.mkdtempSync(remote.app.getPath('temp') + Path.sep);
@ -104,7 +106,7 @@ export function doDownloadUpgrade() {
} }
export function doCancelUpgrade() { export function doCancelUpgrade() {
return function(dispatch, getState) { return (dispatch, getState) => {
const state = getState(); const state = getState();
const upgradeDownloadItem = selectUpgradeDownloadItem(state); const upgradeDownloadItem = selectUpgradeDownloadItem(state);
@ -127,7 +129,7 @@ export function doCancelUpgrade() {
} }
export function doCheckUpgradeAvailable() { export function doCheckUpgradeAvailable() {
return function(dispatch, getState) { return (dispatch, getState) => {
const state = getState(); const state = getState();
dispatch({ dispatch({
type: ACTIONS.CHECK_UPGRADE_START, 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. Initiate a timer that will check for an app upgrade every 10 minutes.
*/ */
export function doCheckUpgradeSubscribe() { export function doCheckUpgradeSubscribe() {
return function(dispatch) { return dispatch => {
const checkUpgradeTimer = setInterval( const checkUpgradeTimer = setInterval(
() => dispatch(doCheckUpgradeAvailable()), () => dispatch(doCheckUpgradeAvailable()),
CHECK_UPGRADE_INTERVAL CHECK_UPGRADE_INTERVAL
@ -183,7 +185,7 @@ export function doCheckUpgradeSubscribe() {
} }
export function doCheckDaemonVersion() { export function doCheckDaemonVersion() {
return function(dispatch) { return dispatch => {
Lbry.version().then(({ lbrynet_version: lbrynetVersion }) => { Lbry.version().then(({ lbrynet_version: lbrynetVersion }) => {
dispatch({ dispatch({
type: type:
@ -196,7 +198,7 @@ export function doCheckDaemonVersion() {
} }
export function doAlertError(errorList) { export function doAlertError(errorList) {
return function(dispatch) { return dispatch => {
dispatch({ dispatch({
type: ACTIONS.OPEN_MODAL, type: ACTIONS.OPEN_MODAL,
data: { data: {
@ -208,7 +210,7 @@ export function doAlertError(errorList) {
} }
export function doDaemonReady() { export function doDaemonReady() {
return function(dispatch, getState) { return (dispatch, getState) => {
const state = getState(); const state = getState();
dispatch(doAuthenticate()); dispatch(doAuthenticate());
@ -238,7 +240,7 @@ export function doRemoveSnackBarSnack() {
} }
export function doClearCache() { export function doClearCache() {
return function() { return () => {
window.cacheStore.purge(); window.cacheStore.purge();
return Promise.resolve(); return Promise.resolve();
@ -246,13 +248,13 @@ export function doClearCache() {
} }
export function doQuit() { export function doQuit() {
return function() { return () => {
remote.app.quit(); remote.app.quit();
}; };
} }
export function doChangeVolume(volume) { export function doChangeVolume(volume) {
return function(dispatch) { return dispatch => {
dispatch({ dispatch({
type: ACTIONS.VOLUME_CHANGED, type: ACTIONS.VOLUME_CHANGED,
data: { 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 // eslint-disable-next-line import/prefer-default-export
export function doFetchAvailability() { export function doFetchAvailability() {
return function() { return () => {
/* /*
this is disabled atm - Jeremy this is disabled atm - Jeremy
*/ */

View file

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

View file

@ -1,25 +1,25 @@
import * as ACTIONS from 'constants/action_types'; import * as ACTIONS from 'constants/action_types';
import { shell } from 'electron';
import Lbry from 'lbry'; 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 { import {
selectClaimsByUri, selectClaimsByUri,
selectIsFetchingClaimListMine, selectIsFetchingClaimListMine,
selectMyClaimsOutpoints, selectMyClaimsOutpoints,
} from 'redux/selectors/claims'; } from 'redux/selectors/claims';
import { import {
selectIsFetchingFileList,
selectFileInfosByOutpoint, selectFileInfosByOutpoint,
selectUrisLoading, selectIsFetchingFileList,
selectTotalDownloadProgress, selectTotalDownloadProgress,
selectUrisLoading,
} from 'redux/selectors/file_info'; } 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 batchActions from 'util/batchActions';
import { shell } from 'electron'; import setProgressBar from 'util/setProgressBar';
export function doFetchFileInfo(uri) { export function doFetchFileInfo(uri) {
return function(dispatch, getState) { return (dispatch, getState) => {
const state = getState(); const state = getState();
const claim = selectClaimsByUri(state)[uri]; const claim = selectClaimsByUri(state)[uri];
const outpoint = claim ? `${claim.txid}:${claim.nout}` : null; const outpoint = claim ? `${claim.txid}:${claim.nout}` : null;
@ -47,7 +47,7 @@ export function doFetchFileInfo(uri) {
} }
export function doFileList() { export function doFileList() {
return function(dispatch, getState) { return (dispatch, getState) => {
const state = getState(); const state = getState();
const isFetching = selectIsFetchingFileList(state); const isFetching = selectIsFetchingFileList(state);
@ -69,13 +69,13 @@ export function doFileList() {
} }
export function doOpenFileInFolder(path) { export function doOpenFileInFolder(path) {
return function() { return () => {
shell.showItemInFolder(path); shell.showItemInFolder(path);
}; };
} }
export function doOpenFileInShell(path) { export function doOpenFileInShell(path) {
return function(dispatch) { return dispatch => {
const success = shell.openItem(path); const success = shell.openItem(path);
if (!success) { if (!success) {
dispatch(doOpenFileInFolder(path)); dispatch(doOpenFileInFolder(path));
@ -84,7 +84,7 @@ export function doOpenFileInShell(path) {
} }
export function doDeleteFile(outpoint, deleteFromComputer, abandonClaim) { export function doDeleteFile(outpoint, deleteFromComputer, abandonClaim) {
return function(dispatch, getState) { return (dispatch, getState) => {
const state = getState(); const state = getState();
Lbry.file_delete({ Lbry.file_delete({
@ -92,7 +92,7 @@ export function doDeleteFile(outpoint, deleteFromComputer, abandonClaim) {
delete_from_download_dir: deleteFromComputer, 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); const myClaimsOutpoints = selectMyClaimsOutpoints(state);
if (abandonClaim && myClaimsOutpoints.indexOf(outpoint) !== -1) { if (abandonClaim && myClaimsOutpoints.indexOf(outpoint) !== -1) {
const byOutpoint = selectFileInfosByOutpoint(state); const byOutpoint = selectFileInfosByOutpoint(state);
@ -119,7 +119,7 @@ export function doDeleteFile(outpoint, deleteFromComputer, abandonClaim) {
} }
export function doDeleteFileAndGoBack(fileInfo, deleteFromComputer, abandonClaim) { export function doDeleteFileAndGoBack(fileInfo, deleteFromComputer, abandonClaim) {
return function(dispatch) { return dispatch => {
const actions = []; const actions = [];
actions.push(doCloseModal()); actions.push(doCloseModal());
actions.push(doHistoryBack()); actions.push(doHistoryBack());
@ -129,7 +129,7 @@ export function doDeleteFileAndGoBack(fileInfo, deleteFromComputer, abandonClaim
} }
export function doFetchFileInfosAndPublishedClaims() { export function doFetchFileInfosAndPublishedClaims() {
return function(dispatch, getState) { return (dispatch, getState) => {
const state = getState(); const state = getState();
const isFetchingClaimListMine = selectIsFetchingClaimListMine(state); const isFetchingClaimListMine = selectIsFetchingClaimListMine(state);
const isFetchingFileInfo = selectIsFetchingFileList(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 * 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'; import { toQueryString } from 'util/query_params';
export function doNavigate(path, params = {}, options = {}) { export function doNavigate(path, params = {}, options = {}) {
return function(dispatch) { return dispatch => {
if (!path) { if (!path) {
return; return;
} }
@ -23,7 +23,7 @@ export function doNavigate(path, params = {}, options = {}) {
} }
export function doAuthNavigate(pathAfterAuth = null, params = {}) { export function doAuthNavigate(pathAfterAuth = null, params = {}) {
return function(dispatch) { return dispatch => {
if (pathAfterAuth) { if (pathAfterAuth) {
dispatch({ dispatch({
type: ACTIONS.CHANGE_AFTER_AUTH_PATH, type: ACTIONS.CHANGE_AFTER_AUTH_PATH,
@ -47,19 +47,15 @@ export function doHistoryTraverse(dispatch, state, modifier) {
} }
export function doHistoryBack() { export function doHistoryBack() {
return function(dispatch, getState) { return (dispatch, getState) => doHistoryTraverse(dispatch, getState(), -1);
return doHistoryTraverse(dispatch, getState(), -1);
};
} }
export function doHistoryForward() { export function doHistoryForward() {
return function(dispatch, getState) { return (dispatch, getState) => doHistoryTraverse(dispatch, getState(), 1);
return doHistoryTraverse(dispatch, getState(), 1);
};
} }
export function doRecordScroll(scroll) { export function doRecordScroll(scroll) {
return function(dispatch) { return dispatch => {
dispatch({ dispatch({
type: ACTIONS.WINDOW_SCROLLED, type: ACTIONS.WINDOW_SCROLLED,
data: { scrollY: scroll }, data: { scrollY: scroll },

View file

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

View file

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

View file

@ -1,12 +1,12 @@
import * as ACTIONS from 'constants/action_types'; import * as ACTIONS from 'constants/action_types';
import * as SETTINGS from 'constants/settings'; import * as SETTINGS from 'constants/settings';
import Lbry from 'lbry';
import Fs from 'fs'; import Fs from 'fs';
import Http from 'http'; import Http from 'http';
import Lbry from 'lbry';
export function doFetchDaemonSettings() { export function doFetchDaemonSettings() {
return function(dispatch) { return dispatch => {
Lbry.settings_get().then(settings => { Lbry.settings_get().then(settings => {
dispatch({ dispatch({
type: ACTIONS.DAEMON_SETTINGS_RECEIVED, type: ACTIONS.DAEMON_SETTINGS_RECEIVED,
@ -19,15 +19,15 @@ export function doFetchDaemonSettings() {
} }
export function doSetDaemonSetting(key, value) { export function doSetDaemonSetting(key, value) {
return function(dispatch) { return dispatch => {
const settings = {}; const newSettings = {};
settings[key] = value; newSettings[key] = value;
Lbry.settings_set(settings).then(settings); Lbry.settings_set(newSettings).then(newSettings);
Lbry.settings_get().then(remoteSettings => { Lbry.settings_get().then(settings => {
dispatch({ dispatch({
type: ACTIONS.DAEMON_SETTINGS_RECEIVED, type: ACTIONS.DAEMON_SETTINGS_RECEIVED,
data: { data: {
remoteSettings, settings,
}, },
}); });
}); });
@ -45,14 +45,14 @@ export function doSetClientSetting(key, value) {
} }
export function doGetThemes() { export function doGetThemes() {
return function(dispatch) { return dispatch => {
const themes = ['light', 'dark']; const themes = ['light', 'dark'];
dispatch(doSetClientSetting(SETTINGS.THEMES, themes)); dispatch(doSetClientSetting(SETTINGS.THEMES, themes));
}; };
} }
export function doDownloadLanguage(langFile) { export function doDownloadLanguage(langFile) {
return function(dispatch) { return dispatch => {
const destinationPath = `${app.i18n.directory}/${langFile}`; const destinationPath = `${app.i18n.directory}/${langFile}`;
const language = langFile.replace('.json', ''); const language = langFile.replace('.json', '');
const errorHandler = () => { const errorHandler = () => {
@ -105,7 +105,7 @@ export function doDownloadLanguage(langFile) {
} }
export function doDownloadLanguages() { export function doDownloadLanguages() {
return function() { return () => {
// temporarily disable i18n so I can get a working build out -- Jeremy // temporarily disable i18n so I can get a working build out -- Jeremy
// if (!Fs.existsSync(app.i18n.directory)) { // if (!Fs.existsSync(app.i18n.directory)) {
// Fs.mkdirSync(app.i18n.directory); // Fs.mkdirSync(app.i18n.directory);
@ -135,7 +135,7 @@ export function doDownloadLanguages() {
} }
export function doChangeLanguage(language) { export function doChangeLanguage(language) {
return function(dispatch) { return dispatch => {
dispatch(doSetClientSetting(SETTINGS.LANGUAGE, language)); dispatch(doSetClientSetting(SETTINGS.LANGUAGE, language));
app.i18n.setLocale(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 * as MODALS from 'constants/modal_types';
import Lbryio from 'lbryio'; import Lbryio from 'lbryio';
import { doOpenModal, doShowSnackBar } from 'redux/actions/app'; 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 { selectEmailToVerify } from 'redux/selectors/user';
import rewards from 'rewards'; import rewards from 'rewards';
export function doFetchInviteStatus() { export function doFetchInviteStatus() {
return function(dispatch) { return dispatch => {
dispatch({ dispatch({
type: ACTIONS.USER_INVITE_STATUS_FETCH_STARTED, type: ACTIONS.USER_INVITE_STATUS_FETCH_STARTED,
}); });
@ -32,7 +32,7 @@ export function doFetchInviteStatus() {
} }
export function doAuthenticate() { export function doAuthenticate() {
return function(dispatch) { return dispatch => {
dispatch({ dispatch({
type: ACTIONS.AUTHENTICATION_STARTED, type: ACTIONS.AUTHENTICATION_STARTED,
}); });
@ -56,7 +56,7 @@ export function doAuthenticate() {
} }
export function doUserFetch() { export function doUserFetch() {
return function(dispatch) { return dispatch => {
dispatch({ dispatch({
type: ACTIONS.USER_FETCH_STARTED, type: ACTIONS.USER_FETCH_STARTED,
}); });
@ -79,7 +79,7 @@ export function doUserFetch() {
} }
export function doUserEmailNew(email) { export function doUserEmailNew(email) {
return function(dispatch) { return dispatch => {
dispatch({ dispatch({
type: ACTIONS.USER_EMAIL_NEW_STARTED, type: ACTIONS.USER_EMAIL_NEW_STARTED,
email, email,
@ -116,20 +116,31 @@ export function doUserEmailNew(email) {
}; };
} }
export function doUserEmailVerify(verificationToken) { export function doUserEmailVerifyFailure(error) {
return function(dispatch, getState) { return {
type: ACTIONS.USER_EMAIL_VERIFY_FAILURE,
data: { error },
};
}
export function doUserEmailVerify(verificationToken, recaptcha) {
return (dispatch, getState) => {
const email = selectEmailToVerify(getState()); const email = selectEmailToVerify(getState());
const trimmedVerificationToken = verificationToken.toString().trim();
dispatch({ dispatch({
type: ACTIONS.USER_EMAIL_VERIFY_STARTED, type: ACTIONS.USER_EMAIL_VERIFY_STARTED,
code: trimmedVerificationToken, code: verificationToken,
recaptcha,
}); });
Lbryio.call( Lbryio.call(
'user_email', 'user_email',
'confirm', 'confirm',
{ verification_token: trimmedVerificationToken, email }, {
verification_token: verificationToken,
email,
recaptcha,
},
'post' 'post'
) )
.then(userEmail => { .then(userEmail => {
@ -138,23 +149,17 @@ export function doUserEmailVerify(verificationToken) {
type: ACTIONS.USER_EMAIL_VERIFY_SUCCESS, type: ACTIONS.USER_EMAIL_VERIFY_SUCCESS,
data: { email }, data: { email },
}); });
dispatch(doClaimRewardType(rewards.TYPE_CONFIRM_EMAIL));
dispatch(doUserFetch()); dispatch(doUserFetch());
} else { } else {
throw new Error('Your email is still not verified.'); // shouldn't happen throw new Error('Your email is still not verified.'); // shouldn't happen
} }
}) })
.catch(error => { .catch(error => dispatch(doUserEmailVerifyFailure(error)));
dispatch({
type: ACTIONS.USER_EMAIL_VERIFY_FAILURE,
data: { error },
});
});
}; };
} }
export function doUserIdentityVerify(stripeToken) { export function doUserIdentityVerify(stripeToken) {
return function(dispatch) { return dispatch => {
dispatch({ dispatch({
type: ACTIONS.USER_IDENTITY_VERIFY_STARTED, type: ACTIONS.USER_IDENTITY_VERIFY_STARTED,
token: stripeToken, token: stripeToken,
@ -182,7 +187,7 @@ export function doUserIdentityVerify(stripeToken) {
} }
export function doFetchAccessToken() { export function doFetchAccessToken() {
return function(dispatch) { return dispatch => {
const success = token => const success = token =>
dispatch({ dispatch({
type: ACTIONS.FETCH_ACCESS_TOKEN_SUCCESS, type: ACTIONS.FETCH_ACCESS_TOKEN_SUCCESS,
@ -193,7 +198,7 @@ export function doFetchAccessToken() {
} }
export function doUserInviteNew(email) { export function doUserInviteNew(email) {
return function(dispatch) { return dispatch => {
dispatch({ dispatch({
type: ACTIONS.USER_INVITE_NEW_STARTED, type: ACTIONS.USER_INVITE_NEW_STARTED,
}); });

View file

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

View file

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

View file

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

View file

@ -4,7 +4,7 @@ const reducers = {};
const defaultState = {}; const defaultState = {};
reducers[ACTIONS.RESOLVE_URIS_COMPLETED] = function(state, action) { reducers[ACTIONS.RESOLVE_URIS_COMPLETED] = (state, action) => {
const { resolveInfo } = action.data; const { resolveInfo } = action.data;
const byUri = Object.assign({}, state.claimsByUri); const byUri = Object.assign({}, state.claimsByUri);
const byId = Object.assign({}, state.byId); 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) { reducers[ACTIONS.FETCH_CLAIM_LIST_MINE_STARTED] = state =>
return Object.assign({}, state, { Object.assign({}, state, {
isFetchingClaimListMine: true, 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 { claims } = action.data;
const byId = Object.assign({}, state.byId); const byId = Object.assign({}, state.byId);
const pendingById = Object.assign({}, state.pendingById); 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) { reducers[ACTIONS.FETCH_CHANNEL_LIST_MINE_STARTED] = state =>
return Object.assign({}, state, { fetchingMyChannels: true }); 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 { claims } = action.data;
const myChannelClaims = new Set(state.myChannelClaims); const myChannelClaims = new Set(state.myChannelClaims);
const byId = Object.assign({}, state.byId); 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 { uri, page } = action.data;
const fetchingChannelClaims = Object.assign({}, state.fetchingChannelClaims); 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 { uri, claims, page } = action.data;
const claimsByChannel = Object.assign({}, state.claimsByChannel); 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 { claimId } = action.data;
const abandoningById = Object.assign({}, state.abandoningById); 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 { claimId } = action.data;
const byId = Object.assign({}, state.byId); const byId = Object.assign({}, state.byId);
const claimsByUri = Object.assign({}, state.claimsByUri); 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 { channelClaim } = action.data;
const byId = Object.assign({}, state.byId); const byId = Object.assign({}, state.byId);
const myChannelClaims = new Set(state.myChannelClaims); const myChannelClaims = new Set(state.myChannelClaims);

View file

@ -7,13 +7,12 @@ const defaultState = {
channelClaimCounts: {}, channelClaimCounts: {},
}; };
reducers[ACTIONS.FETCH_FEATURED_CONTENT_STARTED] = function(state) { reducers[ACTIONS.FETCH_FEATURED_CONTENT_STARTED] = state =>
return Object.assign({}, state, { Object.assign({}, state, {
fetchingFeaturedContent: true, fetchingFeaturedContent: true,
}); });
};
reducers[ACTIONS.FETCH_FEATURED_CONTENT_COMPLETED] = function(state, action) { reducers[ACTIONS.FETCH_FEATURED_CONTENT_COMPLETED] = (state, action) => {
const { uris, success } = action.data; const { uris, success } = action.data;
return Object.assign({}, state, { 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; const { claimIds } = action.data;
return Object.assign({}, state, { 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 { uris } = action.data;
const oldResolving = state.resolvingUris || []; 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 { resolveInfo } = action.data;
const channelClaimCounts = Object.assign({}, state.channelClaimCounts); const channelClaimCounts = Object.assign({}, state.channelClaimCounts);
@ -69,7 +68,7 @@ reducers[ACTIONS.SET_PLAYING_URI] = (state, action) =>
playingUri: action.data.uri, 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 channelClaimCounts = Object.assign({}, state.channelClaimCounts);
const { uri, totalClaims } = action.data; const { uri, totalClaims } = action.data;

View file

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

View file

@ -3,13 +3,12 @@ import * as ACTIONS from 'constants/action_types';
const reducers = {}; const reducers = {};
const defaultState = {}; const defaultState = {};
reducers[ACTIONS.FILE_LIST_STARTED] = function(state) { reducers[ACTIONS.FILE_LIST_STARTED] = state =>
return Object.assign({}, state, { Object.assign({}, state, {
isFetchingFileList: true, isFetchingFileList: true,
}); });
};
reducers[ACTIONS.FILE_LIST_SUCCEEDED] = function(state, action) { reducers[ACTIONS.FILE_LIST_SUCCEEDED] = (state, action) => {
const { fileInfos } = action.data; const { fileInfos } = action.data;
const newByOutpoint = Object.assign({}, state.byOutpoint); const newByOutpoint = Object.assign({}, state.byOutpoint);
const pendingByOutpoint = Object.assign({}, state.pendingByOutpoint); 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 { outpoint } = action.data;
const newFetching = Object.assign({}, state.fetching); 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 { fileInfo, outpoint } = action.data;
const newByOutpoint = Object.assign({}, state.byOutpoint); 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 { uri, outpoint, fileInfo } = action.data;
const newByOutpoint = Object.assign({}, state.byOutpoint); 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 { outpoint, fileInfo } = action.data;
const newByOutpoint = Object.assign({}, state.byOutpoint); 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 { outpoint, fileInfo } = action.data;
const newByOutpoint = Object.assign({}, state.byOutpoint); 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 { outpoint } = action.data;
const newByOutpoint = Object.assign({}, state.byOutpoint); 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 { uri } = action.data;
const newLoading = Object.assign({}, state.urisLoading); 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 { uri } = action.data;
const newLoading = Object.assign({}, state.urisLoading); 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; const { time } = action.data;
if (time) { if (time) {
return Object.assign({}, state, { 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: [], stack: [],
}; };
reducers[ACTIONS.DAEMON_READY] = function(state) { reducers[ACTIONS.DAEMON_READY] = state => {
const { currentPath } = state; const { currentPath } = state;
return Object.assign({}, state, { return Object.assign({}, state, {
@ -22,11 +22,10 @@ reducers[ACTIONS.DAEMON_READY] = function(state) {
}); });
}; };
reducers[ACTIONS.CHANGE_AFTER_AUTH_PATH] = function(state, action) { reducers[ACTIONS.CHANGE_AFTER_AUTH_PATH] = (state, action) =>
return Object.assign({}, state, { Object.assign({}, state, {
pathAfterAuth: action.data.path, pathAfterAuth: action.data.path,
}); });
};
reducers[ACTIONS.HISTORY_NAVIGATE] = (state, action) => { reducers[ACTIONS.HISTORY_NAVIGATE] = (state, action) => {
const { stack, index } = state; const { stack, index } = state;

View file

@ -9,13 +9,12 @@ const defaultState = {
claimErrorsByType: {}, claimErrorsByType: {},
}; };
reducers[ACTIONS.FETCH_REWARDS_STARTED] = function(state) { reducers[ACTIONS.FETCH_REWARDS_STARTED] = state =>
return Object.assign({}, state, { Object.assign({}, state, {
fetching: true, fetching: true,
}); });
};
reducers[ACTIONS.FETCH_REWARDS_COMPLETED] = function(state, action) { reducers[ACTIONS.FETCH_REWARDS_COMPLETED] = (state, action) => {
const { userRewards } = action.data; const { userRewards } = action.data;
const unclaimedRewards = {}; 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; const { reward } = action.data;
return setClaimRewardState(state, reward, true, ''); 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 { reward } = action.data;
const unclaimedRewardsByType = Object.assign({}, state.unclaimedRewardsByType); const unclaimedRewardsByType = Object.assign({}, state.unclaimedRewardsByType);
@ -83,13 +82,13 @@ reducers[ACTIONS.CLAIM_REWARD_SUCCESS] = function(state, action) {
return setClaimRewardState(newState, newReward, false, ''); 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; const { reward, error } = action.data;
return setClaimRewardState(state, reward, false, error ? error.message : ''); 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; const { reward } = action.data;
return setClaimRewardState(state, reward, state.claimPendingByType[reward.reward_type], ''); return setClaimRewardState(state, reward, state.claimPendingByType[reward.reward_type], '');

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,10 +1,9 @@
import { createSelector } from 'reselect';
import { import {
selectClaimsByUri, selectClaimsByUri,
selectIsFetchingClaimListMine, selectIsFetchingClaimListMine,
selectMyClaims, selectMyClaims,
selectMyClaimsOutpoints,
} from 'redux/selectors/claims'; } from 'redux/selectors/claims';
import { createSelector } from 'reselect';
export const selectState = state => state.fileInfo || {}; export const selectState = state => state.fileInfo || {};
@ -52,10 +51,6 @@ export const selectUrisLoading = createSelector(selectState, state => state.uris
export const makeSelectLoadingForUri = uri => export const makeSelectLoadingForUri = uri =>
createSelector(selectUrisLoading, byUri => byUri && byUri[uri]); createSelector(selectUrisLoading, byUri => byUri && byUri[uri]);
export const selectFileInfosPendingPublish = createSelector(selectState, state =>
Object.values(state.pendingByOutpoint || {})
);
export const selectFileInfosDownloaded = createSelector( export const selectFileInfosDownloaded = createSelector(
selectFileInfosByOutpoint, selectFileInfosByOutpoint,
selectMyClaims, 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) => { // export const selectFileInfoForUri = (state, props) => {
// const claims = selectClaimsByUri(state), // const claims = selectClaimsByUri(state),
// claim = claims[props.uri], // claim = claims[props.uri],
@ -94,26 +75,6 @@ export const selectFileInfosPublished = createSelector(
// return outpoint && fileInfos ? fileInfos[outpoint] : undefined; // 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( export const selectDownloadingFileInfos = createSelector(
selectDownloadingByOutpoint, selectDownloadingByOutpoint,
selectFileInfosByOutpoint, 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 { import {
selectPageTitle,
selectCurrentPage, selectCurrentPage,
selectCurrentParams, selectCurrentParams,
selectPageTitle,
} from 'redux/selectors/navigation'; } from 'redux/selectors/navigation';
import { createSelector } from 'reselect';
export const selectState = state => state.search || {}; export const selectState = state => state.search || {};
@ -13,7 +13,7 @@ export const selectSearchQuery = createSelector(
(page, params) => (page === 'search' ? params && params.query : null) (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); export const selectSearchUrisByQuery = createSelector(selectState, state => state.urisByQuery);

View file

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

View file

@ -6,7 +6,7 @@ function rewardMessage(type, amount) {
return { return {
new_developer: __('You earned %s for registering as a new developer.', amount), new_developer: __('You earned %s for registering as a new developer.', amount),
new_user: __('You earned %s LBC new user reward.', 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), new_channel: __('You earned %s LBC for creating a publisher identity.', amount),
first_stream: __('You earned %s LBC for streaming your first video.', 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), 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.TYPE_NEW_DEVELOPER,
]; ];
rewards.claimReward = function(type) { rewards.claimReward = type => {
function requestReward(resolve, reject, params) { function requestReward(resolve, reject, params) {
if (!Lbryio.enabled) { if (!Lbryio.enabled) {
reject(new Error(__('Rewards are not enabled.'))); reject(new Error(__('Rewards are not enabled.')));

View file

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

View file

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

View file

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

View file

@ -1,6 +1,6 @@
// these don't need to be exact // these don't need to be exact
// shapeshift does a more thourough check on validity // Shapeshift does a more thorough check on validity
// just general matches to prevent unneccesary api calls // just general matches to prevent unnecessary api calls
export const coinRegexPatterns = { export const coinRegexPatterns = {
BTC: /^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/, BTC: /^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/,
BCH: /^[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; let previous = 0;
const getNow = () => new Date().getTime(); const getNow = () => new Date().getTime();
const later = function() { const later = () => {
previous = options.leading === false ? 0 : getNow(); previous = options.leading === false ? 0 : getNow();
timeout = null; timeout = null;
result = func.apply(context, args); 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(); const now = getNow();
if (!previous && options.leading === false) previous = now; if (!previous && options.leading === false) previous = now;
@ -53,7 +53,7 @@ export default function throttle(func, wait, options = {}) {
return result; return result;
}; };
throttled.cancel = function() { throttled.cancel = () => {
clearTimeout(timeout); clearTimeout(timeout);
previous = 0; previous = 0;
timeout = null; timeout = null;