Compare commits

..

No commits in common. "master" and "v0.47.0" have entirely different histories.

1113 changed files with 37885 additions and 95458 deletions

View file

@ -1,109 +1,38 @@
####### .env.default #########
# Copy this file to .env to make modifications # Copy this file to .env to make modifications
# Base config # Base config
MATOMO_URL=https://analytics.lbry.com/
MATOMO_ID=4
WEBPACK_WEB_PORT=9090 WEBPACK_WEB_PORT=9090
WEBPACK_ELECTRON_PORT=9091 WEBPACK_ELECTRON_PORT=9091
WEB_SERVER_PORT=1337 WEB_SERVER_PORT=1337
LBRY_WEB_API=https://api.lbry.tv
## APIS
LBRY_WEB_API=https://api.na-backend.odysee.com
LBRY_WEB_STREAMING_API=https://cdn.lbryplayer.xyz LBRY_WEB_STREAMING_API=https://cdn.lbryplayer.xyz
LBRY_WEB_BUFFER_API=https://collector-service.api.lbry.tv/api/v1/events/video WELCOME_VERSION=1.0
COMMENT_SERVER_API=https://comments.odysee.com/api/v2
COMMENT_SERVER_NAME=Odysee
SEARCH_SERVER_API=https://lighthouse.odysee.com/search
SOCKETY_SERVER_API=wss://sockety.odysee.com/ws
THUMBNAIL_CDN_URL=https://image-processor.vanwanet.com/optimize/
WELCOME_VERSION=1.2
# STRIPE
# STRIPE_PUBLIC_KEY='pk_test_NoL1JWL7i1ipfhVId5KfDZgo'
# Analytics
MATOMO_URL=https://analytics.lbry.com/
MATOMO_ID=4
# Custom Site info
DOMAIN=lbry.tv
URL=https://lbry.tv
# UI
SITE_TITLE=lbry.tv
SITE_NAME=LBRY
SITE_DESCRIPTION=Meet LBRY, an open, free, and community-controlled content wonderland.
LOGO_TITLE=lbry.tv
# OG # OG
OG_TITLE_SUFFIX=| lbry.tv OG_TITLE_SUFFIX=| lbry.tv
OG_HOMEPAGE_TITLE=lbry.tv OG_HOMEPAGE_TITLE=lbry.tv
OG_IMAGE_URL= OG_IMAGE_URL=
SITE_CANONICAL_URL=https://lbry.tv SITE_CANONICAL_URL=https://lbry.tv
# UI
## Custom Site info
DOMAIN=lbry.tv
URL=https://lbry.tv
SITE_TITLE=LBRY
SITE_NAME=LBRY
SITE_DESCRIPTION=Meet LBRY, an open, free, and community-controlled content wonderland.
SITE_HELP_EMAIL=help@lbry.com
LOGO_TITLE=LBRY
CLOUD_CONNECT_SITE_NAME=Odysee
## Social media
TWITTER_ACCOUNT=LBRYcom
BRANDED_SITE=odysee
## OLD IMAGE ASSETS
#YRBL_HAPPY_IMG_URL=https://player.odysee.com/api/v3/streams/free/yrbl-happy/7aa50a7e5adaf48691935d55e45d697547392929/839d9a
#YRBL_SAD_IMG_URL=https://player.odysee.com/api/v3/streams/free/yrbl-sad/c2d9649633d974e5ffb503925e1f17d951f1bd0f/f262dd
# LOCALE # LOCALE
DEFAULT_LANGUAGE=en DEFAULT_LANGUAGE=en
## CUSTOM SETTINGS # Custom Content
# Additional settings for below are found in ui/constants/settings and are for
# preventing user settings from applying to custom sites without overwriting them.
# UNSYNCED_SETTINGS='theme dark_mode_times automatic_dark_mode_enabled'
## LINKED CONTENT WHITELIST
KNOWN_APP_DOMAINS=lbry.tv,lbry.lat,odysee.com
## CUSTOM CONTENT
# If the following is true, copy custom/homepage.example.js to custom/homepage.js and modify # If the following is true, copy custom/homepage.example.js to custom/homepage.js and modify
CUSTOM_HOMEPAGE=false CUSTOM_HOMEPAGE=false
# Add up to 2 sidebar links:
# PINNED_URI_1=@Lbrylatam#2/Integracionesporseguridad#4
# PINNED_LABEL_1=LBRY LATAM
# PINNED_URI_2=$/discover?t=lbrytvpaidbeta&fee_amount=>0&claim_type=stream&channel_ids=5af39f818f668d8c00943c9326c5201c4fe3c423,cda9c4e92f19d6fe0764524a2012056e06ca2055,760da3ba3dd85830a843beaaed543a89b7a367e7,40c36948f0da072dcba3e4833e90f71e16de78be,e8f68563d242f6ac9784dcbc41dd86c28a9391d6,7236fc5d2783ea7314d9076ae6c8a250e3992d1a,cf7792c2a37d0d76aaaff84aff0b99a8c791429d,8316ac90764fedf3147799b7b81a6575a9cc398e,8627af93c1a1219150f06b698f4b33e6ed2f1c1e,8972a1bd06de5186e5e89292b05aac8aaa817791,c5b0b17838df2f6c31162f64d55f60f34ae8bfc6,f576d5dba905fc179de880c3fe3eb3281ea74f59,97dd77c93c9603cbb2583f3589f7f5a6c92baa43,f399d873e0c37cf24de9569b5f22bbb30a5c6709,dba870d0620d41b2b9a152c961e0c06cf875ccfc,ca1fd651c9d14bf2e5088bb2aa0146ee7aeb2ae0,50ad846a4b1543b847bf3fdafb7b45f6b2f5844c,e09ff5abe9fb44dd0dd0576894a6db60a6211603,7b6f7517f6b816827d076fa0eaad550aa315a4e7,2068452c41d8da3bd68961335da0072a99258a1a,5da63df97c8255ae94a88940695b8471657dd5a1,3645cf2f5d0bdac0523f945be1c3ff60758f7845,4da85b12244839d6368b9290f1619ff9514ab2a8,4ad942982e43326c7700b1b6443049b3cfd82161,55304f219244abf82f684f759cc0c7769242f3b4,8f42e5b592bb7f7a03f4a94a86a41b1236bb099f,e2a014d885a48f5be2dc6409610996337312facb,c18996ca488753f714d36d4654715927c1d7f9c2,ebc4214424cfa683a7046e1f794fea1e44788d84,06b6d6d6a893fb589ec2ded948f5122856921ed5,07e4546674268fc0222b2ca22d31d0549dc217ee,060940e41973d4f7f16d72a2733138e931c35f41,f8d6eccd887c9cebd36b1d42aa349279b7f5c3ed,68098b8426f967b8d04cc566348b5c128823219e,2bfe6cdb24a21bdc1b76fb7c416edd50e9e85945,1f9bb08bfa2259629f4aaa9ed40f97e9a41b6fa1,2f20148495612946675fe1c8ea99171e4d950b81,bc6938fa1e09e840056c2e831abf9664f397c472,2a6194792beac5130641e932b5ac6e5a99b5ca4f,185ba2bd547a5e4a77d29fe6c1484f47db5e058f,29cc7f6081268eaa5b3f2946e0cd0b952a94812c,49389450b1241f5d8f4c8c4271a3eb56bba33965,ffdc62ac2f7549398d3aca9d2119e83d80d588d5,d7a4d2808074b0c55d6b239f69d90e7a4930f943,d58aa4a0b2f6c2504c3abce8de3f1afb71800acc,77ae23dc7eb8a75609881d4548a79e4935a89d37,f79bce8a60fbece671f6265adc39f6469f3b9b8c,051995fdf0af634e4911704057a551e9392e62b1
# PINNED_LABEL_2=Paid Beta
# Add channels to auto-follow on first run
AUTO_FOLLOW_CHANNELS=lbry://@lbry#3fda836a92faaceedfe398225fb9b2ee2ed1f01a
## FEATURES AND LIMITS
SIMPLE_SITE=false
#BRANDED_SITE
ENABLE_COMMENT_REACTIONS=true
ENABLE_FILE_REACTIONS=true
ENABLE_CREATOR_REACTIONS=true
ENABLE_NO_SOURCE_CLAIMS=false
ENABLE_PREROLL_ADS=false
CHANNEL_STAKED_LEVEL_VIDEO_COMMENTS=4
CHANNEL_STAKED_LEVEL_LIVESTREAM=5
WEB_PUBLISH_SIZE_LIMIT_GB=4
LOADING_BAR_COLOR=#2bbb90
LIGHTHOUSE_DEFAULT_TYPES=audio,video,text,image,application
SHOW_ADS=false
## SIMPLE_SITE REPLACEMENTS
ENABLE_MATURE=true
ENABLE_UI_NOTIFICATIONS=false
#ENABLE_LINK_TO_APP=true
#FORCE_ANALYTICS=true
#ENABLE_ADVANCED_FILTER=true
#ENABLE_PAID_CONTENT=true
#USE_FOOTER=true
#USE_DISCOVER_WHITELIST=false
#ENABLE_WILD_WEST=false
#FULL_SIDE_LINKS=true
#SHOW_TAGS_INTRO=false
# SEARCH TYPES
#VIDEO_ENABLED=true
#AUDIO_ENABLED=true
#POSTS_ENABLED=true
#IMAGES_ENABLED=true
#FILES_ENABLED=true
#MODELS_ENABLED=true
BRANDED_SITE=odysee

View file

@ -1,4 +0,0 @@
node_modules/*
./node_modules/**
**/node_modules/**
web/dist/**

View file

@ -14,11 +14,6 @@
"IS_WEB": true, "IS_WEB": true,
"WEBPACK_PORT": true "WEBPACK_PORT": true
}, },
"settings": {
"react": {
"version": "detect"
}
},
"rules": { "rules": {
"brace-style": 0, "brace-style": 0,
"camelcase": 0, "camelcase": 0,
@ -35,14 +30,13 @@
"object-curly-spacing": 0, "object-curly-spacing": 0,
"one-var": 0, "one-var": 0,
"prefer-promise-reject-errors": 0, "prefer-promise-reject-errors": 0,
"promise/param-names": 0,
"react/jsx-indent": 0, "react/jsx-indent": 0,
"react/jsx-no-comment-textnodes": 0, "react/jsx-no-comment-textnodes": 0,
"react-hooks/exhaustive-deps": "warn", "react-hooks/exhaustive-deps": "warn",
"react-hooks/rules-of-hooks": "error", "react-hooks/rules-of-hooks": "error",
"react/no-unescaped-entities": 0, "react/no-unescaped-entities": 0,
"space-before-function-paren": [ "space-before-function-paren": [
"warn", "error",
{ {
"anonymous": "never", "anonymous": "never",
"named": "never", "named": "never",

View file

@ -1,14 +1,12 @@
[ignore] [ignore]
.*\.typeface\.json .*\.typeface\.json
.*/node_modules/findup/.*
.*/node_modules/react-plastic/.*
.*/node_modules/raf-schd/.*
.*/node_modules/react-beautiful-dnd/.*
.*/node_modules/resolve/test/.*
[include] [include]
[libs] [libs]
./flow-typed
node_modules/lbry-redux/flow-typed/
node_modules/lbryinc/flow-typed/
[lints] [lints]
@ -26,27 +24,13 @@ module.name_mapper='^modal\(.*\)$' -> '<PROJECT_ROOT>/ui/modal\1'
module.name_mapper='^app\(.*\)$' -> '<PROJECT_ROOT>/ui/app\1' module.name_mapper='^app\(.*\)$' -> '<PROJECT_ROOT>/ui/app\1'
module.name_mapper='^native\(.*\)$' -> '<PROJECT_ROOT>/ui/native\1' module.name_mapper='^native\(.*\)$' -> '<PROJECT_ROOT>/ui/native\1'
module.name_mapper='^analytics\(.*\)$' -> '<PROJECT_ROOT>/ui/analytics\1' module.name_mapper='^analytics\(.*\)$' -> '<PROJECT_ROOT>/ui/analytics\1'
module.name_mapper='^recsys\(.*\)$' -> '<PROJECT_ROOT>/extras/recsys\1'
module.name_mapper='^rewards\(.*\)$' -> '<PROJECT_ROOT>/ui/rewards\1' module.name_mapper='^rewards\(.*\)$' -> '<PROJECT_ROOT>/ui/rewards\1'
module.name_mapper='^i18n\(.*\)$' -> '<PROJECT_ROOT>/ui/i18n\1' module.name_mapper='^i18n\(.*\)$' -> '<PROJECT_ROOT>/ui/i18n\1'
module.name_mapper='^effects\(.*\)$' -> '<PROJECT_ROOT>/ui/effects\1' module.name_mapper='^effects\(.*\)$' -> '<PROJECT_ROOT>/ui/effects\1'
module.name_mapper='^comments\(.*\)$' -> '<PROJECT_ROOT>/ui/comments\1'
module.name_mapper='^config\(.*\)$' -> '<PROJECT_ROOT>/config\1' module.name_mapper='^config\(.*\)$' -> '<PROJECT_ROOT>/config\1'
module.name_mapper='^web\/component\(.*\)$' -> '<PROJECT_ROOT>/web/component\1' module.name_mapper='^web\/component\(.*\)$' -> '<PROJECT_ROOT>/web/component\1'
module.name_mapper='^web\/effects\(.*\)$' -> '<PROJECT_ROOT>/web/effects\1' module.name_mapper='^web\/effects\(.*\)$' -> '<PROJECT_ROOT>/web/effects\1'
module.name_mapper='^web\/page\(.*\)$' -> '<PROJECT_ROOT>/web/page\1' module.name_mapper='^web\/page\(.*\)$' -> '<PROJECT_ROOT>/web/page\1'
module.name_mapper='^homepage\(.*\)$' -> '<PROJECT_ROOT>/ui/util/homepage\1' module.name_mapper='^homepage\(.*\)$' -> '<PROJECT_ROOT>/ui/util/homepage\1'
module.name_mapper='^scss\/component\(.*\)$' -> '<PROJECT_ROOT>/ui/scss/component/\1'
esproposal.optional_chaining=enable
; Extensions
module.file_ext=.js
module.file_ext=.jsx
module.file_ext=.json
module.file_ext=.css
module.file_ext=.scss
[strict] [strict]

View file

@ -0,0 +1,72 @@
---
name: "🙋First Timers Only"
about: "Welcome New Contributors!"
title: ''
labels: 'first-timers-only'
assignees: ''
---
<!-- If you are an Open Source Contributor, your first ever PR must have been a difficult experience.-->
<!-- You can use this template to open an issue that will make this experience easier for new contributors.-->
<!-- Make sure not to open a very difficult issue that may be too much for a new contributor.-->
### First Timers Only
This issue is reserved for people who haven't contributed to our codebase before, or never contributed to Open Source. [About First Timers Only](https://www.firsttimersonly.com/).
We know that the process of creating a pull request is the biggest barrier for new contributors. This issue is for you 💝
If you have contributed before, **consider leaving this one for someone new** and looking through our general [help wanted](https://github.com/lbryio/lbry-desktop/labels/help%20wanted) issues. Thanks!
### 🤔 What you will need to know.
Nothing. This issue is meant to welcome you to Open Source :) We are happy to walk you through the process.
### What is to be done?
<!-- Describe the issue here. You can add screenshots/GIFs too.-->
<!-- Suggesting a potential approach to fix can be a good idea in some cases.-->
#### :mag_right: Files To Look Into:
<!-- Looking at large codebases can be overwhelming. Narrowing down the search can help a lot.-->
### 📋 Step by Step
- [ ] :scroll: **Read The CONTRIBUTING File**: We welcome contributions from everyone, the [CONTRIBUTING](https://github.com/lbryio/lbry-desktop/blob/master/CONTRIBUTING.md) will help you get started.
**NOTE**: Useful links:
- - [Git Commands](https://www.siteground.com/tutorials/git/commands/)
- - [Installation Instructions](https://github.com/lbryio/lbry-desktop#running-from-source)
- [ ] **Fork This Repository**: A [fork](https://help.github.com/articles/fork-a-repo/) is a copy of this repository that is stored under your github account. A fork can be used to test out changes separately and then request to add those to the main repository as well!
- [ ] :raising_hand: **Claim this issue**: Comment below. If someone else has claimed it, ask if they've opened a pull request already and if they're stuck -- maybe you can help them solve a problem or move it along!
- [ ] **Create A New Branch**: A [branch](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-branches) is used to test new changes before they are merged with the stable `master` branch. You can create a new branch in the terminal. Make sure to name it descriptively!
- [ ] :memo: **Make The Requested Changes**
- [ ] :white_check_mark: **Commit Your Changes**: A Git [commit](https://help.github.com/en/desktop/contributing-to-projects/committing-and-reviewing-changes-to-your-project#about-commits) is a change that is finalized and recorded in the branch.
- [ ] :twisted_rightwards_arrows: **Start a Pull Request(PR)**. There are two ways how you can start a pull request:
1. If you are familiar with the terminal or would like to learn it, [here is a great tutorial](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github) on how to send a pull request using the terminal.
2. You can also [open a PR from the browser](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork).
- [ ] 🏁 **Done** Ask in comments for a review :)
### Please keep us updated
:speech_balloon: :bell: - We encourage contributors to be respectful to the community and provide an update **within a week** of claiming a first-timers-only issue. We're happy to keep it assigned to you as long as you need it if you update us with a request for more time or help, but if we don't see any activity a week after you claim it we may reassign it to give someone else a chance. Thank you in advance!
If this happens to you, don't sweat it! Grab another open issue.
### Is someone else already working on this?
🔗- We encourage contributors to link to the original issue in their pull request so all users can easily see if someone's already started on it.
👥- **If someone seems stuck, offer them some help!** Otherwise, [take a look at some other issues you can help with](https://github.com/lbryio/lbry-desktop/labels/help%20wanted). Thanks!
### 🤔❓ Questions?
Leave a comment below!

View file

@ -1,26 +1,14 @@
## Fixes
Issue Number:
<!-- Tip:
- Add keywords to directly close the Issue when the PR is merged.
- Skip the keyword if the Issue contains multiple items.
- https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword
-->
## What is the current behavior?
## What is the new behavior?
## Other information
<!-- If this PR contains a breaking change, please describe the impact and solution strategy for existing applications below. -->
## PR Checklist ## PR Checklist
<!-- For the checkbox formatting to work properly, make sure there are no spaces on either side of the "x" --> <!-- For the checkbox formatting to work properly, make sure there are no spaces on either side of the "x" -->
<details><summary>Toggle...</summary> Please check all that apply to this PR using "x":
- [ ] I have checked that this PR is not a duplicate of an existing PR (open, closed or merged)
- [ ] I have checked that this PR does not introduce a breaking change
- [ ] This PR introduces breaking changes and I have provided a detailed explanation below
## PR Type
What kind of change does this PR introduce? What kind of change does this PR introduce?
@ -31,11 +19,14 @@ What kind of change does this PR introduce?
- [ ] Documentation changes - [ ] Documentation changes
- [ ] Other - Please describe: - [ ] Other - Please describe:
Please check all that apply to this PR using "x": ## Fixes
- [ ] I have checked that this PR is not a duplicate of an existing PR (open, closed or merged) Issue Number:
- [ ] I added a line describing my change to CHANGELOG.md
- [ ] I have checked that this PR does not introduce a breaking change
- [ ] This PR introduces breaking changes and I have provided a detailed explanation below
</details> ## What is the current behavior?
## What is the new behavior?
## Other information
<!-- If this PR contains a breaking change, please describe the impact and solution strategy for existing applications below. -->

View file

@ -1,127 +0,0 @@
name: Node.js CI
on:
push:
branches: [master]
pull_request:
branches: [master]
jobs:
lint:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: corepack enable
- run: yarn
- run: yarn lint
build:
needs: ['lint']
name: 'build'
strategy:
matrix:
node-version: [16.x]
os:
- ubuntu-latest
- macos-latest
- windows-latest
runs-on: ${{ matrix.os }}
continue-on-error: true
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- run: corepack enable
- uses: maxim-lobanov/setup-xcode@v1
if: startsWith(runner.os, 'mac')
with:
xcode-version: '13.1.0'
# This is gonna be hacky.
# Github made us upgrade xcode, which would force an upgrade of electron-builder to fix mac.
# But there were bugs with copyfiles / extraFiles that kept seeing duplicates erroring on ln.
# A flag USE_HARD_LINKS=false in electron-builder.json was suggested in comments, but that broke windows builds.
# So for now we'll install python2 on mac and make sure it can find it.
# Remove this after successfully upgrading electron-builder.
# HACK part 1
- uses: Homebrew/actions/setup-homebrew@master
if: startsWith(runner.os, 'mac')
# HACK part 2
- name: Install Python2
if: startsWith(runner.os, 'mac')
run: |
/bin/bash -c "$(curl -fsSL https://github.com/alfredapp/dependency-scripts/raw/main/scripts/install-python2.sh)"
echo "PYTHON_PATH=/usr/local/bin/python" >> $GITHUB_ENV
- name: Download blockchain headers
run: |
mkdir -p ./static/daemon
curl -o ./static/daemon/headers https://headers.lbry.io/blockchain_headers_latest
ls ./static/daemon
- name: Build
run: |
yarn dlx cross-env
yarn --network-timeout 600000
yarn build
env:
GH_TOKEN: ${{ secrets.GH_TOKEN_NEW }}
NOTARIZATION_USERNAME: ${{ secrets.NOTARIZATION_USERNAME }}
NOTARIZATION_PASSWORD: ${{ secrets.NOTARIZATION_PASSWORD }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WIN_CSC_KEY_PASSWORD }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
WIN_CSC_LINK: https://raw.githubusercontent.com/lbryio/lbry-desktop/master/build/cert2023.pfx
CSC_LINK: https://s3.amazonaws.com/files.lbry.io/cert/osx-csc-2021-2022.p12
# UI
MATOMO_URL: https://analytics.lbry.com/
MATOMO_ID: 4
WELCOME_VERSION: 1.0
DOMAIN: lbry.tv
URL: https://lbry.tv
SHARE_DOMAIN_URL: https://open.lbry.com
SITE_TITLE: lbry.tv
SITE_NAME: lbry.tv
SHOW_ADS: false
ENABLE_COMMENT_REACTIONS: true
ENABLE_NO_SOURCE_CLAIMS: false
DEFAULT_LANGUAGE: en
KNOWN_APP_DOMAINS: lbry.tv,lbry.lat,odysee.com
CHANNEL_STAKED_LEVEL_VIDEO_COMMENTS: 0
- uses: actions/upload-artifact@v2.2.4
if: |
startsWith(runner.os, 'linux')
with:
name: Linux
path: ./dist/electron/*.*
- uses: actions/upload-artifact@v2.2.4
if: |
startsWith(runner.os, 'mac')
with:
name: macOS
path: ./dist/electron/*.*
- uses: actions/upload-artifact@v2.2.4
if: |
startsWith(runner.os, 'windows')
with:
name: Windows
path: ./dist/electron/*.*
- uses: jakejarvis/s3-sync-action@master
if: |
startsWith(runner.os, 'linux')
with:
args: --acl public-read --follow-symlinks --exclude '*' --include '*.deb' --include '*.AppImage' --include '*.dmg'
env:
AWS_S3_BUCKET: ${{ secrets.ARTIFACTS_BUCKET }}
AWS_ACCESS_KEY_ID: ${{ secrets.ARTIFACTS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.ARTIFACTS_SECRET }}
AWS_REGION: 'us-east-1'
SOURCE_DIR: 'dist/electron'
DEST_DIR: 'app/release'

18
.gitignore vendored
View file

@ -22,23 +22,5 @@ package-lock.json
/web/.env /web/.env
/web/.env.defaults /web/.env.defaults
/custom/* /custom/*
/custom/homepages/*
/custom/content/*
!/custom/content/default.json
!/custom/content/test.json
!/custom/homepages/.gitkeep
!/custom/homepages
!/custom/content
!/custom/homepage.example.js !/custom/homepage.example.js
!/custom/robots.disallowall
!/custom/robots.allowall
.env .env
!.env.ody
.env.desktop
.env.lbrytv
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/sdks
!.yarn/versions
!.yarn/releases

View file

@ -1,7 +1,9 @@
{ {
"linters": { "linters": {
"ui/**/*.{js,jsx,scss,json}": ["prettier --write", "git add"], "ui/**/*.{js,jsx,scss,json}": ["prettier --write", "git add"],
"ui/**/*.{js,jsx}": ["eslint", "flow focus-check --color always", "git add"] "web/**/*.{js,jsx,scss,json}": ["prettier --write", "git add"],
"ui/**/*.{js,jsx}": ["eslint", "flow focus-check --color always", "git add"],
"web/**/*.{js,jsx,scss}": ["eslint", "git add"]
}, },
"ignore": ["node_modules", "dist/**/*", "package-lock.json"] "ignore": ["node_modules", "web/dist/**/*", "dist/**/*", "package-lock.json"]
} }

78
.travis.yml Normal file
View file

@ -0,0 +1,78 @@
matrix:
include:
- os: osx
env: TARGET=mac
osx_image: xcode10
language: node_js
node_js: '10'
- os: linux
env: TARGET=windows
services: docker
language: node_js
node_js: '10'
- os: linux
env: TARGET=linux
language: node_js
node_js: '10'
cache: false
before_install:
- |
unset TRAVIS_COMMIT_MESSAGE;
if [ "$TRAVIS_OS_NAME" == "osx" ]; then
mkdir -p /tmp/git-lfs && curl -L https://github.com/github/git-lfs/releases/download/v2.3.1/git-lfs-$([ "$TRAVIS_OS_NAME" == "linux" ] && echo "linux" || echo "darwin")-amd64-2.3.1.tar.gz | tar -xz -C /tmp/git-lfs --strip-components 1
export PATH="/tmp/git-lfs:$PATH"
else
sudo apt-get -qq update
sudo apt-get install --no-install-recommends -y gcc-multilib g++-multilib wget
curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.6.0
export PATH="$HOME/.yarn/bin:$PATH"
fi
before_script:
- git lfs pull
- if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then unset WIN_CSC_LINK; unset CSC_LINK; fi
- wget https://headers.lbry.io/blockchain_headers_latest -O $TRAVIS_BUILD_DIR/static/daemon/headers
script:
- travis_wait 30
- |
if [ "$TARGET" == "windows" ]; then
# Remove any special characters before adding to our list of ENVs
# https://github.com/electron-userland/electron-builder/issues/2450#issuecomment-421788155
ENVS=`env | grep -iE '(DEBUG|NODE_|ELECTRON_|YARN_|NPM_|CI|CIRCLE|TRAVIS|APPVEYOR_|CSC_|_TOKEN|_KEY|AWS_|STRIP|BUILD_|TARGET)([A-Z]|_)*=' | sed -n '/^[^\t]/s/=.*//p' | sed '/^$/d' | sed 's/^/-e /g' | tr '\n' ' '`
docker run $ENVS --rm \
-v ${PWD}:/project \
electronuserland/builder:wine \
/bin/bash -c "env | grep -v '\r' | grep -iE 'DEBUG|TARGET|NODE_|ELECTRON_|YARN_|NPM_|CI|CIRCLE|TRAVIS|APPVEYOR_|CSC_|GH_|GITHUB_|BT_|AWS_|STRIP|BUILD_|WIN_' && yarn --link-duplicates --pure-lockfile && yarn build --win --publish onTag";
fi
- if [ "$TARGET" == "mac" ]; then yarn build --publish onTag; fi
- if [ "$TARGET" == "linux" ]; then yarn --link-duplicates --pure-lockfile && yarn
build --linux --publish onTag; fi
addons:
artifacts:
working_dir: dist
paths:
- $(git ls-files -o dist/{*.dmg,*.exe,*.deb,*.AppImage} | tr "\n" ":")
target_paths:
- '/app/build-${TRAVIS_BUILD_NUMBER}_commit-${TRAVIS_COMMIT:0:7}$([ ! -z ${TRAVIS_TAG}
] && echo _tag-${TRAVIS_TAG})'
env:
global:
- ARTIFACTS_REGION=us-east-1
- ARTIFACTS_BUCKET=build.lbry.io
- DANGER_GITHUB_API_TOKEN=f8b612049cbda985c251e18ae36c7a2b9c6c92df
- ARTIFACTS_DEBUG=1
- WIN_CSC_LINK=https://s3.amazonaws.com/files.lbry.io/cert/win-csc-2019-2020-08.p12
- CSC_LINK=https://s3.amazonaws.com/files.lbry.io/cert/osx-cert.p12
#AWS_SECRET_ACCESS_KEY
- secure: aF0dZ1ub0r6X5SaTH7YYRpV8cl2gkU2tA7y2tkvGZQL0+Vv2FvkhwaXREa9IM9djm7r3W7I8vEekk6Skn300kIBwQ04COxgu+Oeqjsc2a529wmF9AN5EwKExKkVQFIjMjpMBmd/fE05PD29C0vULdtIMX8uhDe5zFE+tGD1QmwKVZ+55oCxJmt7vsQwQr46s/BsPODrZhvSUPWTUG4HSsc5DJwe/XYY+35vWQsnpCkBVabAZg+lnOpqLioRNzTsBX1oSkqTX9uvOC6fN+NlpbJWetzXsBciFcMHQCx9UHxu+Ibn3W3V8FVYRuFLAinhIg+JoVQHU8mH60ggjizATpgx12riP9e/Uwho5x5bgIYmnIBf4AHqupPv40iC/THMvsS08hCQHicu+/WiAvvN8wNQ1sqpj0+RTreB+4/qdRdwTs70t4jNV83cP+nAuekMCC7uYQ08GLmFNaa+x8NgDZa3JQv7EmXU0ALWVPeJ9UEV+dhEs5iI+7bLEhTUOuvBBWo17F4pst3pfS1j7md1x4wKFRWyP7IFmjK+L1KiAH94c/LIz2Nlp5yCDfbcwU5W/wCHYAxu5c8qnAlkxeub0GpxtZ66j6oOehk1lsHYxaZcBxyn7XhM1A6Mjxgy9OG0WGyYun8iH3aa4WcY0BO21l7je75s7l0qNSRS80st8kUE=
#AWS_ACCESS_KEY_ID
- secure: HHlUGaEjk0qsW9qb/93jdFhJypiDuJ0e6Z4UClW3ciMfdZ22Ur4D0oL0mgVguigTjdkf1Yy4Z1kWtWvtvQ8DBI+mdOl5GTMpX5nMattcuK/4gFXmqz30S3c07IPkilGKkAT64JqRGT7AvQTaV/tXqNNN9BbWCTAJSnhfzYJFvZQjnSmAAQvbqzMWEEein53244fWbDio462T7vzfbEHJD1pVUSrAMrKDgFSrIKJf4GQHceUk+fqWBjAsWN8USboo88JSLXn5qhD3ZczsgtZ2gi2KTvdfrHfZ5SpOzwc2AGr8U1laQzzW8c8xsPJPmM/3dCri/clcXassa4t/+2INYTPhKbmaD9hvjFRRMpoNjMCmPRJOmojxit8tqzCrw4nSGdr3/Bt0JjpLq90mfyL83YYU4gJXF97KzTl0lCy/DFXjBiHA9riRJVaXn3CI6L0i1RLCJI+C/rT9ErFB767t0aQtXlIWSZQ+IqWaoUdp57t0e2EtbRcjK7IYIXLNgZSbR0bY/TS005D/xZkexCpAtcV9F6iqDYP3HbGyiQqC75se0MqpNrZAx1a7WsSnumOcmtuU6LRriwjM0GXEACfGLCp4w4LBzKPwrOOg4QOhewHKgxpUff8KMlfTZ+Z8B7NBNjKSPbxiMedmpsiWpPRy6VeyvwNz32PUpjdsF3tIYMk=
#GH_TOKEN
- secure: g0gSiOmCKFzxLFV//oogmex+MkumCcGJ8CtvLzojAw1qhI1P6dGRH06yuCgBNW4PQGbbrYR2tntGEeq66LZ1bU7BJ2bdkv5lBi15JPH4POpA3OSc88YebitKIyIBI2WHA3VNCkF1JEFvrfTUX+4PjP+9h6vZ9tdfzezNTpc70SrGkWMhaoe+F8u6jCL63VBpnncf0foz3WlqUFNoIdinQLYxI6S2QmHz1p1HToC+MOosBgWDufX2+6B/Efq7JDFZgx/zItFzR1OCafKx8rScUd4woAqK+8mt862IzzEbztTv6IPSdhx/hsbO7rHMcjDFk/MuDeC9TaaXqMvvKmLuvWKH+dy4BvIiJJ/WuDfzumrw+8BvTbcnXFXkEXTlkQnZD0otBZsmI4/OPH5vIGUmVIwBFXjPBcNq/Xwig8hRCxrdioEZPO4C17fMCdOn3gAx+GcMddfAIKnrlG/XK2jL8kCylL7++OEkedzYiHWA7p9eaJDjxYmIxKcfYTyF4VIMYS/wAC5W1zi1AjM5BiUhBGp/Vd+f3UUTOsqWOmS4A8cqhK/rbx4xgdwUuv2DoljeQtWd2xdZFr10L2ErmEIdfdtZBVyauaGOhhXUqpSAB5PAwSRNNWkZFCJXmqMHemih4jlfN/KS+rujh5dbyXz81BPjyxrDAziO4Q19rz7hmVI=
#ARTIFACTS_KEY
- secure: giJRkODdgvMpaLh58c3UKTy6j3IjUVQ2wVvLmECN8caVLuBnEp3U56rUT7sQPzeMoaF9goOLKXD1o+qKdGsLz2Dedi/3vFr3IXTgpYwO03eMkylYUDUwKXV1Fv14ME+wknw/Fshmty1XAc/RwlTaCks9usIRT9OE75HvV8ILVfLdSwwRR/TKFEdEDjAY/3ROVFsleDKBIe1AG/AHeN37Mwqfo39zpN5Zidm3HUegujsMsHtPQtUUCvCxiaiBtqIz79WQuiXjelLWrV53k+ulpptz4T6M8FO5jxBCL5HQMbYy4J8BhukAPHUcTVzarsDe1XEmP6xtClcAydMiHmzUBh9VbvRF8AKwGGO41g6OasgMaJ/eUlACGWTIS2AsqgkXCEgWRtswwI4edLgT/F74fKxGV82zRYLcmjWceGc9H6m2YfIIQZ2PNsDloFgTWa63oZ1sT20fWmNohD3IgVK21rjrCnMbp5/RfBdihR1+GsS8wMA15nhUW3SHfSHKV0009FZdDjxgmigjjmFt1/SM8YGZDijQc7DoUsrScR9n2JR+KVqEED3BYOviw0M0blb6b2dqK0A3+N5C1RGt8Uu8AAuAzdPDviL38m4n1UOdI1Tij1boSfr1USvfipPfQXrqSWHIY3RITMmjPREmcdj2dbhjoUEFaFfp/b3sJxUI3I4=
#ARTIFACTS_SECRET
- secure: RtPES82DBisaCkernDm10u8mzNc8K4EyyTJUCopcFFJHfFpleuJOoXNFZGXixjHXWTxqc5+IjLThBZFG6krTpD4ocftfkBNeVDXL91JsGRW6X4VBpKaJp1UJZZiYjpPZRJDwVuIb570WcX3xXmsvAwwdODkeXgBZoOw+LZIz4HV3OACi5iaWCx4+2WvQiaz/6mRkLlw9iY1m9gCGQXXUSWyY+bGWsOdGWpfp/VfzpX7mBJbcIYjvRYSnBtVUFkEzxHlaR/CFc7Jio45EUImhBKqKV8QGAiv9AowcWqQn8y2SuW8J66bkaZGbGpyEdsRQDJKUU5naRIFJ/002e4XjI2moBfSHHnFTMH/AawA4b+gCjein8jDiRugJw8AqC4KpG660SFJwC4A8IxfPcXWcykahrL6kJhs+NFbyxQEJIMUqP6UAAiPAuUr/Whq1F8x5b39pEibRZ3Vv0Bix/lUj4cWZYRBL1ckoUgSCeG7llkAdnxLuQKfDvTw+HFcWGq7yg/ye7H7CwpBJkSSOlnQ61nuucC82hVqBGoPGbFW3vjS0BqesLcaO7kwW/fdBnIxwLiWWHDWol0aLfQLEzjUzpS+tA1YYKvrG+yNMEeYtHEK9ZcltQD52uEtjK26DIY8tQU8PxMlXha6/XTOlt8MIcl8IPULmVWKT9k8LPTFQiXc=
#CSC_KEY_PASSWORD
- secure: TDKeF7/WGwR+di+JhNp29x7NPzVMO/q+72n58zB5goI1NUKaeKgjeWbfVYx9f+4G9a9/pdDVUfORt8i6OW8ZhhqYS4E8G5F56q+oX3nrjNqM8NqoK09ehZS/wdYGbenG9oTfXYenDdwusZV4Fq0BRRLjqAIXPQCKg3//MKseh/1fHDGVGXpYUimHRSCkwspbbfB/9Qw9KEBjweeXiAwB+5F+E7fPlVtqsIvtkkED1hKe0Z8HdECC6JTZ0ZHPDDFGV3aondXQDgUlfchnZ6HDdNDO5y/hPEj0laiZQ7BssenJ8Z7qjHc5O9AKXfG+6WFICHvtgjQ0+x6rk6gpvJcyI1x2+Kck/s0EcSkFY+Yz81BxdehIeKPn9U8LpGaFbtxsp01661yeaIpAqT/PqFsdj/kFXFT6gwZlGGPMBm2WgQR4A61qkOO1jokqz/z6CnY+MNeE8E1Fh4bFoZ0JwUJFJugoyDahHpVlLw5lZaSipJO6RZ1xjoZ3XGmxvtkM4dQ16xQ++Q8EgD878uCWn2jZ5YTQdKANfXYTKSiQfoEjLeX0T6I6GSdim4ZURjcolmGNMH/3jhISOXj+e2UkLc6jwO4Ek084o6ciJ2JjqEhXvXOCeRJ2I9cf2dEk7CvtbitiDly6XATo2FP4hqNdcNNWyj/jFvuTwFT6hzBqLk1BCBc=
#WIN_CSC_KEY_PASSWORD
- secure: S4kxB3fgQ5WtxsHopOtOJHd+yZg635r/XEf2qfoOvehhubhQVZ6qnlIuWGeGrZkV8SGOWi5buple2q7w9iw3Ac3InYG2P5ZUNJ9ANve0s1GgpBf9qAEb0IlqRbvZfM0X7KZJZGMHasEprjdCxaihlKkEudtaSvoF/8Og7OxtygQmLOu2amN/p0FYr6HY5KLFgpbGBMXloODsvsrGzcLWQyhszdRlaiHEDGmIklDjEVRW+Y5KVxdxpKu3t6R/enFpsBFF3mhrXx8yxSscjxbf4QsatMrD6ZMLU0J5xcEFroTU4JINHnz3kPEdFvs7jfCOZD6dDu5SKDBchvvfS1wllY65r8o3PySu/opCaZb7XS2j5IK9ZJSqjoW+d0519Awb/oyhmYW8GGl0XQZfCkdRrSoOR1Ww+/wZ8K8b+QHUSm99acex/ypCjJio7C7YrUfRGbCplhGHIFnhn4pJZFSQ8YVEovdSLDk3/NnriA5zJffMM+881rrXe5AjD9fya6Gm1Ha/g0HRcCtA+ocdBENkfy6wXugkE+IDyoO9NhnT+VfuqlToEt0JHD5eLgEp/YTIUD1N1CUMQD2Z/mOSdVDnMENPy+S9i8Sz4MK7wH0iTeyWJDLYbqRJBHkd66m77Tl7RnMIzL0so7JVm4AwA/VIQn2qxgPESJKktgRjI0vKZTo=

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

View file

View file

View file

View file

View file

View file

View file

View file

View file

View file

View file

View file

View file

View file

View file

View file

View file

View file

View file

View file

@ -1,7 +0,0 @@
nodeLinker: node-modules
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-version.cjs
spec: "@yarnpkg/plugin-version"
yarnPath: .yarn/releases/yarn-3.2.0.cjs

View file

@ -1,539 +1,10 @@
# Changelog # Changelog
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [0.53.9] - [2023-2-8] ## [Unreleased on desktop]
### Changed
- Updated lbrynet to [0.113.0](https://github.com/lbryio/lbry-sdk/releases/tag/v0.113.0)
## [0.53.8] - [2022-11-17]
### Fixed
- Selecting a large file in publish no longer crashes ([#7736](https://github.com/lbryio/lbry-desktop/pull/7736))
- Unfollowing unpublished channels ([#7737](https://github.com/lbryio/lbry-desktop/pull/7737))
### Changed
- Updated xcode to 13.1 and hacked a fix for release ([#7736](https://github.com/lbryio/lbry-desktop/pull/7736))
## [0.53.7] - [2022-11-10]
### Added
- 'Collections' to txo filter _community pr!_ ([#7711](https://github.com/lbryio/lbry-desktop/pull/7711))
- Swap comment servers _community pr!_ ([#7670](https://github.com/lbryio/lbry-desktop/pull/7670))
### Fixed
- Thumbnails no longer disable publish ([#7714](https://github.com/lbryio/lbry-desktop/pull/7714))
- Publishing posts were empty ([#7715](https://github.com/lbryio/lbry-desktop/pull/7715))
- Minor layout fixes _community pr!_ ([#7709](https://github.com/lbryio/lbry-desktop/pull/7709))
- Comment section buttons layout ([#7716](https://github.com/lbryio/lbry-desktop/pull/7716))
### Changed
- Removed watchman and its errors ([#7710](https://github.com/lbryio/lbry-desktop/pull/7710))
- Updated lbrynet to [0.112.0](https://github.com/lbryio/lbry-sdk/releases/tag/v0.112.0)
## [0.53.6] - [2022-10-21]
### Fixed
- Make thumbnails optional ([#7690](https://github.com/lbryio/lbry-desktop/pull/7690))
- Show downloads newest first ([#7684](https://github.com/lbryio/lbry-desktop/pull/7684))
- Only allow images in image uploader ([#7672](https://github.com/lbryio/lbry-desktop/pull/7672))
- Fixed bug with csv exports ([#7697](https://github.com/lbryio/lbry-desktop/pull/7697))
- Fixed various upload bugs including transcoding ([#7688](https://github.com/lbryio/lbry-desktop/pull/7688))
- Fallback for files with no extension ([#7704](https://github.com/lbryio/lbry-desktop/pull/7704))
### Changed
- Upgraded Electron to v17.2.0 ([#7703](https://github.com/lbryio/lbry-desktop/pull/7703))
- Upgraded Electron to v17.0.0 ([#7691](https://github.com/lbryio/lbry-desktop/pull/7691))
- Updated lbrynet to [0.111.0](https://github.com/lbryio/lbry-sdk/releases/tag/v0.111.0)
## [0.53.5] - [2022-08-26]
### Added
- Checkbox to disable background wallpaper ([#7630](https://github.com/lbryio/lbry-desktop/pull/7630))
- Handle content blocking from hub ([#7665](https://github.com/lbryio/lbry-desktop/pull/7665))
### Fixed
- Better handle decimals liquidating supports ([#7648](https://github.com/lbryio/lbry-desktop/pull/7648))
- Better handle cover uploads ([#7647](https://github.com/lbryio/lbry-desktop/pull/7647))
- Use default path when first choosing file on windows ([#7625](https://github.com/lbryio/lbry-desktop/pull/7625))
- Emoji button hover ([#7620](https://github.com/lbryio/lbry-desktop/pull/7620))
- Prevent infinite retries on thumbs ([#7618](https://github.com/lbryio/lbry-desktop/pull/7618))
- Double splash/error on app startup ([#7615](https://github.com/lbryio/lbry-desktop/pull/7615))
- App updates are now more coherent, also debs work. ([#7502](https://github.com/lbryio/lbry-desktop/pull/7502))
- Better handle many channels moderation calls at startup ([#7674](https://github.com/lbryio/lbry-desktop/pull/7674))
- Fix mobile floating viewer position ([#7677](https://github.com/lbryio/lbry-desktop/pull/7677))
### Changed
- Upgraded Electron to v15.5.5 ([#7614](https://github.com/lbryio/lbry-desktop/pull/7614))
- Upgraded to lbrynet v0.110.0 ([#7680](https://github.com/lbryio/lbry-desktop/pull/7680))
## [0.53.4] - [2022-06-10]
### Added
- Add top in language category for non-english on homepage ([#7585](https://github.com/lbryio/lbry-desktop/pull/7585))
- Auto hosting in settings and hosting first run page ([#7598](https://github.com/lbryio/lbry-desktop/pull/7598))
### Changed
- Updated lbry-sdk to [0.107.2](https://github.com/lbryio/lbry-sdk/releases/tag/v0.107.2)
### Fixed
- Better handle empty collections ([#7571](https://github.com/lbryio/lbry-desktop/pull/7571))
- Better handle thumbnails in uploads/collections ([#7574](https://github.com/lbryio/lbry-desktop/pull/7574))
- Work towards supporting collections of any claim type ([#7578](https://github.com/lbryio/lbry-desktop/pull/7578))
- Improve handling of downed custom servers on startup ([#7593](https://github.com/lbryio/lbry-desktop/pull/7593))
- Hide watch progress in related if being played ([#7606](https://github.com/lbryio/lbry-desktop/pull/7606))
- IPC disk space calls wait for daemon ready; refresh on vis. component load ([#7610](https://github.com/lbryio/lbry-desktop/pull/7610))
## [0.53.3] - [2022-04-27]
### Fixed
- Reverted lbry.tv changes that broke production login ([#7569](https://github.com/lbryio/lbry-desktop/pull/7569))
- Reverted lbry.tv changes that broke login ([#7570](https://github.com/lbryio/lbry-desktop/pull/7570))
## [0.53.2] - [2022-04-26]
### Changed
- Upgraded Yarn to Berry branch ([#7530](https://github.com/lbryio/lbry-desktop/pull/7530))
- Removed some lbrytv references ([#7560](https://github.com/lbryio/lbry-desktop/pull/7560))
- Removed some lbrytv player references ([#7552](https://github.com/lbryio/lbry-desktop/pull/7552))
### Fixed
- Repost style issues ([#7559](https://github.com/lbryio/lbry-desktop/pull/7559))
- Disappearing sidebar thumbs ([#7556](https://github.com/lbryio/lbry-desktop/pull/7556))
- Restore tags sidebar link ([#7555](https://github.com/lbryio/lbry-desktop/pull/7555))
- Playlist view link no longer crashes ([#7552](https://github.com/lbryio/lbry-desktop/pull/7552))
## [0.53.1] - [2022-04-22]
### Added
- Uploads: show placeholder when loading page _community pr!_ ([#7531](https://github.com/lbryio/lbry-desktop/pull/7531))
- Sidebar channel search _styles pr_ ([#7542](https://github.com/lbryio/lbry-desktop/pull/7542))
- Viewed content progress indicator on thumbnail part 1 ([#7541](https://github.com/lbryio/lbry-desktop/pull/7541))
- Viewed content progress indicator on thumbnail part 2 ([#7547](https://github.com/lbryio/lbry-desktop/pull/7547))
- Ability to search through publishes ([#7535](https://github.com/lbryio/lbry-desktop/pull/7535))
### Changed
- Large styles revamp following odysee _styles pr_ ([#7542](https://github.com/lbryio/lbry-desktop/pull/7542))
### Fixed
- Fix bad rerender on homepage _styles pr_ ([#7542](https://github.com/lbryio/lbry-desktop/pull/7542))
- Fix post-editor preview mode _community pr!_ ([#7532](https://github.com/lbryio/lbry-desktop/pull/7532))
- Fix send-tip default tab ([#7533](https://github.com/lbryio/lbry-desktop/pull/7533))
## [0.52.6] - [2022-04-04]
### Added
- Discover page medium duration filter ([#7506](https://github.com/lbryio/lbry-desktop/pull/7506))
- Keep last used collection for Add To ([#7491](https://github.com/lbryio/lbry-desktop/pull/7491))
- Disk space functionality on mac / windows ([#7500](https://github.com/lbryio/lbry-desktop/pull/7500))
- Enable renaming private collections ([#7519](https://github.com/lbryio/lbry-desktop/pull/7519))
### Changed
- Some upgrade modal improvements ([#7488](https://github.com/lbryio/lbry-desktop/pull/7488))
- Updated lbry-sdk to [0.107.1](https://github.com/lbryio/lbry-sdk/releases/tag/v0.107.1)
- New YRBL!; facelift for first run ([#7527](https://github.com/lbryio/lbry-desktop/pull/7527))
### Fixed
- Failed comment count increment ([#7510](https://github.com/lbryio/lbry-desktop/pull/7510))
- App crash playing media on older windows versions by updating electron ([#7509](https://github.com/lbryio/lbry-desktop/pull/7509))
- Local build failures on mac ([#7497](https://github.com/lbryio/lbry-desktop/pull/7497))
- Language change now rerenders whole app ([#7504](https://github.com/lbryio/lbry-desktop/pull/7504))
- Mac notarization ([#7518](https://github.com/lbryio/lbry-desktop/pull/7518))
- Prevent crash when deleting last comment reply ([#7526](https://github.com/lbryio/lbry-desktop/pull/7526))
## [0.52.5] - [2022-02-25]
### Fixed
- New data hosting ux ([#7493](https://github.com/lbryio/lbry-desktop/pull/7493))
- Fix markdown guide button ([#7485](https://github.com/lbryio/lbry-desktop/pull/7485))
## [0.52.4] - [2022-02-15]
### Fixed
- Fixed active channel ([#7481](https://github.com/lbryio/lbry-desktop/pull/7481))
- Remove extra search button in header ([#7482](https://github.com/lbryio/lbry-desktop/pull/7482))
## [0.52.3] - [2022-02-15]
### Fixed
- Fixed comment editing and pinning ([#7476](https://github.com/lbryio/lbry-desktop/pull/7476))
- Fixed mac header ([#7479](https://github.com/lbryio/lbry-desktop/pull/7479))
- Fixed markdown display and lbry url embedding ([#7474](https://github.com/lbryio/lbry-desktop/pull/7474))
## [0.52.2] - [2022-02-11]
### Added
- Reenabled generating thumbs from video ([#7384](https://github.com/lbryio/lbry-desktop/pull/7409))
- Brought in playlist drag and drop playlist reordering _odysee team!_ ([#7442](https://github.com/lbryio/lbry-desktop/pull/7442))
- Added duration overlays to ClaimPreview component ([#7420](https://github.com/lbryio/lbry-desktop/pull/7420))
- Some Horizontal Scroll groundwork from _odysee team!_
- Comment Emotes and Stickers and Mentions refactors from _odysee team!_ ([#7435](https://github.com/lbryio/lbry-desktop/pull/7435))
- Seek forward and back from _odysee team!_ () ([#7460](https://github.com/lbryio/lbry-desktop/pull/7460))
### Changed
- Upgraded Electron to v15 ([#7384](https://github.com/lbryio/lbry-desktop/pull/7384))
- Performance improvements in some selectors ([#7370](https://github.com/lbryio/lbry-desktop/pull/7370))
- More Header refactoring from _odysee team!_ ([#7441](https://github.com/lbryio/lbry-desktop/pull/7441))
- Header refactoring from _odysee team!_ ([#7440](https://github.com/lbryio/lbry-desktop/pull/7440))
- Data hosting ui _incomplete_ ([#7438](https://github.com/lbryio/lbry-desktop/pull/7438))
- Updated c: control tags from _odysee team!_ ([#7433](https://github.com/lbryio/lbry-desktop/pull/7433))
- Nav keycodes (alt+left) no longer navigate while textarea is focused ([#7458](https://github.com/lbryio/lbry-desktop/pull/7458))
- Improved comment-server selection ui/ux ([#7455](https://github.com/lbryio/lbry-desktop/pull/7455))
- Improved Data Hosting settings ([#7563](https://github.com/lbryio/lbry-desktop/pull/7563))
### Fixed
- Several fallout bugs from recent changes
## [0.52.1]
### Skipped patch version
## [0.52.0] - [2021-12-31]
### Compatibility
- Mac <= 10.13 (High Sierra) and Ubuntu <= 16 (Xenial) are no longer supported. If you upgrade, you will need to manually build and install your own lbrynet SDK
### Added
- Direct replying to notifications _community pr!_ ([#6935](https://github.com/lbryio/lbry-desktop/pull/6935))
- Added "Replay" option on autoplay countdown ([#6921](https://github.com/lbryio/lbry-desktop/pull/6921))
- Added "Loop" option on Lists ([#6921](https://github.com/lbryio/lbry-desktop/pull/6921))
- Added "Shuffle" option on Lists ([#6921](https://github.com/lbryio/lbry-desktop/pull/6921))
- Added Play Next/Previous buttons (with shortcuts SHIFT+N/SHIFT+P) ([#6921](https://github.com/lbryio/lbry-desktop/pull/6921))
- Separate control for autoplay next on video player ([#6921](https://github.com/lbryio/lbry-desktop/pull/6921))
- Channel Mention selection ability while creating a comment ([#7151](https://github.com/lbryio/lbry-desktop/pull/7151))
- Disk space setting under Data Hosting ([#7266](https://github.com/lbryio/lbry-desktop/pull/7266))
- Paginated 'All Playlists' page ([#7268](https://github.com/lbryio/lbry-desktop/pull/7268))
- Expanded playlist ordering tools ([#7305](https://github.com/lbryio/lbry-desktop/pull/7305))
- Setting to upgrade to alpha prerelease builds ([#7353](https://github.com/lbryio/lbry-desktop/pull/7353))
### Changed
- Changing the supported language from Filipino to Tagalog _community pr!_ ([#6951](https://github.com/lbryio/lbry-desktop/pull/6951))
- Don't show countdown to next item in list ([#6921](https://github.com/lbryio/lbry-desktop/pull/6921))
- Changed "View List" popup option to link, so can be opened on a new tab ([#6921](https://github.com/lbryio/lbry-desktop/pull/6921))
- App reorganized to remove lbry-redux and lbryinc repository dependencies ([#7240](https://github.com/lbryio/lbry-desktop/pull/7240))
- Styling cleanup for file reactions ([#7251](https://github.com/lbryio/lbry-desktop/pull/7251))
- Change share url to odysee and allow custom share url in settings ([#7258](https://github.com/lbryio/lbry-desktop/pull/7258))
- Change Sign in/up to Cloud Connect for Odysee ([#7260](https://github.com/lbryio/lbry-desktop/pull/7260))
- Upgraded to lbrynet v0.106.0 ([#7315](https://github.com/lbryio/lbry-desktop/pull/7315))
- Upgraded Electron to v11.5.0 ([#7276](https://github.com/lbryio/lbry-desktop/pull/7276))
- Cleaner Discover page filters ([#7306](https://github.com/lbryio/lbry-desktop/pull/7306))
- Scroll bar styling ([#7314](https://github.com/lbryio/lbry-desktop/pull/7314))
- Remove pages for obsolete features like invites, rewards, swap ([#7330](https://github.com/lbryio/lbry-desktop/pull/7330))
- Change file repost to modal _community pr!_ ([#7341](https://github.com/lbryio/lbry-desktop/pull/7341))
### Fixed
- Clicking on the title of a floating player will take you back to the list ([#6921](https://github.com/lbryio/lbry-desktop/pull/6921))
- Fix floating player stopping on markdown or image files ([#7073](https://github.com/lbryio/lbry-desktop/pull/7073))
- Fix list thumbnail upload ([#7074](https://github.com/lbryio/lbry-desktop/pull/7074))
- Stream Key is now hidden _community pr!_ ([#7127](https://github.com/lbryio/lbry-desktop/pull/7127))
- Fix playlist preview thumbnail ([#7178](https://github.com/lbryio/lbry-desktop/pull/7178)
- Fixed “Your Account” popup on mobile ([#7172](https://github.com/lbryio/lbry-desktop/pull/7172))
- Fix disable-support for comments ([#7245](https://github.com/lbryio/lbry-desktop/pull/7245))
- Fix Electron taking over .html files on linux ([#7291](https://github.com/lbryio/lbry-desktop/pull/7291))
- Fix floating player play/pause on drag _community pr!_ ([#7339](https://github.com/lbryio/lbry-desktop/pull/7339))
- Fix card dropdown menus triggering menu actions _community pr!_ ([#7335](https://github.com/lbryio/lbry-desktop/pull/7335))
## [0.51.2] - [2021-08-20]
### Added
- Show currently active playing item on playlist _community pr!_ ([#6453](https://github.com/lbryio/lbry-desktop/pull/6453))
- Add watch later to hover action for last used playlist on popup _community pr!_ ([#6274](https://github.com/lbryio/lbry-desktop/pull/6274))
- Add confirmation on comment removal _community pr!_ ([#6563](https://github.com/lbryio/lbry-desktop/pull/6563))
- Show on content page if a file is part of a playlist already _community pr!_([#6393](https://github.com/lbryio/lbry-desktop/pull/6393))
- Add filtering to playlists ([#6905](https://github.com/lbryio/lbry-desktop/pull/6905))
### Changed
- Use Canonical Url for copy link ([#6500](https://github.com/lbryio/lbry-desktop/pull/6500))
- Use better icon for copy link ([#6485](https://github.com/lbryio/lbry-desktop/pull/6485))
- Comments load paginated ([#6390](https://github.com/lbryio/lbry-desktop/pull/6390))
- Improve twitter share _community pr!_ ([#6690](https://github.com/lbryio/lbry-desktop/pull/6690))
- Update lighthouse search api _community pr!_ ([#6731](https://github.com/lbryio/lbry-desktop/pull/6731))
- Update sockety api _community pr!_ ([#6747](https://github.com/lbryio/lbry-desktop/pull/6747))
- Use resolve for OG metadata instead of chainquery _community pr!_ ([#6787](https://github.com/lbryio/lbry-desktop/pull/6787))
- Improved clickability of notification links _community pr!_ ([#6711](https://github.com/lbryio/lbry-desktop/pull/6711))
### Fixed
- App now supports '#' and ':' for claimId separator ([#6496](https://github.com/lbryio/lbry-desktop/pull/6496))
- Fix "exact match" being applied to Recommended ([#6460](https://github.com/lbryio/lbry-desktop/pull/6460))
- Fix upload button on creator analytics _community pr!_ ([#6458](https://github.com/lbryio/lbry-desktop/pull/6458))
- Prevent sidebar shortcut activation on textarea _community pr!_ ([#6454](https://github.com/lbryio/lbry-desktop/pull/6454))
- Improve accessibility and some minor css fixes _community pr!_ ([#6470](https:/github.com/lbryio/lbry-desktop/pull/6470))
- Fix drag / drop publish issues for web users _community pr!_ ([#6466](https://github.com/lbryio/lbry-desktop/pull/6466))
- Fix yarn copyenv on windows _community pr!_ ([#6702](https://github.com/lbryio/lbry-desktop/pull/6702))
- Fix unnecessary livestream api calls in channel page _community pr!_ ([#6652](https://github.com/lbryio/lbry-desktop/pull/6652))
- Fix desktop app fails to resolve deep links _community pr!_ ([#6779](https://github.com/lbryio/lbry-desktop/pull/6779))
- Fix wrong release date on GoogleVideo metadata _community pr!_ ([#6787](https://github.com/lbryio/lbry-desktop/pull/6787))
- Fix markdown line breaking mid word _community pr!_ ([#6805](https://github.com/lbryio/lbry-desktop/pull/6805))
- Added \ and = to reserved symbol warning _community pr!_ ([#6733](https://github.com/lbryio/lbry-desktop/pull/6733))
- Don't break words in chat + fix text overflow past 3 dot menu _community pr!_ ([#6602](https://github.com/lbryio/lbry-desktop/pull/6602))
- Fix embed shows wrong OG metadata _community pr!_ ([#6815](https://github.com/lbryio/lbry-desktop/pull/6815))
- Fix OG: "Unparsable data structure - Truncated Unicode character" _community pr!_ ([#6839](https://github.com/lbryio/lbry-desktop/pull/6839))
- Fix Paid embed warning overlay redirection button now links to odysee _community pr!_ ([#6819](https://github.com/lbryio/lbry-desktop/pull/6819))
- Fix comment section redirection to create channel _community pr!_ ([#6557](https://github.com/lbryio/lbry-desktop/pull/6557))
## [0.51.1] - [2021-06-26]
### Added
### Changed
### Fixed
- Enable sign up on desktop ([#6071](https://github.com/lbryio/lbry-desktop/issues/6071))
## [0.51.0] - [2021-06-26]
### Added
- Private and Publishable Playlists ([#6157](https://github.com/lbryio/lbry-desktop/pull/6157))
- Channel thumbnails in following side menu ([#6193](https://github.com/lbryio/lbry-desktop/pull/6193))
- Web is now PWA app ([#6120](https://github.com/lbryio/lbry-desktop/pull/6120))
- Send a tip with your comment ([#5920](https://github.com/lbryio/lbry-desktop/issues/5920))
- Search for tags in search dropdown ([#5876](https://github.com/lbryio/lbry-desktop/issues/5876))
- Japanese, Afrikaans, Filipino, Thai and Vietnamese language support ([#5684](https://github.com/lbryio/lbry-desktop/issues/5684))
- Brazilian-Portuguese language support ([#5900](https://github.com/lbryio/lbry-desktop/issues/5900))
- Highlight comments made by content owner _community pr!_ ([#5744](https://github.com/lbryio/lbry-desktop/pull/5744))
- Ability to report infringing content directly from the application ([#5808](https://github.com/lbryio/lbry-desktop/pull/5808))
- Re-added ability to export wallet transactions ([#5899](https://github.com/lbryio/lbry-desktop/pull/5899))
- 24-hour clock setting _community pr!_ ([#5820](https://github.com/lbryio/lbry-desktop/pull/5820))
- "Related" (recommendations) section: added option to view more from current creator _community pr!_ ([#5847](https://github.com/lbryio/lbry-desktop/pull/5847))
- Wallet: ability to swap cryptocurrency into LBC ([#5654](https://github.com/lbryio/lbry-desktop/pull/5654))
- Wallet: ability to send LBC directly to a user through a name or URL search _community pr!_ ([#5990](https://github.com/lbryio/lbry-desktop/pull/5990))
- Publish: ability to edit the Release Date field ([#6049](https://github.com/lbryio/lbry-desktop/pull/6049))
- Creator Settings: ability to mute specific words in comments ([#5934](https://github.com/lbryio/lbry-desktop/pull/5934))
- Creator Settings: ability to disable comments + assign moderators ([#6199](https://github.com/lbryio/lbry-desktop/pull/6199))
- Additional options in context-menu _community pr!_ ([#6106](https://github.com/lbryio/lbry-desktop/pull/6106))
### Changed
- Keyboard shortcut additions and changes _community pr!_ ([#5717](https://github.com/lbryio/lbry-desktop/pull/5717))
- Removed the 10k character-limit when editing _Posts_ ([#5719](https://github.com/lbryio/lbry-desktop/pull/5719))
- Improved search functionality (more filters, infinite-scroll, etc.) ([#5742](https://github.com/lbryio/lbry-desktop/pull/5742))
- Removed google related section ads ([#6312](https://github.com/lbryio/lbry-desktop/pull/6312))
- Debounce and min characters on wunderbar search ([#6319](https://github.com/lbryio/lbry-desktop/pull/6319))
- Don't use optimizer for gif thumbnails ([#6320](https://github.com/lbryio/lbry-desktop/pull/6320))
- Upgraded [LBRY SDK 0.99.0](https://github.com/lbryio/lbry/releases/tag/v0.99.0)
### Fixed
- Lazy-load claim images to improve app responsiveness ([#5795](https://github.com/lbryio/lbry-desktop/issues/5795))
- Fix display of upload date and view count on smaller screens ([#5822](https://github.com/lbryio/lbry-desktop/issues/5822))
- Autoplay looping to a previous video or itself ([#5711](https://github.com/lbryio/lbry-desktop/pull/5711))
- Autoplay not working in mini-player mode ([#5716](https://github.com/lbryio/lbry-desktop/pull/5716))
- Edited claim accidentally moved to 'Anonymous' ([#5767](https://github.com/lbryio/lbry-desktop/pull/5767))
- Squished "Upload Date" and "View Count" on smaller screens _community pr!_ ([#5823](https://github.com/lbryio/lbry-desktop/pull/5823))
- Home and End key not working in search bar ([#5867](https://github.com/lbryio/lbry-desktop/pull/5867))
- Unable to buy paid-Images or Posts ([#6114](https://github.com/lbryio/lbry-desktop/pull/6114))
## [0.50.2] - [2021-04-2]
### Changed
- Disable PDFs until security issue is fixed
## [0.50.1] - [2021-03-18]
### Fixed
- Upgrade modal extended beyond the height of the app ([#5709](https://github.com/lbryio/lbry-desktop/pull/5709))
## [0.50.0] - [2021-03-18]
### Added
- New moderation tools: block & mute ([#5572](https://github.com/lbryio/lbry-desktop/pull/5572))
- Improved markdown file styling ([#5659](https://github.com/lbryio/lbry-desktop/pull/5659))
- Mass tip unlock ([#5409](https://github.com/lbryio/lbry-desktop/pull/5387))
- Zoomable image viewer in Markdown (posts and comments) _community pr!_ ([#5387](https://github.com/lbryio/lbry-desktop/pull/5387))
- Enable PDF Viewer in App _community pr!_ ([#2903](https://github.com/lbryio/lbry-desktop/issues/2903))
- The search bar now handles both lbry.tv and Odysee links regardless of which site the user is in ([#5604](https://github.com/lbryio/lbry-desktop/issues/5604))
- The video/audio player's control tooltip is now localized and includes hints for keyboard shortcuts ([#5584](https://github.com/lbryio/lbry-desktop/issues/5584))
- Finnish and Norwegian language support ([#5532](https://github.com/lbryio/lbry-desktop/issues/5532))
### Changed
- Updated lbry-sdk to [0.92.0](https://github.com/lbryio/lbry-sdk/releases/tag/v0.92.0)
- Re-enable PDF Viewer in desktop app _community pr!_ ([#5420](https://github.com/lbryio/lbry-desktop/issues/5420))
### Fixed
- Player doesn't stop playing when editing a comment that includes a video url _community pr!_ ([#5384](https://github.com/lbryio/lbry-desktop/pull/5384))
- Main video/audio playback stopped every time a comment is edited or deleted ([#5606](https://github.com/lbryio/lbry-desktop/pull/5606))
- Winning search result not working correctly with mature content enabled _community pr!_ ([#5388](https://github.com/lbryio/lbry-desktop/pull/5388))
- Home/End key not working as expected in Search Bar ([#5642](https://github.com/lbryio/lbry-desktop/pull/5642))
- Fixes to inline videos in comments _community pr!_ ([#5389](https://github.com/lbryio/lbry-desktop/pull/5389))
- Search page crashing on some results ([#5428](https://github.com/lbryio/lbry-desktop/pull/5428))
- Long channel names caused comments to extend underneath related content _community pr!_ ([#5431](https://github.com/lbryio/lbry-desktop/pull/5431))
- Keep floating player always visible _community pr!_ ([#5448](https://github.com/lbryio/lbry-desktop/pull/5448))
- LBRY Desktop incorrectly setting itself as the default HTML-file application in Linux ([#5617](https://github.com/lbryio/lbry-desktop/pull/5617))
- All notifications being cleared when only 1 is clicked ([#5619](https://github.com/lbryio/lbry-desktop/pull/5619))
- Markdown editor's "Create Link (Ctrl-K)" action now directly highlights the URL stub when the label is present ([#5585](https://github.com/lbryio/lbry-desktop/issues/5585))
## [0.49.4] - [2021-01-26]
### Added
- Theater mode ([#5255](https://github.com/lbryio/lbry-desktop/pull/5255))
- Repost flow improvements ([#5293](https://github.com/lbryio/lbry-desktop/pull/5293))
- Desktop: Enable 'Alt+Left/Right' for history navigation _community pr!_ ([#5203](https://github.com/lbryio/lbry-desktop/pull/5203))
- Make playback rate setting persistent _community pr!_ ([#5310](https://github.com/lbryio/lbry-desktop/pull/5310))
- Make UI zoom setting persistent _community pr!_ ([#5385](https://github.com/lbryio/lbry-desktop/pull/5385))
### Changed
- No longer supporting 32 bit windows
- Updated lbry-sdk to [0.88.0](https://github.com/lbryio/lbry-sdk/releases/tag/v0.88.0)
- Improve how spendable balance is displayed ([#5409](https://github.com/lbryio/lbry-desktop/pull/5276))
### Fixed
- Lots of bug fixes
- Fix bug where subscriptions could be lost ([#5302](https://github.com/lbryio/lbry-desktop/pull/5302))
## [0.49.3] - [2020-12-30]
### Added
- Turn timestamps in a video's description and comments into links _community pr!_ ([#5156](https://github.com/lbryio/lbry-desktop/pull/5156))
- Mobile video player enhancements and the ability to tap on the left and right edges to seek _community pr!_ ([#5119](https://github.com/lbryio/lbry-desktop/pull/5119))
- Added tile layout for tags page [a0ec5](https://github.com/lbryio/lbry-desktop/commit/a0ec5097d93496b92736e1682f9fb2d20dadf425).
### Changed
- Repost option now has a full page and can be triggered from search results (click "View competing uploads for ...").
### Fixed
- [Upgrade SDK to 0.87.0](https://github.com/lbryio/lbry-sdk/releases/tag/v0.87.0). Forces wallet resync which fixes balance issues for some users and improves memory usage.
## [0.49.1] - [2020-12-15]
### Fixed
- Sidenav unable to open when on the file page
- Fix "open external" button to open files on your OS
## [0.49.0] - [2020-12-10]
### Added
- Better search suggestions ([#5124](https://github.com/lbryio/lbry-desktop/pull/5124))
- Inline video player in comments/markdown posts ([#4894](https://github.com/lbryio/lbry-desktop/pull/4894))
- Better handling of winning claim + top page improvements ([#4944](https://github.com/lbryio/lbry-desktop/pull/4944))
- Comment pinning ([#4895](https://github.com/lbryio/lbry-desktop/pull/4895))
- Comment reactions ([#4895](https://github.com/lbryio/lbry-desktop/pull/4895))
- Language search ([#4907](https://github.com/lbryio/lbry-desktop/pull/4907))
- Content notifications ([#4981](https://github.com/lbryio/lbry-desktop/pull/4981))
- Customize scrollbar to fit the dark theme _community pr!_ ([#5056](https://github.com/lbryio/lbry-desktop/pull/5056))
- Keyboard shortcut: adjust video playback-rate using ">" and "<" _community pr!_ ([#5075](https://github.com/lbryio/lbry-desktop/pull/5075))
### Changed
- Updated lbry-sdk to [0.86.1](https://github.com/lbryio/lbry-sdk/releases/tag/v0.86.1)
- Refactored file/lbc details UI ([#4937](https://github.com/lbryio/lbry-desktop/pull/4937))
### Fixed
- Loss of subscriptions on startup ([#4882](https://github.com/lbryio/lbry-desktop/pull/4882))
- Fix lost search results when a timeout occurs _community pr!_ ([#4996](https://github.com/lbryio/lbry-desktop/pull/4996))
- Can't slide volume slider in pop-sout mode _community pr!_ ([#4913](https://github.com/lbryio/lbry-desktop/pull/4913))
- Hide mouse cursor when video is playing _community pr!_ ([#5046](https://github.com/lbryio/lbry-desktop/pull/5046))
## [0.48.2] - [2020-10-16]
### Changed
- Disable snapshot thumbnail upload on desktop until Electron issue is fixed ([#4905](https://github.com/lbryio/lbry-desktop/pull/4905))
### Fixed
- Files not opening externally properly ([#4908](https://github.com/lbryio/lbry-desktop/pull/4908))
## [0.48.1] - [2020-10-14]
### Added
- Add Publish Preview dialog _community pr!_ ([#4620](https://github.com/lbryio/lbry-desktop/pull/4620))
- "ctrl/cmd+enter" can now be used to directly submit a comment ([#4809](https://github.com/lbryio/lbry-desktop/pull/4809))
- Deeper comment thread depth ([#4868](https://github.com/lbryio/lbry-desktop/pull/4868))
### Changed
- Updated lbry-sdk to [0.83.0](https://github.com/lbryio/lbry-sdk/releases/tag/v0.83.0)
- Upgrade electron to v9 ([#4824](https://github.com/lbryio/lbry-desktop/pull/4824))
- Fade out expandable areas (e.g. comments, file description) when collapsed _community pr!_ ([#4818](https://github.com/lbryio/lbry-desktop/pull/4818))
### Fixed
- Videos started as muted on initial desktop session _community pr!_ ([#4864](https://github.com/lbryio/lbry-desktop/pull/4864))
## [0.48.0] - [2020-09-23]
### Added
- New File Page layout + make sidebar collapsable ([#4648](https://github.com/lbryio/lbry-desktop/pull/4648))
- Block mature content when accessed directly from URL _community pr!_ ([#4560](https://github.com/lbryio/lbry-desktop/pull/4560))
- You can now add LBRY as a search engine in your browser (via OpenSearch) _community pr!_ ([#4640](https://github.com/lbryio/lbry-desktop/pull/4640))
- In-app text and markdown publishing and editing _community pr!_ ([#4591](https://github.com/lbryio/lbry-desktop/pull/4591))
- New comment stats on creator analytics page _jiggytom certified_ ([#4715](https://github.com/lbryio/lbry-desktop/pull/4715))
### Changed
- Move file properties over thumbnails for more space-saving ([#4632](https://github.com/lbryio/lbry-desktop/pull/4632))
### Fixed
- Fix sluggish Back button when navigation back to channels with lots of comments _community pr!_ ([#4576](https://github.com/lbryio/lbry-desktop/pull/4576))
- Fix 'Related' and 'Comments' section lazy-load not working in some scenarios _community pr!_ ([#4586](https://github.com/lbryio/lbry-desktop/pull/4586))
- Fix comment-creation failure if you have recently deleted a channel _community pr!_ ([#4630](https://github.com/lbryio/lbry-desktop/pull/4630))
- Tip Modal: Don't do final submit when the intention is to create New Channel _community pr!_ ([#4629](https://github.com/lbryio/lbry-desktop/pull/4629))
- Fix related + search results loading slowly ([#4657](https://github.com/lbryio/lbry-desktop/pull/4657))
- Fix partially untranslated text in the Upgrade Modal _community pr!_ ([#4722](https://github.com/lbryio/lbry-desktop/pull/4722))
- Fix floating player being paused after dragging _community pr!_ ([#4710](https://github.com/lbryio/lbry-desktop/pull/4710))
- Web: Fix 'Download' not triggering until second attempt _community pr!_ ([#4721](https://github.com/lbryio/lbry-desktop/pull/4721))
- Floating player: Stay within screen when window is resized _community pr!_([#4750](https://github.com/lbryio/lbry-desktop/pull/4750))
- App crash when including % sign in search _community pr!_([#4753](https://github.com/lbryio/lbry-desktop/pull/4753))
- Fix markdown editor "preview" not working when `<iframe>`s are present _community pr!_([#4767](https://github.com/lbryio/lbry-desktop/pull/4767))
- First comment on claim not showing up immeditately ([#4747](https://github.com/lbryio/lbry-desktop/pull/4747))
- Fix "Password updated successfully" kept showing up _community pr!_([#4766](https://github.com/lbryio/lbry-desktop/pull/4766))
- Fix "Price=free" giving 0 results in Following query _community pr!_ ([#4489](https://github.com/lbryio/lbry-desktop/pull/4489))
## [0.47.1] - [2020-07-23]
### Added
- Allow zooming on Desktop _community pr!_ ([#4513](https://github.com/lbryio/lbry-desktop/pull/4513))
- Show "YT Creator" label in File Page as well _community pr!_ ([#4523](https://github.com/lbryio/lbry-desktop/pull/4523))
- Add option to retry video stream on failure _community pr!_ ([#4541](https://github.com/lbryio/lbry-desktop/pull/4541))
- Allow blocking channels from comments ([#4557](https://github.com/lbryio/lbry-desktop/pull/4557))
### Changed
- Updated lbry-sdk to [0.79.1](https://github.com/lbryio/lbry-sdk/releases/tag/v0.79.1)
### Fixed
- Fix 'transcoding' checkbox state when switching file types _community pr!_ ([#4529](https://github.com/lbryio/lbry-desktop/pull/4529))
- Fix channel file-search not available in mobile _community pr!_ ([#4527](https://github.com/lbryio/lbry-desktop/pull/4527))
- New Channel: Fix incorrect GUI configuration at entry _community pr!_ ([#4545](https://github.com/lbryio/lbry-desktop/pull/4545))
- Hide blocked channels in comments ([#4557](https://github.com/lbryio/lbry-desktop/pull/4557))
## [0.47.0] - [2020-07-13]
### Added ### Added
@ -543,7 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Pause the "autoplay next" timer when performing long operations such as Tipping, Supporting or commenting _community pr!_ ([4419](https://github.com/lbryio/lbry-desktop/pull/4419)) - Pause the "autoplay next" timer when performing long operations such as Tipping, Supporting or commenting _community pr!_ ([4419](https://github.com/lbryio/lbry-desktop/pull/4419))
- Email notification management page ([#4409](https://github.com/lbryio/lbry-desktop/pull/4409)) - Email notification management page ([#4409](https://github.com/lbryio/lbry-desktop/pull/4409))
- Publish Page improvements to prevent accidental overwrites of existing claims _community pr!_ ([#4416](https://github.com/lbryio/lbry-desktop/pull/4416)) - Publish Page improvements to prevent accidental overwrites of existing claims _community pr!_ ([#4416](https://github.com/lbryio/lbry-desktop/pull/4416))
- Option to remove abandoned claims from Blocked channels page _community pr!_ ([#4433](https://github.com/lbryio/lbry-desktop/pull/4433)) - Option to remove abandoned claims from Blocked Channels page _community pr!_ ([#4433](https://github.com/lbryio/lbry-desktop/pull/4433))
- New channel create/edit page ([#4445](https://github.com/lbryio/lbry-desktop/pull/4445)) - New channel create/edit page ([#4445](https://github.com/lbryio/lbry-desktop/pull/4445))
- Add dialog to copy various types of links for a claim _community pr!_ ([#4474](https://github.com/lbryio/lbry-desktop/pull/4474)) - Add dialog to copy various types of links for a claim _community pr!_ ([#4474](https://github.com/lbryio/lbry-desktop/pull/4474))
- Add password reset link to settings page for logged in users _community pr!_ ([#4473](https://github.com/lbryio/lbry-desktop/pull/4473)) - Add password reset link to settings page for logged in users _community pr!_ ([#4473](https://github.com/lbryio/lbry-desktop/pull/4473))
@ -564,7 +35,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Fix "Refresh" on Publish page not showing the loading indicator when pressed _community pr!_ ([#4451](https://github.com/lbryio/lbry-desktop/pull/4451)) - Fix "Refresh" on Publish page not showing the loading indicator when pressed _community pr!_ ([#4451](https://github.com/lbryio/lbry-desktop/pull/4451))
- Fix video duration not appearing on Mobile with enough width _community pr!_ ([#4452](https://github.com/lbryio/lbry-desktop/pull/4452)) - Fix video duration not appearing on Mobile with enough width _community pr!_ ([#4452](https://github.com/lbryio/lbry-desktop/pull/4452))
- Fix video transcode setting not reflected correctly (MP3 incorrectly transcoded to MP4) _community pr!_ ([#4458](https://github.com/lbryio/lbry-desktop/pull/4458)) - Fix video transcode setting not reflected correctly (MP3 incorrectly transcoded to MP4) _community pr!_ ([#4458](https://github.com/lbryio/lbry-desktop/pull/4458))
- Fix scrolling glitch when results are exactly the page size _community pr!_ ([#4521](https://github.com/lbryio/lbry-desktop/pull/4521))
- Fix search results not appearing when scrolling due to long Tags or Following list in the navigation bar _community pr!_ ([#4465](https://github.com/lbryio/lbry-desktop/pull/4465)) - Fix search results not appearing when scrolling due to long Tags or Following list in the navigation bar _community pr!_ ([#4465](https://github.com/lbryio/lbry-desktop/pull/4465))
- Fix unmuted state lost or reverted when playing a new video _community pr!_ ([#4483](https://github.com/lbryio/lbry-desktop/pull/4483)) - Fix unmuted state lost or reverted when playing a new video _community pr!_ ([#4483](https://github.com/lbryio/lbry-desktop/pull/4483))
@ -1869,7 +1339,7 @@ This release includes a breaking change that will reset many of your settings. T
### Removed ### Removed
- Removed the label "Max purchase price" from settings page. It was redundant. - Removed the label "Max Purchase Price" from settings page. It was redundant.
- Unused old files from previous commit(9c3d633) - Unused old files from previous commit(9c3d633)
### Fixed ### Fixed

View file

@ -1,19 +1,21 @@
# Contribute to LBRY # Contribute to LBRY
**First:** if you're unsure or afraid of anything, just ask or submit the issue or pull request anyways. You won't be yelled at for giving your best effort. The worst that can happen is that you'll be politely asked to change something. We appreciate any sort of contributions, and don't want a wall of rules to get in the way of that. **First:** if you're unsure or afraid of anything, just ask or submit the issue or pull request anyways. You won't be yelled at for giving your best effort. The worst that can happen is that you'll be politely asked to change something. We appreciate any sort of contributions, and don't want a wall of rules to get in the way of that.
However, for those individuals who want a bit more guidance on the best way to contribute to the project, read on. This document will cover what we're looking for. By addressing all the points we're looking for, it raises the chances we can quickly merge or address your contributions. However, for those individuals who want a bit more guidance on the best way to contribute to the project, read on. This document will cover what we're looking for. By addressing all the points we're looking for, it raises the chances we can quickly merge or address your contributions.
## TL;DR? ## TL;DR?
- [Here](https://github.com/lbryio/lbry-desktop/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+no%3Aassignee) * [Here](https://github.com/lbryio/lbry-desktop/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+no%3Aassignee)
is a list of help wanted issues. 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. * 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.
* [Check out this video for a quickstart guide to running and developing the desktop app](https://spee.ch/7da73fc508ffc4ff8b2711e3c3950110430b0c5f/LBRYAppDesign.mp4).
## Contents ## Contents
- [Choose an Issue](#choose-an-issue) - [Choose an Issue](#choose-an-issue)
- [Code Overview](#code-overview) - [Code Overview](#code-overview)
- [Libraries](#libraries) - [Libraries](#libraries)
@ -36,20 +38,20 @@ receives contributions from individuals outside the core team -- such as yoursel
To make contributing as easy and rewarding as possible, we have instituted the following system: To make contributing as easy and rewarding as possible, we have instituted the following system:
- Anyone can view all issues in the system by clicking on the * Anyone can view all issues in the system by clicking on the
[Issues](https://github.com/lbryio/lbry-desktop/issues) button at the top of the page. Feel free to [Issues](https://github.com/lbryio/lbry-desktop/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 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). because we do tip people for reporting bugs).
- Once on the [Issues](https://github.com/lbryio/lbry-desktop/issues) page, a potential contributor can * Once on the [Issues](https://github.com/lbryio/lbry-desktop/issues) page, a potential contributor can
filter issues by the filter issues by the
[Help Wanted](https://github.com/lbryio/lbry-desktop/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+no%3Aassignee) [Help Wanted](https://github.com/lbryio/lbry-desktop/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. label to see a curated list of suggested issues with which community members can help.
- Every * Every
[Help Wanted](https://github.com/lbryio/lbry-desktop/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+no%3Aassignee) [Help Wanted](https://github.com/lbryio/lbry-desktop/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+no%3Aassignee)
issue is ranked on a scale from zero to four. issue is ranked on a scale from zero to four.
| Level | Description | | Level | Description |
| ---------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | | ------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------- |
| [**level 0**](https://github.com/lbryio/lbry-desktop/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+label%3A%22level%3A+0%22+no%3Aassignee) | Typos and text edits -- a tech-savvy non-programmer can fix these. | | [**level 0**](https://github.com/lbryio/lbry-desktop/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+label%3A%22level%3A+0%22+no%3Aassignee) | Typos and text edits -- a tech-savvy non-programmer can fix these. |
| [**level 1**](https://github.com/lbryio/lbry-desktop/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+label%3A%22level%3A+1%22+no%3Aassignee) | Programming issues that require little knowledge of how the LBRY app works. | | [**level 1**](https://github.com/lbryio/lbry-desktop/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+label%3A%22level%3A+1%22+no%3Aassignee) | Programming issues that require little knowledge of how the LBRY app works. |
| [**level 2**](https://github.com/lbryio/lbry-desktop/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+label%3A%22level%3A+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-desktop/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22+label%3A%22level%3A+2%22+no%3Aassignee) | Issues of average difficulty that require the developer to dig into how the app works a little bit. |
@ -78,7 +80,7 @@ our styling rules and code best practices.
### Libraries ### Libraries
This project uses [lbry-redux](https://github.com/lbryio/lbry-redux) and [lbryionc](https://github.com/lbryio/lbryinc) to share Redux and LBRY API specific code with other LBRY apps. Over time, more Redux code that is suitable to be shared will be moved into lbry-redux. If modifying Redux code, you may be asked to make some of your changes in lbry-redux rather than lbry-desktop. The steps to work with lbry-redux locally can be found [here](https://github.com/lbryio/lbry-redux#local-development). This project uses [lbry-redux](https://github.com/lbryio/lbry-redux) and [lbryionc](https://github.com/lbryio/lbryinc) to share Redux and LBRY API specific code with [lbry-android](https://github.com/lbryio/lbry-android) and other LBRY apps. Over time, more Redux code that is suitable to be shared will be moved into lbry-redux. If modifying Redux code, you may be asked to make some of your changes in lbry-redux rather than lbry-desktop. The steps to work with lbry-redux locally can be found [here](https://github.com/lbryio/lbry-redux#local-development).
### Flow ### Flow
@ -138,15 +140,15 @@ Editor integrations are available [here](https://prettier.io/docs/en/editors.htm
There are a few tools integrated to the project that will ease the process of debugging: There are a few tools integrated to the project that will ease the process of debugging:
- [Chrome DevTools](https://developer.chrome.com/devtools) * [Chrome DevTools](https://developer.chrome.com/devtools)
- Also available for the main process as a [remote target](chrome://inspect/#devices). * Also available for the main process as a [remote target](chrome://inspect/#devices).
- [Electron Devtron](https://electronjs.org/devtron) * [Electron Devtron](https://electronjs.org/devtron)
- [React DevTools](https://github.com/facebook/react-devtools) * [React DevTools](https://github.com/facebook/react-devtools)
- [Redux DevTools](https://github.com/gaearon/redux-devtools) * [Redux DevTools](https://github.com/gaearon/redux-devtools)
## Submit a Pull Request ## Submit a Pull Request
- After deciding what to work on, a potential contributor can * 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, [fork](https://help.github.com/articles/fork-a-repo/) this repository, make his or her changes,
and submit a and submit a
[pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/). A [pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/). A
@ -155,12 +157,12 @@ There are a few tools integrated to the project that will ease the process of de
manner and, therefore, not begin working on anything reserved (or updated) within the last 3 days. 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 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. available. Contributors are encouraged to ask if they have any questions about issue availability.
- The [changelog](https://github.com/lbryio/lbry-desktop/blob/master/CHANGELOG.md) should be updated to * The [changelog](https://github.com/lbryio/lbry-desktop/blob/master/CHANGELOG.md) should be updated to
include a reference to the fix/change/addition. See previous entries for format. include a reference to the fix/change/addition. See previous entries for format.
- Once the pull request is visible in the LBRY repo, a LBRY team member will review it and make sure * 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 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. on our suggestions and comments.
- Then, upon a satisfactory review of the code, we will merge it and send the contributor a tip (in * Then, upon a satisfactory review of the code, we will merge it and send the contributor a tip (in
LBC) for the contribution. LBC) for the contribution.
We are dedicated to being fair and friendly in this process. In **general**, level 4 issues will be We are dedicated to being fair and friendly in this process. In **general**, level 4 issues will be
@ -189,10 +191,10 @@ 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 | | Name | Role | Discord | Email |
| -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------- | | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------------
| [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.com | | [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.com |
| [Sean](https://github.com/seanyesmunt) | The primary engineer working on the app. Feel free to ask any questions about the code. | sean | sean@lbry.com | | [Sean](https://github.com/seanyesmunt) | The primary engineer working on the app. Feel free to ask any questions about the code. | sean | sean@lbry.com |
Join our Discord [here](https://chat.lbry.com/). Join our Discord [here](https://chat.lbry.com/).

13
Dockerfile Normal file
View file

@ -0,0 +1,13 @@
FROM node:10
EXPOSE 1337
RUN yarn -v && npm -v
RUN apt-get update -y && apt-get upgrade -y
WORKDIR /app
ENV PATH="/app/node_modules/.bin:${PATH}"
COPY ./ ./
RUN rm -rf node_modules && APP_ENV=web yarn && SDK_API_URL='https://api.lbry.tv/api/proxy' NODE_ENV=production yarn compile:web --display errors-only
CMD node ./dist/web/server.js

View file

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2017-2021 LBRY Inc. Copyright (c) 2017-2020 LBRY Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View file

@ -1,8 +1,8 @@
<img width="40%" src="https://miro.medium.com/max/5198/1*bTVuL2THG_0mpwmE-n7Ezg.png" /> <img width="40%" src="https://miro.medium.com/max/5198/1*bTVuL2THG_0mpwmE-n7Ezg.png" />
# LBRY App # LBRY App - https://lbry.tv
This repo contains the UI code that powers the official LBRY desktop app. The LBRY app is a graphical browser for the decentralized content marketplace provided by the This repo contains the UI code that powers the official LBRY desktop app, as well as lbry.tv. The LBRY app is a graphical browser for the decentralized content marketplace provided by the
[LBRY](https://lbry.com) protocol. It is essentially the [LBRY](https://lbry.com) protocol. It is essentially the
[lbry daemon](https://github.com/lbryio/lbry) bundled with a UI using [lbry daemon](https://github.com/lbryio/lbry) bundled with a UI using
[Electron](https://electron.atom.io/). [Electron](https://electron.atom.io/).
@ -29,7 +29,7 @@ This repo contains the UI code that powers the official LBRY desktop app. The LB
</a> </a>
</h2> </h2>
![App GIF](https://spee.ch/ba/lbry-joule.gif) ![App GIF](https://spee.ch/@lbrynews:0/lbry-joule.gif)
## Table of Contents ## Table of Contents
@ -55,7 +55,7 @@ We provide installers for Windows, macOS (v10.12.4, Sierra, or greater), and Deb
| Latest Pre-release | [Download](https://lbry.com/get/lbry.pre.exe) | [Download](https://lbry.com/get/lbry.pre.dmg) | [Download](https://lbry.com/get/lbry.pre.deb) | | Latest Pre-release | [Download](https://lbry.com/get/lbry.pre.exe) | [Download](https://lbry.com/get/lbry.pre.dmg) | [Download](https://lbry.com/get/lbry.pre.deb) |
Our [releases page](https://github.com/lbryio/lbry-desktop/releases) also contains the latest Our [releases page](https://github.com/lbryio/lbry-desktop/releases) also contains the latest
release, pre-releases, and past builds. release, pre-releases, and past builds.
_Note: If the deb fails to install using the Ubuntu Software Center, install manually via `sudo dpkg -i <path to deb>`. You'll need to run `sudo apt-get install -f` if this is the first time installing it to install dependencies_ _Note: If the deb fails to install using the Ubuntu Software Center, install manually via `sudo dpkg -i <path to deb>`. You'll need to run `sudo apt-get install -f` if this is the first time installing it to install dependencies_
To install from source or make changes to the application, continue to the next section below. To install from source or make changes to the application, continue to the next section below.
@ -63,35 +63,95 @@ To install from source or make changes to the application, continue to the next
**Community maintained** builds for Arch Linux and Flatpak are available, see below. These installs will need to be updated manually as the in-app update process only supports Debian installs at this time. **Community maintained** builds for Arch Linux and Flatpak are available, see below. These installs will need to be updated manually as the in-app update process only supports Debian installs at this time.
_Note: If coming from a deb install, the directory structure is different and you'll need to [migrate data](https://lbry.com/faq/backup-data)._ _Note: If coming from a deb install, the directory structure is different and you'll need to [migrate data](https://lbry.com/faq/backup-data)._
| | Flatpak | Arch | Nixpkgs | ARM/ARM64 | | | Flatpak | Arch | Raspberry Pi |
| -------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------- | | -------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------- |
| Latest Release | [FlatHub Page](https://flathub.org/apps/details/io.lbry.lbry-app) | [AUR Package](https://aur.archlinux.org/packages/lbry-desktop-bin/) | [Nixpkgs](https://search.nixos.org/packages?channel=unstable&show=lbry&query=lbry) | [Build Guide](https://lbry.tv/@LBRYarm:5) | | Latest Release | [FlatHub Page](https://flathub.org/apps/details/io.lbry.lbry-app) | [AUR Package](https://aur.archlinux.org/packages/lbry-app-bin/) | [Pi Installer](https://lbrypi.com) |
| Maintainers | N/A | [@RubenKelevra](https://github.com/RubenKelevra) | [@Enderger](https://github.com/enderger) | [@Madiator2011](https://github.com/kodxana) | | Maintainers | [@kcSeb](https://keybase.io/kcseb) | [@kcSeb](https://keybase.io/kcseb)/[@TimurKiyivinski](https://github.com/TimurKiyivinski) | [@Madiator2011](https://github.com/kodxana) |
## Usage ## Usage
Start the installed application to interact with the LBRY network. Double click the installed application to interact with the LBRY network.
## Running from Source ## Running from Source
You can run the web version (lbry.tv), the electron app, or both at the same time.
#### Prerequisites #### Prerequisites
- [Git](https://git-scm.com/downloads) - [Git](https://git-scm.com/downloads)
- [Node.js](https://nodejs.org/en/download/) (v16 required) - [Node.js](https://nodejs.org/en/download/) (v10 required)
- [Corepack](https://nodejs.org/dist/latest-v17.x/docs/api/corepack.html) `npm i -g corepack` (Included in nodejs 14 LTS, 16 LTS and 17)
- [Yarn](https://yarnpkg.com/en/docs/install) - [Yarn](https://yarnpkg.com/en/docs/install)
1. Clone (or [fork](https://help.github.com/articles/fork-a-repo/)) this repository: `git clone https://github.com/lbryio/lbry-desktop` 1. Clone (or [fork](https://help.github.com/articles/fork-a-repo/)) this repository: `git clone https://github.com/lbryio/lbry-desktop`
2. Change directory into the cloned repository: `cd lbry-desktop` 2. Change directory into the cloned repository: `cd lbry-desktop`
3. If corepack is not enabled, run `sudo corepack enable` (the sudo is necessary for system-wide installation, if you use container, nvm etc... you might not be forced to use it) 3. Install the dependencies: `yarn`
4. Install the dependencies: `yarn`
#### Run the electron app #### Run the electron app
`yarn compile:electron` (this is only needed the first time you run the app)
`yarn dev` `yarn dev`
- If you want to build and launch the production app you can run `yarn build`. This will give you an executable inside the `/dist` folder. We use [electron-builder](https://github.com/electron-userland/electron-builder) to create distributable packages. - If you want to build and launch the production app you can run `yarn build`. This will give you an executable inside the `/dist` folder. We use [electron-builder](https://github.com/electron-userland/electron-builder) to create distributable packages.
#### Run the web app for development
`yarn compile:web` (this is only needed the first time you run the app)
`yarn dev:web`
- This uses webpack-dev-server and includes hot-reloading. If you want to debug the [web server we use in production](https://github.com/lbryio/lbry-desktop/blob/master/src/platforms/web/server.js) you can run `yarn dev:web-server`. This starts a server at `localhost:1337` and does not include hot reloading.
#### Customize the web app
- In root directory, copy .env.defaults to .env and make changes
```
cp .env.defaults .env
nano .env
```
- To specify your own OG-IMAGE
You can either place a png named v2-og.png in the /custom folder or specify the OG_IMAGE_URL in .env
- If you want to customize the homepage content
1. add `CUSTOM_HOMEPAGE=true` to the '.env' file
2. copy `/custom/homepage.example.js` to `/custom/homepage.js` and make desired changes to `homepage.js`
- If you want up to two custom sidebar links
```
PINNED_URI_1=@someurl#2/someclaim#4
PINNED_LABEL_1=Linktext
PINNED_URI_2=$/discover?t=tag&[queryparams]
PINNED_LABEL_2=OtherLinkText
```
- Finally `NODE_ENV=production yarn compile:web` to rebuild
_Note: You don't need to edit the .env file in the /web folder - that is copied during compile._
#### Deploy the web app (_experimental_)
1. Create a server with a domain name and a reverse proxy https to port 1337.
2. Install pm2, node v10, yarn
3. Clone this repo
4. Make any customizations as above
5. Run `yarn` to install
6. Run `NODE_ENV=production yarn compile:web` to build
7. Set up pm2 to start ./web/index.js
#### Run both at the same time
Run the two commands above in separate terminal windows
```
yarn dev
// in another terminal window
yarn dev:web
```
#### Resetting your Packages #### Resetting your Packages
If the app isn't building, or `yarn xxx` commands aren't working you may need to just reset your `node_modules`. To do so you can run: `rm -r node_modules && yarn` or `del /s /q node_modules && yarn` on Windows. If the app isn't building, or `yarn xxx` commands aren't working you may need to just reset your `node_modules`. To do so you can run: `rm -r node_modules && yarn` or `del /s /q node_modules && yarn` on Windows.
@ -110,9 +170,8 @@ This project is MIT licensed. For the full license, see [LICENSE](LICENSE).
## Security ## Security
We take security seriously. Please contact security@lbry.com regarding any security issues. Our PGP key is [here](https://lbry.com/faq/pgp-key) if you need it. Previous versions up to v0.50.2 were signed by [Sean Yesmunt](https://keybase.io/seanyesmunt/key.asc). We take security seriously. Please contact security@lbry.com regarding any security issues. Our PGP key is [here](https://keybase.io/lbry/key.asc) if you need it. All releases are signed by [Sean Yesmunt](https://keybase.io/seanyesmunt/key.asc).
New Releases are signed by [Jessop Breth](https://keybase.io/jessopb/key.asc).
## Contact ## Contact
The primary contact for this project is [@jessopb](https://github.com/jessopb). The primary contact for this project is [@seanyesmunt](https://github.com/seanyesmunt).

2
action.yml Normal file
View file

@ -0,0 +1,2 @@
- name: Danger JS Action
uses: danger/danger-js@9.1.8

View file

@ -7,8 +7,6 @@ module.exports = api => {
'import-glob', 'import-glob',
'@babel/plugin-transform-runtime', '@babel/plugin-transform-runtime',
['@babel/plugin-proposal-decorators', { decoratorsBeforeExport: true }], ['@babel/plugin-proposal-decorators', { decoratorsBeforeExport: true }],
['@babel/plugin-proposal-private-methods', { 'loose': false }],
['@babel/plugin-proposal-private-property-in-object', { 'loose': false }],
'@babel/plugin-transform-flow-strip-types', '@babel/plugin-transform-flow-strip-types',
'@babel/plugin-proposal-class-properties', '@babel/plugin-proposal-class-properties',
'react-hot-loader/babel', 'react-hot-loader/babel',

View file

@ -4,14 +4,17 @@ const fs = require('fs');
const path = require('path'); const path = require('path');
var electron_notarize = require('electron-notarize'); var electron_notarize = require('electron-notarize');
module.exports = async function() { module.exports = async function(params) {
if (process.env.RUNNER_OS !== 'macOS') { // Only notarize the app on Mac OS only.
if (process.platform !== 'darwin') {
return; return;
} }
console.log('afterSign hook triggered', params);
const appId = 'io.lbry.LBRY'; // Same appId in electron-builder.
const appPath = path.resolve(__dirname, '../dist/electron/mac/LBRY.app'); let appId = 'io.lbry.LBRY';
let appPath = path.join(params.appOutDir, `${params.packager.appInfo.productFilename}.app`);
if (!fs.existsSync(appPath)) { if (!fs.existsSync(appPath)) {
throw new Error(`Cannot find application at: ${appPath}`); throw new Error(`Cannot find application at: ${appPath}`);
} }
@ -22,8 +25,8 @@ module.exports = async function() {
await electron_notarize.notarize({ await electron_notarize.notarize({
appBundleId: appId, appBundleId: appId,
appPath: appPath, appPath: appPath,
appleId: process.env.NOTARIZATION_USERNAME, appleId: process.env.appleId,
appleIdPassword: process.env.NOTARIZATION_PASSWORD, appleIdPassword: process.env.appleIdPassword,
}); });
} catch (error) { } catch (error) {
console.error(error); console.error(error);

Binary file not shown.

Binary file not shown.

View file

@ -71,7 +71,7 @@ const downloadDaemon = targetPlatform =>
}) })
) )
.then(() => { .then(() => {
console.log(`\x1b[32msuccess\x1b[0m Daemon downloaded! - v${daemonVersion}`); console.log('\x1b[32msuccess\x1b[0m Daemon downloaded!');
if (hasDaemonVersion) { if (hasDaemonVersion) {
del(daemonVersionPath); del(daemonVersionPath);
} }

View file

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>

View file

@ -51,7 +51,7 @@ function cleanUpAfterSigning() {
} }
function signFile() { function signFile() {
console.log('Logging in file with hashes using keybase...'); console.log('Signing file with hashes using keybase...');
// All files are hashed and added to hashes.txt // All files are hashed and added to hashes.txt
// Sign it with keybase // Sign it with keybase
const fileName = `LBRY_${versionToSign}_sigs.asc`; const fileName = `LBRY_${versionToSign}_sigs.asc`;
@ -126,17 +126,16 @@ function downloadAssets() {
.then(({ data }) => { .then(({ data }) => {
const release_id = data.id; const release_id = data.id;
return octokit.repos.listReleases({ return octokit.repos.listAssetsForRelease({
owner: 'lbryio', owner: 'lbryio',
repo: 'lbry-desktop', repo: 'lbry-desktop',
release_id, release_id,
}); });
}) })
.then(({ data }) => { .then(({ data }) => {
const releaseToDownload = data.filter(releaseData => releaseData.tag_name === versionToSign)[0]; fileCountToDownload = data.length;
const assets = releaseToDownload.assets;
fileCountToDownload = assets.length; data
assets
.map(({ browser_download_url, name }) => ({ download_url: browser_download_url, name })) .map(({ browser_download_url, name }) => ({ download_url: browser_download_url, name }))
.forEach(({ name, download_url }) => { .forEach(({ name, download_url }) => {
const fileName = path.resolve(__dirname, `../dist/releaseDownloads/${name}`); const fileName = path.resolve(__dirname, `../dist/releaseDownloads/${name}`);

View file

@ -1,82 +1,34 @@
// On Web, this will find .env.defaults and optional .env in web/ // On Web, this will find .env.defaults and optional .env in web/
// On Desktop App, this will find .env.defaults and optional .env in root dir // On Desktop App, this will find .env.defaults and optional .env in root dir
require('dotenv-defaults').config({ silent: false }); require('dotenv-defaults').config({silent: false});
const config = { const config = {
MATOMO_URL: process.env.MATOMO_URL, MATOMO_URL: process.env.MATOMO_URL,
MATOMO_ID: process.env.MATOMO_ID, MATOMO_ID: process.env.MATOMO_ID,
WEBPACK_WEB_PORT: process.env.WEBPACK_WEB_PORT, WEBPACK_WEB_PORT: process.env.WEBPACK_WEB_PORT,
WEBPACK_ELECTRON_PORT: process.env.WEBPACK_ELECTRON_PORT, WEBPACK_ELECTRON_PORT: process.env.WEBPACK_ELECTRON_PORT,
WEB_SERVER_PORT: process.env.WEB_SERVER_PORT, WEB_SERVER_PORT: process.env.WEB_SERVER_PORT,
LBRY_WEB_API: process.env.LBRY_WEB_API, //api.na-backend.odysee.com', LBRY_WEB_API: process.env.LBRY_WEB_API, //api.lbry.tv',
LBRY_WEB_PUBLISH_API: process.env.LBRY_WEB_PUBLISH_API, LBRY_WEB_STREAMING_API: process.env.LBRY_WEB_STREAMING_API, //cdn.lbryplayer.xyz',
LBRY_API_URL: process.env.LBRY_API_URL, //api.lbry.com',
LBRY_WEB_STREAMING_API: process.env.LBRY_WEB_STREAMING_API, //player.odysee.com
LBRY_WEB_BUFFER_API: process.env.LBRY_WEB_BUFFER_API,
SEARCH_SERVER_API: process.env.SEARCH_SERVER_API,
CLOUD_CONNECT_SITE_NAME: process.env.CLOUD_CONNECT_SITE_NAME,
COMMENT_SERVER_API: process.env.COMMENT_SERVER_API,
COMMENT_SERVER_NAME: process.env.COMMENT_SERVER_NAME,
SOCKETY_SERVER_API: process.env.SOCKETY_SERVER_API,
WELCOME_VERSION: process.env.WELCOME_VERSION, WELCOME_VERSION: process.env.WELCOME_VERSION,
DOMAIN: process.env.DOMAIN, DOMAIN: process.env.DOMAIN,
SHARE_DOMAIN_URL: process.env.SHARE_DOMAIN_URL,
URL: process.env.URL, URL: process.env.URL,
THUMBNAIL_CDN_URL: process.env.THUMBNAIL_CDN_URL,
SITE_TITLE: process.env.SITE_TITLE, SITE_TITLE: process.env.SITE_TITLE,
SITE_NAME: process.env.SITE_NAME, SITE_NAME: process.env.SITE_NAME,
SITE_DESCRIPTION: process.env.SITE_DESCRIPTION, SITE_DESCRIPTION: process.env.SITE_DESCRIPTION,
SITE_HELP_EMAIL: process.env.SITE_HELP_EMAIL,
// SOCIAL MEDIA
TWITTER_ACCOUNT: process.env.TWITTER_ACCOUNT,
// LOGO
LOGO_TITLE: process.env.LOGO_TITLE, LOGO_TITLE: process.env.LOGO_TITLE,
FAVICON: process.env.FAVICON,
LOGO: process.env.LOGO,
LOGO_TEXT_LIGHT: process.env.LOGO_TEXT_LIGHT,
LOGO_TEXT_DARK: process.env.LOGO_TEXT_DARK,
AVATAR_DEFAULT: process.env.AVATAR_DEFAULT,
MISSING_THUMB_DEFAULT: process.env.MISSING_THUMB_DEFAULT,
// OG
OG_TITLE_SUFFIX: process.env.OG_TITLE_SUFFIX, OG_TITLE_SUFFIX: process.env.OG_TITLE_SUFFIX,
OG_HOMEPAGE_TITLE: process.env.OG_HOMEPAGE_TITLE, OG_HOMEPAGE_TITLE: process.env.OG_HOMEPAGE_TITLE,
OG_IMAGE_URL: process.env.OG_IMAGE_URL, OG_IMAGE_URL: process.env.OG_IMAGE_URL,
// MASCOT
YRBL_HAPPY_IMG_URL: process.env.YRBL_HAPPY_IMG_URL,
YRBL_SAD_IMG_URL: process.env.YRBL_SAD_IMG_URL,
LOGIN_IMG_URL: process.env.LOGIN_IMG_URL,
SITE_CANONICAL_URL: process.env.SITE_CANONICAL_URL, SITE_CANONICAL_URL: process.env.SITE_CANONICAL_URL,
DEFAULT_LANGUAGE: process.env.DEFAULT_LANGUAGE, DEFAULT_LANGUAGE: process.env.DEFAULT_LANGUAGE,
AUTO_FOLLOW_CHANNELS: process.env.AUTO_FOLLOW_CHANNELS,
UNSYNCED_SETTINGS: process.env.UNSYNCED_SETTINGS,
// ENABLE FEATURES
ENABLE_COMMENT_REACTIONS: process.env.ENABLE_COMMENT_REACTIONS === 'true',
ENABLE_FILE_REACTIONS: process.env.ENABLE_FILE_REACTIONS === 'true',
ENABLE_CREATOR_REACTIONS: process.env.ENABLE_CREATOR_REACTIONS === 'true',
ENABLE_NO_SOURCE_CLAIMS: process.env.ENABLE_NO_SOURCE_CLAIMS === 'true',
ENABLE_PREROLL_ADS: process.env.ENABLE_PREROLL_ADS === 'true',
CHANNEL_STAKED_LEVEL_VIDEO_COMMENTS: process.env.CHANNEL_STAKED_LEVEL_VIDEO_COMMENTS,
CHANNEL_STAKED_LEVEL_LIVESTREAM: process.env.CHANNEL_STAKED_LEVEL_LIVESTREAM,
WEB_PUBLISH_SIZE_LIMIT_GB: process.env.WEB_PUBLISH_SIZE_LIMIT_GB,
LOADING_BAR_COLOR: process.env.LOADING_BAR_COLOR,
SIMPLE_SITE: process.env.SIMPLE_SITE === 'true',
SHOW_ADS: process.env.SHOW_ADS === 'true',
PINNED_URI_1: process.env.PINNED_URI_1, PINNED_URI_1: process.env.PINNED_URI_1,
PINNED_LABEL_1: process.env.PINNED_LABEL_1, PINNED_LABEL_1: process.env.PINNED_LABEL_1,
PINNED_URI_2: process.env.PINNED_URI_2, PINNED_URI_2: process.env.PINNED_URI_2,
PINNED_LABEL_2: process.env.PINNED_LABEL_2, PINNED_LABEL_2: process.env.PINNED_LABEL_2,
KNOWN_APP_DOMAINS: process.env.KNOWN_APP_DOMAINS ? process.env.KNOWN_APP_DOMAINS.split(',') : [],
STRIPE_PUBLIC_KEY: process.env.STRIPE_PUBLIC_KEY,
ENABLE_UI_NOTIFICATIONS: process.env.ENABLE_UI_NOTIFICATIONS === 'true',
ENABLE_MATURE: process.env.ENABLE_MATURE === 'true',
CUSTOM_HOMEPAGE: process.env.CUSTOM_HOMEPAGE === 'true',
SHOW_TAGS_INTRO: process.env.SHOW_TAGS_INTRO === 'true',
LIGHTHOUSE_DEFAULT_TYPES: process.env.LIGHTHOUSE_DEFAULT_TYPES,
BRANDED_SITE: process.env.BRANDED_SITE,
}; };
config.URL_DEV = `http://localhost:${config.WEBPACK_WEB_PORT}`;
config.URL_LOCAL = `http://localhost:${config.WEB_SERVER_PORT}`; config.URL_LOCAL = `http://localhost:${config.WEB_SERVER_PORT}`;
config.URL_DEV = `http://localhost:${config.WEBPACK_WEB_PORT}`;
module.exports = config; module.exports = config;

View file

@ -63,14 +63,14 @@ import { toCapitalCase } from 'util/string';
import React from 'react'; import React from 'react';
import Icon from 'component/common/icon'; import Icon from 'component/common/icon';
export type RowDataItem = { type RowDataItem = {
title: string, title: string,
link?: string, link?: string,
help?: any, help?: any,
options?: {}, options?: {},
}; };
export default function GetHomePageRowData( export default function getHomePageRowData(
authenticated: boolean, authenticated: boolean,
showPersonalizedChannels: boolean, showPersonalizedChannels: boolean,
showPersonalizedTags: boolean, showPersonalizedTags: boolean,

View file

@ -1,2 +0,0 @@
User-agent: *
Allow: /

View file

@ -1,2 +0,0 @@
User-agent: *
Disallow: /

9
dangerfile.js Normal file
View file

@ -0,0 +1,9 @@
import { danger, warn } from 'danger';
// Check for a CHANGELOG entry
const hasChangelog = danger.git.modified_files.some(f => f === 'CHANGELOG.md');
if (!hasChangelog) {
const title = ':page_facing_up: Changelog Entry Missing';
const idea = 'Please add a changelog entry for your changes.';
warn(`${title} - <i>${idea}</i>`);
}

View file

@ -20,6 +20,11 @@
"to": "static/daemon/", "to": "static/daemon/",
"filter": ["**/*"] "filter": ["**/*"]
}, },
{
"from": "./static/lbry-first/",
"to": "static/lbry-first/",
"filter": ["**/*"]
},
{ {
"from": "./static/img", "from": "./static/img",
"to": "static/img", "to": "static/img",
@ -29,10 +34,6 @@
"from": "./static/font", "from": "./static/font",
"to": "static/font", "to": "static/font",
"filter": ["**/*"] "filter": ["**/*"]
},
{
"from": "./static/app-update.yml",
"to": "app-update.yml"
} }
], ],
"publish": [ "publish": [
@ -41,11 +42,7 @@
} }
], ],
"mac": { "mac": {
"category": "public.app-category.entertainment", "category": "public.app-category.entertainment"
"entitlements": "build/entitlements.mac.plist",
"entitlementsInherit": "build/entitlements.mac.plist",
"hardenedRuntime" : true,
"gatekeeperAssess": false
}, },
"dmg": { "dmg": {
"iconSize": 128, "iconSize": 128,
@ -85,7 +82,7 @@
} }
}, },
"deb": { "deb": {
"depends": ["gconf2", "gconf-service", "libnotify4", "libxtst6", "libnss3"] "depends": ["gconf2", "gconf-service", "libnotify4", "libappindicator1", "libxtst6", "libnss3"]
}, },
"nsis": { "nsis": {
"perMachine": true, "perMachine": true,
@ -96,7 +93,7 @@
"target": [ "target": [
{ {
"target": "nsis", "target": "nsis",
"arch": ["x64"] "arch": ["x64", "ia32"]
} }
] ]
}, },

View file

@ -1,7 +1,7 @@
import path from 'path'; import path from 'path';
import fs from 'fs'; import fs from 'fs';
import { spawn, execSync } from 'child_process'; import { spawn, execSync } from 'child_process';
import Lbry from 'lbry'; import { Lbry } from 'lbry-redux';
export default class Daemon { export default class Daemon {
static lbrynetPath = static lbrynetPath =

View file

@ -6,26 +6,6 @@ let tray;
export default window => { export default window => {
let iconPath; let iconPath;
/*
* A maximized window can't be properly
* restored when minimized to the taskbar
* (it will be restored/showed as unmaximized).
*
* window.isMaximized() will also return
* false when minimizing a maximized window.
*
* The safest way to keep track of the
* maximized state using maximize and
* unmaximize events.
*/
let isWindowMaximized = false;
window.on('maximize', () => {
isWindowMaximized = true;
});
window.on('unmaximize', () => {
isWindowMaximized = false;
});
switch (process.platform) { switch (process.platform) {
case 'darwin': { case 'darwin': {
iconPath = 'static/img/tray/mac/trayTemplate.png'; iconPath = 'static/img/tray/mac/trayTemplate.png';
@ -42,21 +22,18 @@ export default window => {
tray = new Tray(process.env.NODE_ENV === 'development' ? iconPath : path.join(process.resourcesPath, iconPath)); tray = new Tray(process.env.NODE_ENV === 'development' ? iconPath : path.join(process.resourcesPath, iconPath));
const restoreFromTray = () => { tray.on('double-click', () => {
if (isWindowMaximized) {
window.maximize();
}
window.show(); window.show();
}; });
tray.on('double-click', restoreFromTray);
tray.setToolTip('LBRY App'); tray.setToolTip('LBRY App');
const template = [ const template = [
{ {
label: `Open ${app.name}`, label: `Open ${app.getName()}`,
click: restoreFromTray, click: () => {
window.show();
},
}, },
{ role: 'quit' }, { role: 'quit' },
]; ];

View file

@ -1,27 +1,23 @@
import { WEBPACK_ELECTRON_PORT } from 'config'; import { WEBPACK_ELECTRON_PORT } from 'config';
import { app, BrowserWindow, dialog, screen, nativeImage } from 'electron'; import { app, BrowserWindow, dialog, shell, screen } from 'electron';
import isDev from 'electron-is-dev'; import isDev from 'electron-is-dev';
import windowStateKeeper from 'electron-window-state'; import windowStateKeeper from 'electron-window-state';
import SUPPORTED_LANGUAGES from 'constants/supported_languages'; import SUPPORTED_LANGUAGES from 'constants/supported_languages';
import { SUPPORTED_SUB_LANGUAGE_CODES, SUB_LANG_CODE_LEN } from 'constants/supported_sub_languages'; import { SUPPORTED_SUB_LANGUAGE_CODES, SUB_LANG_CODE_LEN } from 'constants/supported_sub_languages';
import SUPPORTED_BROWSER_LANGUAGES from 'constants/supported_browser_languages';
import { TO_TRAY_WHEN_CLOSED } from 'constants/settings';
import setupBarMenu from './menu/setupBarMenu'; import setupBarMenu from './menu/setupBarMenu';
import * as PAGES from 'constants/pages'; import * as PAGES from 'constants/pages';
const remote = require('@electron/remote/main');
const shell = require('electron').shell;
function GetAppLangCode() { function GetAppLangCode() {
// https://www.electronjs.org/docs/api/locales
// 1. Gets the user locale. // 1. Gets the user locale.
// 2. Converts unsupported sub-languages to its primary (e.g. "en-GB" -> "en"). // 2. Converts unsupported sub-languages to its primary (e.g. "en-GB" -> "en").
// Note that the primary itself may or may not be a supported language // Note that the primary itself may or may not be a supported language
// (up to clients to verify against SUPPORTED_LANGUAGES). // (up to clients to verify against SUPPORTED_LANGUAGES).
const langCode = app.getLocale(); const langCode = app.getLocale();
if (langCode.length === SUB_LANG_CODE_LEN && !SUPPORTED_SUB_LANGUAGE_CODES.includes(langCode)) { if (langCode.length === SUB_LANG_CODE_LEN && !SUPPORTED_SUB_LANGUAGE_CODES.includes(langCode)) {
return SUPPORTED_BROWSER_LANGUAGES[langCode.slice(0, 2)]; return langCode.slice(0, 2);
} }
return SUPPORTED_BROWSER_LANGUAGES[langCode]; return langCode;
} }
export default appState => { export default appState => {
@ -49,14 +45,11 @@ export default appState => {
// If state is undefined, create window as maximized. // If state is undefined, create window as maximized.
width: windowState.width === undefined ? width : windowState.width, width: windowState.width === undefined ? width : windowState.width,
height: windowState.height === undefined ? height : windowState.height, height: windowState.height === undefined ? height : windowState.height,
icon: nativeImage.createFromPath('static/img/tray/default/tray.png'), icon: 'static/img/tray/default/tray.png',
webPreferences: { webPreferences: {
// Disable renderer process's webSecurity on development to enable CORS. // Disable renderer process's webSecurity on development to enable CORS.
webSecurity: !isDev, webSecurity: !isDev,
plugins: true, plugins: true,
nodeIntegration: true,
contextIsolation: false,
enableRemoteModule: true, // see about removing this
}, },
}; };
const lbryProto = 'lbry://'; const lbryProto = 'lbry://';
@ -64,7 +57,6 @@ export default appState => {
const rendererURL = isDev ? `http://localhost:${WEBPACK_ELECTRON_PORT}` : `file://${__dirname}/index.html`; const rendererURL = isDev ? `http://localhost:${WEBPACK_ELECTRON_PORT}` : `file://${__dirname}/index.html`;
let window = new BrowserWindow(windowConfiguration); let window = new BrowserWindow(windowConfiguration);
remote.enable(window.webContents);
// Let us register listeners on the window, so we can update the state // Let us register listeners on the window, so we can update the state
// automatically (the listeners will be removed when the window is closed) // automatically (the listeners will be removed when the window is closed)
@ -95,7 +87,7 @@ export default appState => {
// is it a lbry://? pointing to an app page // is it a lbry://? pointing to an app page
if (deepLinkingURI.includes(lbryProtoQ)) { if (deepLinkingURI.includes(lbryProtoQ)) {
let path = deepLinkingURI.slice(lbryProtoQ.length); let path = deepLinkingURI.substr(lbryProtoQ.length);
let page = path.indexOf('?') >= 0 ? path.substring(0, path.indexOf('?')) : path; let page = path.indexOf('?') >= 0 ? path.substring(0, path.indexOf('?')) : path;
if (Object.values(PAGES).includes(page)) { if (Object.values(PAGES).includes(page)) {
deepLinkingURI = deepLinkingURI.replace(lbryProtoQ, '#/$/'); deepLinkingURI = deepLinkingURI.replace(lbryProtoQ, '#/$/');
@ -114,11 +106,7 @@ export default appState => {
window.loadURL(rendererURL + deepLinkingURI); window.loadURL(rendererURL + deepLinkingURI);
window.on('close', event => { window.on('close', event => {
if (appState.isQuitting) { if (!appState.isQuitting && !appState.autoUpdateAccepted) {
return;
}
if (!appState.autoUpdateAccepted) {
event.preventDefault(); event.preventDefault();
if (window.isFullScreen()) { if (window.isFullScreen()) {
window.once('leave-full-screen', () => { window.once('leave-full-screen', () => {
@ -129,16 +117,6 @@ export default appState => {
window.hide(); window.hide();
} }
} }
const getToTrayWhenClosedSetting = window.webContents.executeJavaScript(`localStorage.getItem('${TO_TRAY_WHEN_CLOSED}')`);
getToTrayWhenClosedSetting.then(toTrayWhenClosedSetting => {
const closeApp = toTrayWhenClosedSetting === 'false';
if (closeApp) {
app.quit();
}
});
}); });
window.on('focus', () => { window.on('focus', () => {
@ -184,19 +162,19 @@ export default appState => {
window.webContents.send('language-set', language); window.webContents.send('language-set', language);
} }
}); });
if (isDev) {
window.webContents.openDevTools();
}
}); });
window.webContents.on('crashed', () => { window.webContents.on('crashed', () => {
window = null; window = null;
}); });
window.webContents.setWindowOpenHandler((details) => { window.webContents.on('new-window', (event, url) => {
// Only open http and https links to prevent event.preventDefault();
// security issues. shell.openExternal(url);
if (['https:', 'http:'].includes(new URL(details.url).protocol)) {
shell.openExternal(details.url);
}
return { action: 'deny' };
}); });
window.webContents.on('update-target-url', (event, url) => { window.webContents.on('update-target-url', (event, url) => {

View file

@ -4,9 +4,9 @@
import '@babel/polyfill'; import '@babel/polyfill';
import SemVer from 'semver'; import SemVer from 'semver';
import https from 'https'; import https from 'https';
import { app, dialog, ipcMain, session, shell, BrowserWindow } from 'electron'; import { app, dialog, ipcMain, session, shell, ipcRenderer } from 'electron';
import { autoUpdater } from 'electron-updater'; import { autoUpdater } from 'electron-updater';
import Lbry from 'lbry'; import { Lbry, LbryFirst } from 'lbry-redux';
import LbryFirstInstance from './LbryFirstInstance'; import LbryFirstInstance from './LbryFirstInstance';
import Daemon from './Daemon'; import Daemon from './Daemon';
import isDev from 'electron-is-dev'; import isDev from 'electron-is-dev';
@ -17,17 +17,6 @@ import startSandbox from './startSandbox';
import installDevtools from './installDevtools'; import installDevtools from './installDevtools';
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import { diskSpaceLinux, diskSpaceWindows, diskSpaceMac } from '../ui/util/diskspace';
const { download } = require('electron-dl');
const mime = require('mime');
const remote = require('@electron/remote/main');
const os = require('os');
const sudo = require('sudo-prompt');
const probe = require('ffmpeg-probe');
const MAX_IPC_SEND_BUFFER_SIZE = 500000000; // large files crash when serialized for ipc message
remote.initialize();
const filePath = path.join(process.resourcesPath, 'static', 'upgradeDisabled'); const filePath = path.join(process.resourcesPath, 'static', 'upgradeDisabled');
let upgradeDisabled; let upgradeDisabled;
try { try {
@ -37,18 +26,11 @@ try {
upgradeDisabled = false; upgradeDisabled = false;
} }
autoUpdater.autoDownload = !upgradeDisabled; autoUpdater.autoDownload = !upgradeDisabled;
autoUpdater.allowPrerelease = false;
const UPDATE_STATE_INIT = 0; // This is set to true if an auto update has been downloaded through the Electron
const UPDATE_STATE_CHECKING = 1; // auto-update system and is ready to install. If the user declined an update earlier,
const UPDATE_STATE_UPDATES_FOUND = 2; // it will still install on shutdown.
const UPDATE_STATE_NO_UPDATES_FOUND = 3; let autoUpdateDownloaded = false;
const UPDATE_STATE_DOWNLOADING = 4;
const UPDATE_STATE_DOWNLOADED = 5;
let updateState = UPDATE_STATE_INIT;
let updateDownloadItem;
const isAutoUpdateSupported = ['win32', 'darwin'].includes(process.platform) || !!process.env.APPIMAGE;
// This is used to keep track of whether we are showing the special dialog // This is used to keep track of whether we are showing the special dialog
// that we show on Windows after you decline an upgrade and close the app later. // that we show on Windows after you decline an upgrade and close the app later.
@ -63,21 +45,11 @@ let daemon;
let lbryFirst; let lbryFirst;
const appState = {}; const appState = {};
const PROTOCOL = 'lbry';
if (isDev && process.platform === 'win32') { app.setAsDefaultProtocolClient('lbry');
// Setting this is required to get this working in dev mode. app.setName('LBRY');
app.setAsDefaultProtocolClient(PROTOCOL, process.execPath, [
path.resolve(process.argv[1]),
]);
} else if (process.platform !== 'linux') {
app.setAsDefaultProtocolClient(PROTOCOL);
}
app.name = 'LBRY';
app.setAppUserModelId('io.lbry.LBRY'); app.setAppUserModelId('io.lbry.LBRY');
app.commandLine.appendSwitch('force-color-profile', 'srgb'); app.commandLine.appendSwitch('force-color-profile', 'srgb');
app.commandLine.appendSwitch('disable-features', 'OutOfBlinkCors');
if (isDev) { if (isDev) {
// Disable security warnings in dev mode: // Disable security warnings in dev mode:
@ -173,15 +145,9 @@ if (!gotSingleInstanceLock) {
app.on('second-instance', (event, argv) => { app.on('second-instance', (event, argv) => {
// Send the url to the app to navigate first, then focus // Send the url to the app to navigate first, then focus
if (rendererWindow) { if (rendererWindow) {
// External uri (last item on argv): if ((process.platform === 'win32' || process.platform === 'linux') && String(argv[1]).startsWith('lbry')) {
const EXTERNAL_URI = (argv.length) ? argv[argv.length - 1] : ''; let URI = argv[1];
// Handle protocol requests for windows and linux
const platforms = (process.platform === 'win32' || process.platform === 'linux');
// Is LBRY protocol
const isProtocolURI = String(EXTERNAL_URI).startsWith(PROTOCOL + '://');
// External protocol requested:
if (platforms && isProtocolURI) {
let URI = EXTERNAL_URI;
// Keep only command line / deep linked arguments // Keep only command line / deep linked arguments
// Windows normalizes URIs when they're passed in from other apps. On Windows, this tries to // Windows normalizes URIs when they're passed in from other apps. On Windows, this tries to
// restore the original URI that was typed. // restore the original URI that was typed.
@ -210,6 +176,7 @@ if (!gotSingleInstanceLock) {
if (isDev) { if (isDev) {
await installDevtools(); await installDevtools();
} }
rendererWindow = createWindow(appState); rendererWindow = createWindow(appState);
tray = createTray(rendererWindow); tray = createTray(rendererWindow);
@ -220,6 +187,21 @@ if (!gotSingleInstanceLock) {
}); });
} }
// HACK: patch webrequest to fix devtools incompatibility with electron 2.x.
// See https://github.com/electron/electron/issues/13008#issuecomment-400261941
session.defaultSession.webRequest.onBeforeRequest({}, (details, callback) => {
if (details.url.includes('7accc8730b0f99b5e7c0702ea89d1fa7c17bfe33')) {
callback({
redirectURL: details.url.replace(
'7accc8730b0f99b5e7c0702ea89d1fa7c17bfe33',
'57c9d07b416b5a2ea23d28247300e4af36329bdc'
),
});
} else {
callback({ cancel: false });
}
});
// If an "Origin" header is passed, the SDK will check that it is set to allow that origin in the daemon_settings.yml // If an "Origin" header is passed, the SDK will check that it is set to allow that origin in the daemon_settings.yml
// By default, electron sends http://localhost:{port} as the origin for POST requests // By default, electron sends http://localhost:{port} as the origin for POST requests
// https://github.com/electron/electron/issues/7931#issuecomment-361759277 // https://github.com/electron/electron/issues/7931#issuecomment-361759277
@ -241,8 +223,7 @@ app.on('activate', () => {
app.on('will-quit', event => { app.on('will-quit', event => {
if ( if (
process.platform === 'win32' && process.platform === 'win32' &&
updateState === UPDATE_STATE_DOWNLOADED && autoUpdateDownloaded &&
isAutoUpdateSupported &&
!appState.autoUpdateAccepted && !appState.autoUpdateAccepted &&
!showingAutoUpdateCloseAlert !showingAutoUpdateCloseAlert
) { ) {
@ -268,7 +249,6 @@ app.on('will-quit', event => {
} }
appState.isQuitting = true; appState.isQuitting = true;
if (daemon) { if (daemon) {
daemon.quit(); daemon.quit();
event.preventDefault(); event.preventDefault();
@ -279,7 +259,6 @@ app.on('will-quit', event => {
} }
if (rendererWindow) { if (rendererWindow) {
tray.destroy();
rendererWindow = null; rendererWindow = null;
} }
}); });
@ -302,118 +281,27 @@ app.on('before-quit', () => {
appState.isQuitting = true; appState.isQuitting = true;
}); });
// Get the content of a file as a raw buffer of bytes. ipcMain.on('upgrade', (event, installerPath) => {
// Useful to convert a file path to a File instance. app.on('quit', () => {
// Example: console.log('Launching upgrade installer at', installerPath);
// const result = await ipcMain.invoke('get-file-from-path', 'path/to/file'); // This gets triggered called after *all* other quit-related events, so
// const file = new File([result.buffer], result.name); // we'll only get here if we're fully prepared and quitting for real.
// NOTE: if path points to a folder, an empty shell.openItem(installerPath);
// file will be given.
ipcMain.handle('get-file-from-path', (event, path, readContents = true) => {
return new Promise((resolve, reject) => {
fs.stat(path, (error, stats) => {
if (error) {
reject(error);
return;
}
// Separate folders considering "\" and "/"
// as separators (cross platform)
const folders = path.split(/[\\/]/);
const name = folders[folders.length - 1];
if (stats.isDirectory()) {
resolve({
name,
mime: undefined,
path,
buffer: new ArrayBuffer(0),
});
return;
}
if (!readContents) {
resolve({
name,
mime: mime.getType(name) || undefined,
path,
buffer: new ArrayBuffer(0),
});
return;
}
// Encoding null ensures data results in a Buffer.
fs.readFile(path, { encoding: null }, (err, data) => {
if (err) {
reject(err);
return;
}
resolve({
name,
mime: mime.getType(name) || undefined,
path,
buffer: data,
});
});
});
}); });
// what to do if no shutdown in a long time?
console.log('Update downloaded to', installerPath);
console.log('The app will close and you will be prompted to install the latest version of LBRY.');
console.log('After the install is complete, please reopen the app.');
app.quit();
}); });
ipcMain.handle('get-file-details-from-path', async (event, path) => { autoUpdater.on('update-downloaded', () => {
const isFfMp4 = (ffprobeResults) => { autoUpdateDownloaded = true;
return ffprobeResults &&
ffprobeResults.format &&
ffprobeResults.format.format_name &&
ffprobeResults.format.format_name.includes('mp4');
};
const folders = path.split(/[\\/]/);
const name = folders[folders.length - 1];
let duration = 0, size = 0, mimeType;
try {
await fs.promises.stat(path);
let ffprobeResults;
try {
ffprobeResults = await probe(path);
duration = ffprobeResults.format.duration;
size = ffprobeResults.format.size;
} catch (e) {
}
let fileReadResult;
if (size < MAX_IPC_SEND_BUFFER_SIZE) {
try {
fileReadResult = await fs.promises.readFile(path);
} catch (e) {
}
}
// TODO: use mmmagic to inspect file and get mime type
mimeType = isFfMp4(ffprobeResults) ? 'video/mp4' : mime.getType(name);
const fileData = {name, mime: mimeType || undefined, path, duration: duration, size, buffer: fileReadResult };
return fileData;
} catch (e) {
// no stat
return { error: 'no file' };
}
}); });
ipcMain.on('get-disk-space', async (event) => { ipcMain.on('autoUpdateAccepted', () => {
try { appState.autoUpdateAccepted = true;
const { data_dir } = await Lbry.settings_get(); autoUpdater.quitAndInstall();
let diskSpace;
switch (os.platform()) {
case 'linux':
diskSpace = await diskSpaceLinux(data_dir);
break;
case 'darwin':
diskSpace = await diskSpaceMac(data_dir);
break;
case 'win32':
diskSpace = await diskSpaceWindows(data_dir);
break;
default:
throw new Error('unknown platform');
}
rendererWindow.webContents.send('send-disk-space', { diskSpace });
} catch (e) {
rendererWindow.webContents.send('send-disk-space', { error: e.message || e });
console.log('Failed to get disk space', e);
}
}); });
ipcMain.on('version-info-requested', () => { ipcMain.on('version-info-requested', () => {
@ -508,162 +396,3 @@ process.on('uncaughtException', error => {
if (daemon) daemon.quit(); if (daemon) daemon.quit();
app.exit(1); app.exit(1);
}); });
// Auto updater
autoUpdater.on('download-progress', () => {
updateState = UPDATE_STATE_DOWNLOADING;
});
autoUpdater.on('update-downloaded', () => {
updateState = UPDATE_STATE_DOWNLOADED;
// If this download was trigger by
// autoUpdateAccepted it means, the user
// wants to install the new update but
// needed to downloaded the files first.
if (appState.autoUpdateAccepted) {
autoUpdater.quitAndInstall();
}
});
autoUpdater.on('update-available', () => {
if (updateState === UPDATE_STATE_DOWNLOADING) {
return;
}
updateState = UPDATE_STATE_UPDATES_FOUND;
});
autoUpdater.on('update-not-available', () => {
updateState = UPDATE_STATE_NO_UPDATES_FOUND;
});
autoUpdater.on('error', () => {
if (updateState === UPDATE_STATE_DOWNLOADING) {
updateState = UPDATE_STATE_UPDATES_FOUND;
return;
}
updateState = UPDATE_STATE_INIT;
});
// Manual (.deb) update
ipcMain.on('cancel-download-upgrade', () => {
if (updateDownloadItem) {
// Cancel the download and execute the onCancel
// callback set in the options.
updateDownloadItem.cancel();
}
});
ipcMain.on('download-upgrade', (event, params) => {
if (updateState !== UPDATE_STATE_UPDATES_FOUND) {
return;
}
if (isAutoUpdateSupported) {
updateState = UPDATE_STATE_DOWNLOADING;
autoUpdater.downloadUpdate();
return;
}
const { url, options } = params;
const dir = fs.mkdtempSync(app.getPath('temp') + path.sep);
updateState = UPDATE_STATE_DOWNLOADING;
// Grab the download item's handler to allow
// cancelling the operation if required.
options.onStarted = function(downloadItem) {
updateDownloadItem = downloadItem;
};
options.onCancel = function() {
updateState = UPDATE_STATE_UPDATES_FOUND;
updateDownloadItem = undefined;
};
options.onProgress = function(p) {
rendererWindow.webContents.send('download-progress-update', p);
};
options.onCompleted = function(c) {
updateState = UPDATE_STATE_DOWNLOADED;
updateDownloadItem = undefined;
rendererWindow.webContents.send('download-update-complete', c);
};
options.directory = dir;
const win = BrowserWindow.getFocusedWindow();
download(win, url, options).catch(e => {
updateState = UPDATE_STATE_UPDATES_FOUND;
console.log('e', e);
});
});
// Update behavior
ipcMain.on('autoUpdateAccepted', () => {
appState.autoUpdateAccepted = true;
// quitAndInstall can only be called if the
// update has been downloaded. Since the user
// can disable auto updates, we have to make
// sure it has been downloaded first.
if (updateState === UPDATE_STATE_DOWNLOADED) {
autoUpdater.quitAndInstall();
return;
}
if (updateState !== UPDATE_STATE_UPDATES_FOUND) {
return;
}
// If the update hasn't been downloaded,
// start downloading it. After it's done, the
// event 'update-downloaded' will be triggered,
// where we will be able to resume the
// update installation.
updateState = UPDATE_STATE_DOWNLOADING;
autoUpdater.downloadUpdate();
});
ipcMain.on('check-for-updates', (event, autoDownload) => {
if (![UPDATE_STATE_INIT, UPDATE_STATE_NO_UPDATES_FOUND].includes(updateState)) {
return;
}
updateState = UPDATE_STATE_CHECKING;
// If autoDownload is true, checkForUpdates will begin the
// download automatically.
if (autoDownload) {
updateState = UPDATE_STATE_DOWNLOADING;
}
autoUpdater.autoDownload = autoDownload;
autoUpdater.checkForUpdates();
});
ipcMain.on('upgrade', (event, installerPath) => {
// what to do if no shutdown in a long time?
console.log('Update downloaded to', installerPath);
console.log('The app will close and you will be prompted to install the latest version of LBRY.');
console.log('After the install is complete, please reopen the app.');
// Prevent .deb package from opening with archive manager (Ubuntu >= 20)
if (process.platform === 'linux' && !process.env.APPIMAGE) {
sudo.exec(`dpkg -i ${installerPath}`, { name: app.name }, (err, stdout, stderr) => {
if (err || stderr) {
rendererWindow.webContents.send('upgrade-installing-error');
return;
}
// Re-launch the application when the installation finishes.
app.relaunch();
app.quit();
});
return;
}
app.on('quit', () => {
console.log('Launching upgrade installer at', installerPath);
// This gets triggered called after *all* other quit-related events, so
// we'll only get here if we're fully prepared and quitting for real.
shell.openPath(installerPath);
});
app.quit();
});

View file

@ -1,5 +1,4 @@
import { app, Menu, shell } from 'electron'; import { app, Menu, shell } from 'electron';
import { ZOOM } from 'util/zoomWindow';
export default () => { export default () => {
const template = [ const template = [
@ -23,38 +22,6 @@ export default () => {
label: 'View', label: 'View',
submenu: [ submenu: [
{ role: 'reload' }, { role: 'reload' },
{
label: 'Zoom',
submenu: [
{
label: 'Zoom In',
accelerator: 'CmdOrCtrl+=',
click: (menuItem, browserWindow) => {
if (browserWindow) {
browserWindow.webContents.send('zoom-window', ZOOM.INCREMENT);
}
},
},
{
label: 'Zoom Out',
accelerator: 'CmdOrCtrl+-',
click: (menuItem, browserWindow) => {
if (browserWindow) {
browserWindow.webContents.send('zoom-window', ZOOM.DECREMENT);
}
},
},
{
label: 'Reset Zoom',
accelerator: 'CmdOrCtrl+0',
click: (menuItem, browserWindow) => {
if (browserWindow) {
browserWindow.webContents.send('zoom-window', ZOOM.RESET);
}
},
},
],
},
{ {
label: 'Developer', label: 'Developer',
submenu: [{ role: 'forcereload' }, { role: 'toggledevtools' }], submenu: [{ role: 'forcereload' }, { role: 'toggledevtools' }],
@ -105,7 +72,7 @@ export default () => {
]; ];
const darwinTemplateAddition = { const darwinTemplateAddition = {
label: app.name, label: app.getName(),
submenu: [ submenu: [
{ role: 'about' }, { role: 'about' },
{ type: 'separator' }, { type: 'separator' },

View file

@ -1,4 +1,5 @@
// import express from 'express'; import express from 'express';
import unpackByOutpoint from './unpackByOutpoint';
// Polyfills and `lbry-redux` // Polyfills and `lbry-redux`
global.fetch = require('node-fetch'); global.fetch = require('node-fetch');
@ -7,31 +8,31 @@ if (typeof global.fetch === 'object') {
global.fetch = global.fetch.default; global.fetch = global.fetch.default;
} }
// const Lbry = require('lbry'); const { Lbry } = require('lbry-redux');
delete global.window; delete global.window;
export default async function startSandbox() { export default async function startSandbox() {
// const port = 5278; const port = 5278;
// const sandbox = express(); const sandbox = express();
// sandbox.get('/set/:outpoint', async (req, res) => { sandbox.get('/set/:outpoint', async (req, res) => {
// const { outpoint } = req.params; const { outpoint } = req.params;
//
// const resolvedPath = await unpackByOutpoint(Lbry, outpoint); const resolvedPath = await unpackByOutpoint(Lbry, outpoint);
//
// sandbox.use(`/sandbox/${outpoint}/`, express.static(resolvedPath)); sandbox.use(`/sandbox/${outpoint}/`, express.static(resolvedPath));
//
// res.send(`/sandbox/${outpoint}/`); res.send(`/sandbox/${outpoint}/`);
// }); });
//
// sandbox sandbox
// .listen(port, 'localhost', () => console.log(`Sandbox listening on port ${port}.`)) .listen(port, 'localhost', () => console.log(`Sandbox listening on port ${port}.`))
// .on('error', err => { .on('error', err => {
// if (err.code === 'EADDRINUSE') { if (err.code === 'EADDRINUSE') {
// console.log( console.log(
// `Server already listening at localhost:${port}. This is probably another LBRY app running. If not, games in the app will not work.` `Server already listening at localhost:${port}. This is probably another LBRY app running. If not, games in the app will not work.`
// ); );
// } }
// }); });
} }

View file

@ -0,0 +1,23 @@
import fs from 'fs';
import path from 'path';
import { unpackDirectory } from 'lbry-format';
async function unpackByOutpoint(lbry, outpoint) {
const { items: claimFiles } = await lbry.file_list({ outpoint, full_status: true, page: 1, page_size: 1 });
if (claimFiles && claimFiles.length) {
const claimFileInfo = claimFiles[0];
const packFilePath = path.resolve(claimFileInfo.download_path);
const unpackPath = path.normalize(path.join(claimFileInfo.download_directory, claimFileInfo.claim_name));
if (!fs.existsSync(unpackPath)) {
await unpackDirectory(unpackPath, {
fileName: packFilePath,
});
}
return unpackPath;
}
}
export default unpackByOutpoint;

View file

@ -1,184 +0,0 @@
// @flow
/*
LBRY FIRST does not work due to api changes
*/
import 'proxy-polyfill';
const CHECK_LBRYFIRST_STARTED_TRY_NUMBER = 200;
//
// Basic LBRYFIRST connection config
// Offers a proxy to call LBRYFIRST methods
//
const LbryFirst: LbryFirstTypes = {
isConnected: false,
connectPromise: null,
lbryFirstConnectionString: 'http://localhost:1337/rpc',
apiRequestHeaders: { 'Content-Type': 'application/json' },
// Allow overriding lbryFirst connection string (e.g. to `/api/proxy` for lbryweb)
setLbryFirstConnectionString: (value: string) => {
LbryFirst.lbryFirstConnectionString = value;
},
setApiHeader: (key: string, value: string) => {
LbryFirst.apiRequestHeaders = Object.assign(LbryFirst.apiRequestHeaders, { [key]: value });
},
unsetApiHeader: key => {
Object.keys(LbryFirst.apiRequestHeaders).includes(key) &&
delete LbryFirst.apiRequestHeaders['key'];
},
// Allow overriding Lbry methods
overrides: {},
setOverride: (methodName, newMethod) => {
LbryFirst.overrides[methodName] = newMethod;
},
getApiRequestHeaders: () => LbryFirst.apiRequestHeaders,
// LbryFirst Methods
status: (params = {}) => lbryFirstCallWithResult('status', params),
stop: () => lbryFirstCallWithResult('stop', {}),
version: () => lbryFirstCallWithResult('version', {}),
// Upload to youtube
upload: (params: { title: string, description: string, file_path: ?string } = {}) => {
// Only upload when originally publishing for now
if (!params.file_path) {
return Promise.resolve();
}
const uploadParams: {
Title: string,
Description: string,
FilePath: string,
Category: string,
Keywords: string,
} = {
Title: params.title,
Description: params.description,
FilePath: params.file_path,
Category: '',
Keywords: '',
};
return lbryFirstCallWithResult('youtube.Upload', uploadParams);
},
hasYTAuth: (token: string) => {
const hasYTAuthParams = {};
hasYTAuthParams.AuthToken = token;
return lbryFirstCallWithResult('youtube.HasAuth', hasYTAuthParams);
},
ytSignup: () => {
const emptyParams = {};
return lbryFirstCallWithResult('youtube.Signup', emptyParams);
},
remove: () => {
const emptyParams = {};
return lbryFirstCallWithResult('youtube.Remove', emptyParams);
},
// Connect to lbry-first
connect: () => {
if (LbryFirst.connectPromise === null) {
LbryFirst.connectPromise = new Promise((resolve, reject) => {
let tryNum = 0;
// Check every half second to see if the lbryFirst is accepting connections
function checkLbryFirstStarted() {
tryNum += 1;
LbryFirst.status()
.then(resolve)
.catch(() => {
if (tryNum <= CHECK_LBRYFIRST_STARTED_TRY_NUMBER) {
setTimeout(checkLbryFirstStarted, tryNum < 50 ? 400 : 1000);
} else {
reject(new Error('Unable to connect to LBRY'));
}
});
}
checkLbryFirstStarted();
});
}
// Flow thinks this could be empty, but it will always return a promise
// $FlowFixMe
return LbryFirst.connectPromise;
},
};
function checkAndParse(response) {
if (response.status >= 200 && response.status < 300) {
return response.json();
}
return response.json().then(json => {
let error;
if (json.error) {
const errorMessage = typeof json.error === 'object' ? json.error.message : json.error;
error = new Error(errorMessage);
} else {
error = new Error('Protocol error with unknown response signature');
}
return Promise.reject(error);
});
}
export function apiCall(method: string, params: ?{}, resolve: Function, reject: Function) {
const counter = new Date().getTime();
const paramsArray = [params];
const options = {
method: 'POST',
headers: LbryFirst.apiRequestHeaders,
body: JSON.stringify({
jsonrpc: '2.0',
method,
params: paramsArray,
id: counter,
}),
};
return fetch(LbryFirst.lbryFirstConnectionString, options)
.then(checkAndParse)
.then(response => {
const error = response.error || (response.result && response.result.error);
if (error) {
return reject(error);
}
return resolve(response.result);
})
.catch(reject);
}
function lbryFirstCallWithResult(name: string, params: ?{} = {}) {
return new Promise((resolve, reject) => {
apiCall(
name,
params,
result => {
resolve(result);
},
reject
);
});
}
// This is only for a fallback
// If there is a LbryFirst method that is being called by an app, it should be added to /flow-typed/LbryFirst.js
const lbryFirstProxy = new Proxy(LbryFirst, {
get(target: LbryFirstTypes, name: string) {
if (name in target) {
return target[name];
}
return (params = {}) =>
new Promise((resolve, reject) => {
apiCall(name, params, resolve, reject);
});
},
});
export default lbryFirstProxy;

View file

@ -1,97 +0,0 @@
// Claims
export const FETCH_FEATURED_CONTENT_STARTED = 'FETCH_FEATURED_CONTENT_STARTED';
export const FETCH_FEATURED_CONTENT_COMPLETED = 'FETCH_FEATURED_CONTENT_COMPLETED';
export const FETCH_TRENDING_CONTENT_STARTED = 'FETCH_TRENDING_CONTENT_STARTED';
export const FETCH_TRENDING_CONTENT_COMPLETED = 'FETCH_TRENDING_CONTENT_COMPLETED';
export const RESOLVE_URIS_STARTED = 'RESOLVE_URIS_STARTED';
export const RESOLVE_URIS_COMPLETED = 'RESOLVE_URIS_COMPLETED';
export const FETCH_CHANNEL_CLAIMS_STARTED = 'FETCH_CHANNEL_CLAIMS_STARTED';
export const FETCH_CHANNEL_CLAIMS_COMPLETED = 'FETCH_CHANNEL_CLAIMS_COMPLETED';
export const FETCH_CHANNEL_CLAIM_COUNT_STARTED = 'FETCH_CHANNEL_CLAIM_COUNT_STARTED';
export const FETCH_CHANNEL_CLAIM_COUNT_COMPLETED = 'FETCH_CHANNEL_CLAIM_COUNT_COMPLETED';
export const FETCH_CLAIM_LIST_MINE_STARTED = 'FETCH_CLAIM_LIST_MINE_STARTED';
export const FETCH_CLAIM_LIST_MINE_COMPLETED = 'FETCH_CLAIM_LIST_MINE_COMPLETED';
export const ABANDON_CLAIM_STARTED = 'ABANDON_CLAIM_STARTED';
export const ABANDON_CLAIM_SUCCEEDED = 'ABANDON_CLAIM_SUCCEEDED';
export const FETCH_CHANNEL_LIST_STARTED = 'FETCH_CHANNEL_LIST_STARTED';
export const FETCH_CHANNEL_LIST_COMPLETED = 'FETCH_CHANNEL_LIST_COMPLETED';
export const CREATE_CHANNEL_STARTED = 'CREATE_CHANNEL_STARTED';
export const CREATE_CHANNEL_COMPLETED = 'CREATE_CHANNEL_COMPLETED';
export const PUBLISH_STARTED = 'PUBLISH_STARTED';
export const PUBLISH_COMPLETED = 'PUBLISH_COMPLETED';
export const PUBLISH_FAILED = 'PUBLISH_FAILED';
export const SET_PLAYING_URI = 'SET_PLAYING_URI';
export const SET_CONTENT_POSITION = 'SET_CONTENT_POSITION';
export const SET_CONTENT_LAST_VIEWED = 'SET_CONTENT_LAST_VIEWED';
export const CLEAR_CONTENT_HISTORY_URI = 'CLEAR_CONTENT_HISTORY_URI';
export const CLEAR_CONTENT_HISTORY_ALL = 'CLEAR_CONTENT_HISTORY_ALL';
// Subscriptions
export const CHANNEL_SUBSCRIBE = 'CHANNEL_SUBSCRIBE';
export const CHANNEL_UNSUBSCRIBE = 'CHANNEL_UNSUBSCRIBE';
export const CHANNEL_SUBSCRIPTION_ENABLE_NOTIFICATIONS =
'CHANNEL_SUBSCRIPTION_ENABLE_NOTIFICATIONS';
export const CHANNEL_SUBSCRIPTION_DISABLE_NOTIFICATIONS =
'CHANNEL_SUBSCRIPTION_DISABLE_NOTIFICATIONS';
export const HAS_FETCHED_SUBSCRIPTIONS = 'HAS_FETCHED_SUBSCRIPTIONS';
export const SET_SUBSCRIPTION_LATEST = 'SET_SUBSCRIPTION_LATEST';
export const UPDATE_SUBSCRIPTION_UNREADS = 'UPDATE_SUBSCRIPTION_UNREADS';
export const REMOVE_SUBSCRIPTION_UNREADS = 'REMOVE_SUBSCRIPTION_UNREADS';
export const CHECK_SUBSCRIPTION_STARTED = 'CHECK_SUBSCRIPTION_STARTED';
export const CHECK_SUBSCRIPTION_COMPLETED = 'CHECK_SUBSCRIPTION_COMPLETED';
export const CHECK_SUBSCRIPTIONS_SUBSCRIBE = 'CHECK_SUBSCRIPTIONS_SUBSCRIBE';
export const FETCH_SUBSCRIPTIONS_START = 'FETCH_SUBSCRIPTIONS_START';
export const FETCH_SUBSCRIPTIONS_FAIL = 'FETCH_SUBSCRIPTIONS_FAIL';
export const FETCH_SUBSCRIPTIONS_SUCCESS = 'FETCH_SUBSCRIPTIONS_SUCCESS';
export const SET_VIEW_MODE = 'SET_VIEW_MODE';
export const GET_SUGGESTED_SUBSCRIPTIONS_START = 'GET_SUGGESTED_SUBSCRIPTIONS_START';
export const GET_SUGGESTED_SUBSCRIPTIONS_SUCCESS = 'GET_SUGGESTED_SUBSCRIPTIONS_SUCCESS';
export const GET_SUGGESTED_SUBSCRIPTIONS_FAIL = 'GET_SUGGESTED_SUBSCRIPTIONS_FAIL';
export const SUBSCRIPTION_FIRST_RUN_COMPLETED = 'SUBSCRIPTION_FIRST_RUN_COMPLETED';
export const VIEW_SUGGESTED_SUBSCRIPTIONS = 'VIEW_SUGGESTED_SUBSCRIPTIONS';
// Blacklist
export const FETCH_BLACK_LISTED_CONTENT_STARTED = 'FETCH_BLACK_LISTED_CONTENT_STARTED';
export const FETCH_BLACK_LISTED_CONTENT_COMPLETED = 'FETCH_BLACK_LISTED_CONTENT_COMPLETED';
export const FETCH_BLACK_LISTED_CONTENT_FAILED = 'FETCH_BLACK_LISTED_CONTENT_FAILED';
export const BLACK_LISTED_CONTENT_SUBSCRIBE = 'BLACK_LISTED_CONTENT_SUBSCRIBE';
// Filtered list
export const FETCH_FILTERED_CONTENT_STARTED = 'FETCH_FILTERED_CONTENT_STARTED';
export const FETCH_FILTERED_CONTENT_COMPLETED = 'FETCH_FILTERED_CONTENT_COMPLETED';
export const FETCH_FILTERED_CONTENT_FAILED = 'FETCH_FILTERED_CONTENT_FAILED';
export const FILTERED_CONTENT_SUBSCRIBE = 'FILTERED_CONTENT_SUBSCRIBE';
// Cost Info
export const FETCH_COST_INFO_STARTED = 'FETCH_COST_INFO_STARTED';
export const FETCH_COST_INFO_COMPLETED = 'FETCH_COST_INFO_COMPLETED';
// Stats
export const FETCH_VIEW_COUNT_STARTED = 'FETCH_VIEW_COUNT_STARTED';
export const FETCH_VIEW_COUNT_FAILED = 'FETCH_VIEW_COUNT_FAILED';
export const FETCH_VIEW_COUNT_COMPLETED = 'FETCH_VIEW_COUNT_COMPLETED';
export const FETCH_SUB_COUNT_STARTED = 'FETCH_SUB_COUNT_STARTED';
export const FETCH_SUB_COUNT_FAILED = 'FETCH_SUB_COUNT_FAILED';
export const FETCH_SUB_COUNT_COMPLETED = 'FETCH_SUB_COUNT_COMPLETED';
// Cross-device Sync
export const GET_SYNC_STARTED = 'GET_SYNC_STARTED';
export const GET_SYNC_COMPLETED = 'GET_SYNC_COMPLETED';
export const GET_SYNC_FAILED = 'GET_SYNC_FAILED';
export const SET_SYNC_STARTED = 'SET_SYNC_STARTED';
export const SET_SYNC_FAILED = 'SET_SYNC_FAILED';
export const SET_SYNC_COMPLETED = 'SET_SYNC_COMPLETED';
export const SET_DEFAULT_ACCOUNT = 'SET_DEFAULT_ACCOUNT';
export const SYNC_APPLY_STARTED = 'SYNC_APPLY_STARTED';
export const SYNC_APPLY_COMPLETED = 'SYNC_APPLY_COMPLETED';
export const SYNC_APPLY_FAILED = 'SYNC_APPLY_FAILED';
export const SYNC_APPLY_BAD_PASSWORD = 'SYNC_APPLY_BAD_PASSWORD';
export const SYNC_RESET = 'SYNC_RESET';
// Lbry.tv
export const UPDATE_UPLOAD_PROGRESS = 'UPDATE_UPLOAD_PROGRESS';
// User
export const GENERATE_AUTH_TOKEN_FAILURE = 'GENERATE_AUTH_TOKEN_FAILURE';
export const GENERATE_AUTH_TOKEN_STARTED = 'GENERATE_AUTH_TOKEN_STARTED';
export const GENERATE_AUTH_TOKEN_SUCCESS = 'GENERATE_AUTH_TOKEN_SUCCESS';

View file

@ -1,5 +0,0 @@
export const MINIMUM_PUBLISH_BID = 0.00000001;
export const CHANNEL_ANONYMOUS = 'anonymous';
export const CHANNEL_NEW = 'new';
export const PAGE_SIZE = 20;

View file

@ -1,4 +0,0 @@
export const ALREADY_CLAIMED =
'once the invite reward has been claimed the referrer cannot be changed';
export const REFERRER_NOT_FOUND =
'A lbry.tv account could not be found for the referrer you provided.';

View file

@ -1,11 +0,0 @@
export const YOUTUBE_SYNC_NOT_TRANSFERRED = 'not_transferred';
export const YOUTUBE_SYNC_PENDING = 'pending';
export const YOUTUBE_SYNC_PENDING_EMAIL = 'pendingemail';
export const YOUTUBE_SYNC_PENDING_TRANSFER = 'pending_transfer';
export const YOUTUBE_SYNC_COMPLETED_TRANSFER = 'completed_transfer';
export const YOUTUBE_SYNC_QUEUED = 'queued';
export const YOUTUBE_SYNC_SYNCING = 'syncing';
export const YOUTUBE_SYNC_SYNCED = 'synced';
export const YOUTUBE_SYNC_FAILED = 'failed';
export const YOUTUBE_SYNC_PENDINGUPGRADE = 'pendingupgrade';
export const YOUTUBE_SYNC_ABANDONDED = 'abandoned';

View file

@ -1,71 +0,0 @@
import * as LBRYINC_ACTIONS from 'constants/action_types';
import * as YOUTUBE_STATUSES from 'constants/youtube';
import * as ERRORS from 'constants/errors';
import Lbryio from './lbryio';
export { Lbryio };
// constants
export { LBRYINC_ACTIONS, YOUTUBE_STATUSES, ERRORS };
// utils
export { doTransifexUpload } from 'util/transifex-upload';
// actions
export { doGenerateAuthToken } from './redux/actions/auth';
export { doFetchCostInfoForUri } from './redux/actions/cost_info';
export { doBlackListedOutpointsSubscribe } from './redux/actions/blacklist';
export { doFilteredOutpointsSubscribe } from './redux/actions/filtered';
export { doFetchViewCount, doFetchSubCount } from './redux/actions/stats';
export {
doCheckSync,
doGetSync,
doSetSync,
doSetDefaultAccount,
doSyncApply,
doResetSync,
doSyncEncryptAndDecrypt,
} from 'redux/actions/sync';
// reducers
export { authReducer } from './redux/reducers/auth';
export { costInfoReducer } from './redux/reducers/cost_info';
export { blacklistReducer } from './redux/reducers/blacklist';
export { filteredReducer } from './redux/reducers/filtered';
export { statsReducer } from './redux/reducers/stats';
export { syncReducer } from './redux/reducers/sync';
// selectors
export { selectAuthToken, selectIsAuthenticating } from './redux/selectors/auth';
export {
selectFetchingCostInfoForUri,
selectCostInfoForUri,
selectAllCostInfoByUri,
selectFetchingCostInfo,
} from './redux/selectors/cost_info';
export {
selectBlackListedOutpoints,
selectBlacklistedOutpointMap,
} from './redux/selectors/blacklist';
export { selectFilteredOutpoints, selectFilteredOutpointMap } from './redux/selectors/filtered';
export {
selectViewCount,
selectViewCountForUri,
// makeSelectViewCountForUri, // deprecated
selectSubCountForUri,
// makeSelectSubCountForUri, // deprecated
} from './redux/selectors/stats';
export { selectBanStateForUri } from './redux/selectors/ban';
export {
selectHasSyncedWallet,
selectSyncData,
selectSyncHash,
selectSetSyncErrorMessage,
selectGetSyncErrorMessage,
selectGetSyncIsPending,
selectSetSyncIsPending,
selectSyncApplyIsPending,
selectHashChanged,
selectSyncApplyErrorMessage,
selectSyncApplyPasswordError,
} from './redux/selectors/sync';

View file

@ -1,238 +0,0 @@
import * as ACTIONS from 'constants/action_types';
import Lbry from 'lbry';
import querystring from 'querystring';
const Lbryio = {
enabled: true,
authenticationPromise: null,
exchangePromise: null,
exchangeLastFetched: null,
CONNECTION_STRING: 'https://api.lbry.com/',
};
const EXCHANGE_RATE_TIMEOUT = 20 * 60 * 1000;
const INTERNAL_APIS_DOWN = 'internal_apis_down';
// We can't use env's because they aren't passed into node_modules
Lbryio.setLocalApi = endpoint => {
Lbryio.CONNECTION_STRING = endpoint.replace(/\/*$/, '/'); // exactly one slash at the end;
};
Lbryio.call = (resource, action, params = {}, method = 'get') => {
if (!Lbryio.enabled) {
return Promise.reject(new Error(__('LBRY internal API is disabled')));
}
if (!(method === 'get' || method === 'post')) {
return Promise.reject(new Error(__('Invalid method')));
}
function checkAndParse(response) {
if (response.status >= 200 && response.status < 300) {
return response.json();
}
if (response.status === 500) {
return Promise.reject(INTERNAL_APIS_DOWN);
}
if (response)
return response.json().then(json => {
let error;
if (json.error) {
error = new Error(json.error);
} else {
error = new Error('Unknown API error signature');
}
error.response = response; // This is primarily a hack used in actions/user.js
return Promise.reject(error);
});
}
function makeRequest(url, options) {
return fetch(url, options).then(checkAndParse);
}
return Lbryio.getAuthToken().then(token => {
const fullParams = { auth_token: token, ...params };
Object.keys(fullParams).forEach(key => {
const value = fullParams[key];
if (typeof value === 'object') {
fullParams[key] = JSON.stringify(value);
}
});
const qs = querystring.stringify(fullParams);
let url = `${Lbryio.CONNECTION_STRING}${resource}/${action}?${qs}`;
let options = {
method: 'GET',
};
if (method === 'post') {
options = {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: qs,
};
url = `${Lbryio.CONNECTION_STRING}${resource}/${action}`;
}
return makeRequest(url, options).then(response => response.data);
});
};
Lbryio.authToken = null;
Lbryio.getAuthToken = () =>
new Promise(resolve => {
if (Lbryio.authToken) {
resolve(Lbryio.authToken);
} else if (Lbryio.overrides.getAuthToken) {
Lbryio.overrides.getAuthToken().then(token => {
resolve(token);
});
} else if (typeof window !== 'undefined') {
const { store } = window;
if (store) {
const state = store.getState();
const token = state.auth ? state.auth.authToken : null;
Lbryio.authToken = token;
resolve(token);
}
resolve(null);
} else {
resolve(null);
}
});
Lbryio.getCurrentUser = () => Lbryio.call('user', 'me');
Lbryio.authenticate = (domain, language) => {
if (!Lbryio.enabled) {
const params = {
id: 1,
primary_email: 'disabled@lbry.io',
has_verified_email: true,
is_identity_verified: true,
is_reward_approved: false,
language: language || 'en',
};
return new Promise(resolve => {
resolve(params);
});
}
if (Lbryio.authenticationPromise === null) {
Lbryio.authenticationPromise = new Promise((resolve, reject) => {
Lbryio.getAuthToken()
.then(token => {
if (!token || token.length > 60) {
return false;
}
// check that token works
return Lbryio.getCurrentUser()
.then(user => user)
.catch(error => {
if (error === INTERNAL_APIS_DOWN) {
throw new Error('Internal APIS down');
}
return false;
});
})
.then(user => {
if (user) {
return user;
}
return Lbry.status()
.then(
status =>
new Promise((res, rej) => {
const appId =
domain && domain !== 'lbry.tv'
? (domain.replace(/[.]/gi, '') + status.installation_id).slice(0, 66)
: status.installation_id;
Lbryio.call(
'user',
'new',
{
auth_token: '',
language: language || 'en',
app_id: appId,
},
'post'
)
.then(response => {
if (!response.auth_token) {
throw new Error('auth_token was not set in the response');
}
const { store } = window;
if (Lbryio.overrides.setAuthToken) {
Lbryio.overrides.setAuthToken(response.auth_token);
}
if (store) {
store.dispatch({
type: ACTIONS.GENERATE_AUTH_TOKEN_SUCCESS,
data: { authToken: response.auth_token },
});
}
Lbryio.authToken = response.auth_token;
return res(response);
})
.catch(error => rej(error));
})
)
.then(newUser => {
if (!newUser) {
return Lbryio.getCurrentUser();
}
return newUser;
});
})
.then(resolve, reject);
});
}
return Lbryio.authenticationPromise;
};
Lbryio.getStripeToken = () =>
Lbryio.CONNECTION_STRING.startsWith('http://localhost:')
? 'pk_test_NoL1JWL7i1ipfhVId5KfDZgo'
: 'pk_live_e8M4dRNnCCbmpZzduEUZBgJO';
Lbryio.getExchangeRates = () => {
if (
!Lbryio.exchangeLastFetched ||
Date.now() - Lbryio.exchangeLastFetched > EXCHANGE_RATE_TIMEOUT
) {
Lbryio.exchangePromise = new Promise((resolve, reject) => {
Lbryio.call('lbc', 'exchange_rate', {}, 'get', true)
.then(({ lbc_usd: LBC_USD, lbc_btc: LBC_BTC, btc_usd: BTC_USD }) => {
const rates = { LBC_USD, LBC_BTC, BTC_USD };
resolve(rates);
})
.catch(reject);
});
Lbryio.exchangeLastFetched = Date.now();
}
return Lbryio.exchangePromise;
};
// Allow overriding lbryio methods
// The desktop app will need to use it for getAuthToken because we use electron's ipcRenderer
Lbryio.overrides = {};
Lbryio.setOverride = (methodName, newMethod) => {
Lbryio.overrides[methodName] = newMethod;
};
export default Lbryio;

View file

@ -1,38 +0,0 @@
import * as ACTIONS from 'constants/action_types';
import { Lbryio } from 'lbryinc';
export function doGenerateAuthToken(installationId) {
return dispatch => {
dispatch({
type: ACTIONS.GENERATE_AUTH_TOKEN_STARTED,
});
Lbryio.call(
'user',
'new',
{
auth_token: '',
language: 'en',
app_id: installationId,
},
'post'
)
.then(response => {
if (!response.auth_token) {
dispatch({
type: ACTIONS.GENERATE_AUTH_TOKEN_FAILURE,
});
} else {
dispatch({
type: ACTIONS.GENERATE_AUTH_TOKEN_SUCCESS,
data: { authToken: response.auth_token },
});
}
})
.catch(() => {
dispatch({
type: ACTIONS.GENERATE_AUTH_TOKEN_FAILURE,
});
});
};
}

View file

@ -1,52 +0,0 @@
import { Lbryio } from 'lbryinc';
import * as ACTIONS from 'constants/action_types';
const CHECK_BLACK_LISTED_CONTENT_INTERVAL = 60 * 60 * 1000;
export function doFetchBlackListedOutpoints() {
return dispatch => {
dispatch({
type: ACTIONS.FETCH_BLACK_LISTED_CONTENT_STARTED,
});
const success = ({ outpoints }) => {
const splitOutpoints = [];
if (outpoints) {
outpoints.forEach((outpoint, index) => {
const [txid, nout] = outpoint.split(':');
splitOutpoints[index] = { txid, nout: Number.parseInt(nout, 10) };
});
}
dispatch({
type: ACTIONS.FETCH_BLACK_LISTED_CONTENT_COMPLETED,
data: {
outpoints: splitOutpoints,
success: true,
},
});
};
const failure = ({ message: error }) => {
dispatch({
type: ACTIONS.FETCH_BLACK_LISTED_CONTENT_FAILED,
data: {
error,
success: false,
},
});
};
Lbryio.call('file', 'list_blocked', {
auth_token: '',
}).then(success, failure);
};
}
export function doBlackListedOutpointsSubscribe() {
return dispatch => {
dispatch(doFetchBlackListedOutpoints());
setInterval(() => dispatch(doFetchBlackListedOutpoints()), CHECK_BLACK_LISTED_CONTENT_INTERVAL);
};
}

View file

@ -1,35 +0,0 @@
import * as ACTIONS from 'constants/action_types';
import { Lbryio } from 'lbryinc';
import { selectClaimForUri } from 'redux/selectors/claims';
// eslint-disable-next-line import/prefer-default-export
export function doFetchCostInfoForUri(uri) {
return (dispatch, getState) => {
const state = getState();
const claim = selectClaimForUri(state, uri);
if (!claim) return;
function resolve(costInfo) {
dispatch({
type: ACTIONS.FETCH_COST_INFO_COMPLETED,
data: {
uri,
costInfo,
},
});
}
const fee = claim.value ? claim.value.fee : undefined;
if (fee === undefined) {
resolve({ cost: 0, includesData: true });
} else if (fee.currency === 'LBC') {
resolve({ cost: fee.amount, includesData: true });
} else {
Lbryio.getExchangeRates().then(({ LBC_USD }) => {
resolve({ cost: fee.amount / LBC_USD, includesData: true });
});
}
};
}

View file

@ -1,47 +0,0 @@
import { Lbryio } from 'lbryinc';
import * as ACTIONS from 'constants/action_types';
const CHECK_FILTERED_CONTENT_INTERVAL = 60 * 60 * 1000;
export function doFetchFilteredOutpoints() {
return dispatch => {
dispatch({
type: ACTIONS.FETCH_FILTERED_CONTENT_STARTED,
});
const success = ({ outpoints }) => {
let formattedOutpoints = [];
if (outpoints) {
formattedOutpoints = outpoints.map(outpoint => {
const [txid, nout] = outpoint.split(':');
return { txid, nout: Number.parseInt(nout, 10) };
});
}
dispatch({
type: ACTIONS.FETCH_FILTERED_CONTENT_COMPLETED,
data: {
outpoints: formattedOutpoints,
},
});
};
const failure = ({ error }) => {
dispatch({
type: ACTIONS.FETCH_FILTERED_CONTENT_FAILED,
data: {
error,
},
});
};
Lbryio.call('file', 'list_filtered', { auth_token: '' }).then(success, failure);
};
}
export function doFilteredOutpointsSubscribe() {
return dispatch => {
dispatch(doFetchFilteredOutpoints());
setInterval(() => dispatch(doFetchFilteredOutpoints()), CHECK_FILTERED_CONTENT_INTERVAL);
};
}

View file

@ -1,32 +0,0 @@
// @flow
import { Lbryio } from 'lbryinc';
import * as ACTIONS from 'constants/action_types';
export const doFetchViewCount = (claimIdCsv: string) => (dispatch: Dispatch) => {
dispatch({ type: ACTIONS.FETCH_VIEW_COUNT_STARTED });
return Lbryio.call('file', 'view_count', { claim_id: claimIdCsv })
.then((result: Array<number>) => {
const viewCounts = result;
dispatch({ type: ACTIONS.FETCH_VIEW_COUNT_COMPLETED, data: { claimIdCsv, viewCounts } });
})
.catch(error => {
dispatch({ type: ACTIONS.FETCH_VIEW_COUNT_FAILED, data: error });
});
};
export const doFetchSubCount = (claimId: string) => (dispatch: Dispatch) => {
dispatch({ type: ACTIONS.FETCH_SUB_COUNT_STARTED });
return Lbryio.call('subscription', 'sub_count', { claim_id: claimId })
.then((result: Array<number>) => {
const subCount = result[0];
dispatch({
type: ACTIONS.FETCH_SUB_COUNT_COMPLETED,
data: { claimId, subCount },
});
})
.catch(error => {
dispatch({ type: ACTIONS.FETCH_SUB_COUNT_FAILED, data: error });
});
};

View file

@ -1,289 +0,0 @@
import * as ACTIONS from 'constants/action_types';
import { Lbryio } from 'lbryinc';
import Lbry from 'lbry';
import { doWalletEncrypt, doWalletDecrypt } from 'redux/actions/wallet';
const NO_WALLET_ERROR = 'no wallet found for this user';
export function doSetDefaultAccount(success, failure) {
return dispatch => {
dispatch({
type: ACTIONS.SET_DEFAULT_ACCOUNT,
});
Lbry.account_list()
.then(accountList => {
const { lbc_mainnet: accounts } = accountList;
let defaultId;
for (let i = 0; i < accounts.length; ++i) {
if (accounts[i].satoshis > 0) {
defaultId = accounts[i].id;
break;
}
}
// In a case where there's no balance on either account
// assume the second (which is created after sync) as default
if (!defaultId && accounts.length > 1) {
defaultId = accounts[1].id;
}
// Set the default account
if (defaultId) {
Lbry.account_set({ account_id: defaultId, default: true })
.then(() => {
if (success) {
success();
}
})
.catch(err => {
if (failure) {
failure(err);
}
});
} else if (failure) {
// no default account to set
failure('Could not set a default account'); // fail
}
})
.catch(err => {
if (failure) {
failure(err);
}
});
};
}
export function doSetSync(oldHash, newHash, data) {
return dispatch => {
dispatch({
type: ACTIONS.SET_SYNC_STARTED,
});
return Lbryio.call('sync', 'set', { old_hash: oldHash, new_hash: newHash, data }, 'post')
.then(response => {
if (!response.hash) {
throw Error('No hash returned for sync/set.');
}
return dispatch({
type: ACTIONS.SET_SYNC_COMPLETED,
data: { syncHash: response.hash },
});
})
.catch(error => {
dispatch({
type: ACTIONS.SET_SYNC_FAILED,
data: { error },
});
});
};
}
export function doGetSync(passedPassword, callback) {
const password = passedPassword === null || passedPassword === undefined ? '' : passedPassword;
function handleCallback(error, hasNewData) {
if (callback) {
if (typeof callback !== 'function') {
throw new Error('Second argument passed to "doGetSync" must be a function');
}
callback(error, hasNewData);
}
}
return dispatch => {
dispatch({
type: ACTIONS.GET_SYNC_STARTED,
});
const data = {};
Lbry.wallet_status()
.then(status => {
if (status.is_locked) {
return Lbry.wallet_unlock({ password });
}
// Wallet is already unlocked
return true;
})
.then(isUnlocked => {
if (isUnlocked) {
return Lbry.sync_hash();
}
data.unlockFailed = true;
throw new Error();
})
.then(hash => Lbryio.call('sync', 'get', { hash }, 'post'))
.then(response => {
const syncHash = response.hash;
data.syncHash = syncHash;
data.syncData = response.data;
data.changed = response.changed;
data.hasSyncedWallet = true;
if (response.changed) {
return Lbry.sync_apply({ password, data: response.data, blocking: true });
}
})
.then(response => {
if (!response) {
dispatch({ type: ACTIONS.GET_SYNC_COMPLETED, data });
handleCallback(null, data.changed);
return;
}
const { hash: walletHash, data: walletData } = response;
if (walletHash !== data.syncHash) {
// different local hash, need to synchronise
dispatch(doSetSync(data.syncHash, walletHash, walletData));
}
dispatch({ type: ACTIONS.GET_SYNC_COMPLETED, data });
handleCallback(null, data.changed);
})
.catch(syncAttemptError => {
if (data.unlockFailed) {
dispatch({ type: ACTIONS.GET_SYNC_FAILED, data: { error: syncAttemptError } });
if (password !== '') {
dispatch({ type: ACTIONS.SYNC_APPLY_BAD_PASSWORD });
}
handleCallback(syncAttemptError);
} else if (data.hasSyncedWallet) {
const error =
(syncAttemptError && syncAttemptError.message) || 'Error getting synced wallet';
dispatch({
type: ACTIONS.GET_SYNC_FAILED,
data: {
error,
},
});
// Temp solution until we have a bad password error code
// Don't fail on blank passwords so we don't show a "password error" message
// before users have ever entered a password
if (password !== '') {
dispatch({ type: ACTIONS.SYNC_APPLY_BAD_PASSWORD });
}
handleCallback(error);
} else {
// user doesn't have a synced wallet
dispatch({
type: ACTIONS.GET_SYNC_COMPLETED,
data: { hasSyncedWallet: false, syncHash: null },
});
// call sync_apply to get data to sync
// first time sync. use any string for old hash
if (syncAttemptError.message === NO_WALLET_ERROR) {
Lbry.sync_apply({ password })
.then(({ hash: walletHash, data: syncApplyData }) => {
dispatch(doSetSync('', walletHash, syncApplyData, password));
handleCallback();
})
.catch(syncApplyError => {
handleCallback(syncApplyError);
});
}
}
});
};
}
export function doSyncApply(syncHash, syncData, password) {
return dispatch => {
dispatch({
type: ACTIONS.SYNC_APPLY_STARTED,
});
Lbry.sync_apply({ password, data: syncData })
.then(({ hash: walletHash, data: walletData }) => {
dispatch({
type: ACTIONS.SYNC_APPLY_COMPLETED,
});
if (walletHash !== syncHash) {
// different local hash, need to synchronise
dispatch(doSetSync(syncHash, walletHash, walletData));
}
})
.catch(() => {
dispatch({
type: ACTIONS.SYNC_APPLY_FAILED,
data: {
error:
'Invalid password specified. Please enter the password for your previously synchronised wallet.',
},
});
});
};
}
export function doCheckSync() {
return dispatch => {
dispatch({
type: ACTIONS.GET_SYNC_STARTED,
});
Lbry.sync_hash().then(hash => {
Lbryio.call('sync', 'get', { hash }, 'post')
.then(response => {
const data = {
hasSyncedWallet: true,
syncHash: response.hash,
syncData: response.data,
hashChanged: response.changed,
};
dispatch({ type: ACTIONS.GET_SYNC_COMPLETED, data });
})
.catch(() => {
// user doesn't have a synced wallet
dispatch({
type: ACTIONS.GET_SYNC_COMPLETED,
data: { hasSyncedWallet: false, syncHash: null },
});
});
});
};
}
export function doResetSync() {
return dispatch =>
new Promise(resolve => {
dispatch({ type: ACTIONS.SYNC_RESET });
resolve();
});
}
export function doSyncEncryptAndDecrypt(oldPassword, newPassword, encrypt) {
return dispatch => {
const data = {};
return Lbry.sync_hash()
.then(hash => Lbryio.call('sync', 'get', { hash }, 'post'))
.then(syncGetResponse => {
data.oldHash = syncGetResponse.hash;
return Lbry.sync_apply({ password: oldPassword, data: syncGetResponse.data });
})
.then(() => {
if (encrypt) {
dispatch(doWalletEncrypt(newPassword));
} else {
dispatch(doWalletDecrypt());
}
})
.then(() => Lbry.sync_apply({ password: newPassword }))
.then(syncApplyResponse => {
if (syncApplyResponse.hash !== data.oldHash) {
return dispatch(doSetSync(data.oldHash, syncApplyResponse.hash, syncApplyResponse.data));
}
})
.catch(console.error);
};
}

View file

@ -1,29 +0,0 @@
import * as ACTIONS from 'constants/action_types';
const reducers = {};
const defaultState = {
authenticating: false,
};
reducers[ACTIONS.GENERATE_AUTH_TOKEN_FAILURE] = state =>
Object.assign({}, state, {
authToken: null,
authenticating: false,
});
reducers[ACTIONS.GENERATE_AUTH_TOKEN_STARTED] = state =>
Object.assign({}, state, {
authenticating: true,
});
reducers[ACTIONS.GENERATE_AUTH_TOKEN_SUCCESS] = (state, action) =>
Object.assign({}, state, {
authToken: action.data.authToken,
authenticating: false,
});
export function authReducer(state = defaultState, action) {
const handler = reducers[action.type];
if (handler) return handler(state, action);
return state;
}

View file

@ -1,37 +0,0 @@
import * as ACTIONS from 'constants/action_types';
import { handleActions } from 'util/redux-utils';
const defaultState = {
fetchingBlackListedOutpoints: false,
fetchingBlackListedOutpointsSucceed: undefined,
blackListedOutpoints: undefined,
};
export const blacklistReducer = handleActions(
{
[ACTIONS.FETCH_BLACK_LISTED_CONTENT_STARTED]: state => ({
...state,
fetchingBlackListedOutpoints: true,
}),
[ACTIONS.FETCH_BLACK_LISTED_CONTENT_COMPLETED]: (state, action) => {
const { outpoints, success } = action.data;
return {
...state,
fetchingBlackListedOutpoints: false,
fetchingBlackListedOutpointsSucceed: success,
blackListedOutpoints: outpoints,
};
},
[ACTIONS.FETCH_BLACK_LISTED_CONTENT_FAILED]: (state, action) => {
const { error, success } = action.data;
return {
...state,
fetchingBlackListedOutpoints: false,
fetchingBlackListedOutpointsSucceed: success,
fetchingBlackListedOutpointsError: error,
};
},
},
defaultState
);

View file

@ -1,38 +0,0 @@
import { handleActions } from 'util/redux-utils';
import * as ACTIONS from 'constants/action_types';
const defaultState = {
fetching: {},
byUri: {},
};
export const costInfoReducer = handleActions(
{
[ACTIONS.FETCH_COST_INFO_STARTED]: (state, action) => {
const { uri } = action.data;
const newFetching = Object.assign({}, state.fetching);
newFetching[uri] = true;
return {
...state,
fetching: newFetching,
};
},
[ACTIONS.FETCH_COST_INFO_COMPLETED]: (state, action) => {
const { uri, costInfo } = action.data;
const newByUri = Object.assign({}, state.byUri);
const newFetching = Object.assign({}, state.fetching);
newByUri[uri] = costInfo;
delete newFetching[uri];
return {
...state,
byUri: newByUri,
fetching: newFetching,
};
},
},
defaultState
);

View file

@ -1,34 +0,0 @@
import * as ACTIONS from 'constants/action_types';
import { handleActions } from 'util/redux-utils';
const defaultState = {
loading: false,
filteredOutpoints: undefined,
};
export const filteredReducer = handleActions(
{
[ACTIONS.FETCH_FILTERED_CONTENT_STARTED]: state => ({
...state,
loading: true,
}),
[ACTIONS.FETCH_FILTERED_CONTENT_COMPLETED]: (state, action) => {
const { outpoints } = action.data;
return {
...state,
loading: false,
filteredOutpoints: outpoints,
};
},
[ACTIONS.FETCH_FILTERED_CONTENT_FAILED]: (state, action) => {
const { error } = action.data;
return {
...state,
loading: false,
fetchingFilteredOutpointsError: error,
};
},
},
defaultState
);

View file

@ -1,55 +0,0 @@
import { handleActions } from 'util/redux-utils';
import * as ACTIONS from 'constants/action_types';
const defaultState = {
fetchingViewCount: false,
viewCountError: undefined,
viewCountById: {},
fetchingSubCount: false,
subCountError: undefined,
subCountById: {},
};
export const statsReducer = handleActions(
{
[ACTIONS.FETCH_VIEW_COUNT_STARTED]: state => ({ ...state, fetchingViewCount: true }),
[ACTIONS.FETCH_VIEW_COUNT_FAILED]: (state, action) => ({
...state,
viewCountError: action.data,
}),
[ACTIONS.FETCH_VIEW_COUNT_COMPLETED]: (state, action) => {
const { claimIdCsv, viewCounts } = action.data;
const viewCountById = Object.assign({}, state.viewCountById);
const claimIds = claimIdCsv.split(',');
if (claimIds.length === viewCounts.length) {
claimIds.forEach((claimId, index) => {
viewCountById[claimId] = viewCounts[index];
});
}
return {
...state,
fetchingViewCount: false,
viewCountById,
};
},
[ACTIONS.FETCH_SUB_COUNT_STARTED]: state => ({ ...state, fetchingSubCount: true }),
[ACTIONS.FETCH_SUB_COUNT_FAILED]: (state, action) => ({
...state,
subCountError: action.data,
}),
[ACTIONS.FETCH_SUB_COUNT_COMPLETED]: (state, action) => {
const { claimId, subCount } = action.data;
const subCountById = { ...state.subCountById, [claimId]: subCount };
return {
...state,
fetchingSubCount: false,
subCountById,
};
},
},
defaultState
);

View file

@ -1,89 +0,0 @@
import * as ACTIONS from 'constants/action_types';
const reducers = {};
const defaultState = {
hasSyncedWallet: false,
syncHash: null,
syncData: null,
setSyncErrorMessage: null,
getSyncErrorMessage: null,
syncApplyErrorMessage: '',
syncApplyIsPending: false,
syncApplyPasswordError: false,
getSyncIsPending: false,
setSyncIsPending: false,
hashChanged: false,
};
reducers[ACTIONS.GET_SYNC_STARTED] = state =>
Object.assign({}, state, {
getSyncIsPending: true,
getSyncErrorMessage: null,
});
reducers[ACTIONS.GET_SYNC_COMPLETED] = (state, action) =>
Object.assign({}, state, {
syncHash: action.data.syncHash,
syncData: action.data.syncData,
hasSyncedWallet: action.data.hasSyncedWallet,
getSyncIsPending: false,
hashChanged: action.data.hashChanged,
});
reducers[ACTIONS.GET_SYNC_FAILED] = (state, action) =>
Object.assign({}, state, {
getSyncIsPending: false,
getSyncErrorMessage: action.data.error,
});
reducers[ACTIONS.SET_SYNC_STARTED] = state =>
Object.assign({}, state, {
setSyncIsPending: true,
setSyncErrorMessage: null,
});
reducers[ACTIONS.SET_SYNC_FAILED] = (state, action) =>
Object.assign({}, state, {
setSyncIsPending: false,
setSyncErrorMessage: action.data.error,
});
reducers[ACTIONS.SET_SYNC_COMPLETED] = (state, action) =>
Object.assign({}, state, {
setSyncIsPending: false,
setSyncErrorMessage: null,
hasSyncedWallet: true, // sync was successful, so the user has a synced wallet at this point
syncHash: action.data.syncHash,
});
reducers[ACTIONS.SYNC_APPLY_STARTED] = state =>
Object.assign({}, state, {
syncApplyPasswordError: false,
syncApplyIsPending: true,
syncApplyErrorMessage: '',
});
reducers[ACTIONS.SYNC_APPLY_COMPLETED] = state =>
Object.assign({}, state, {
syncApplyIsPending: false,
syncApplyErrorMessage: '',
});
reducers[ACTIONS.SYNC_APPLY_FAILED] = (state, action) =>
Object.assign({}, state, {
syncApplyIsPending: false,
syncApplyErrorMessage: action.data.error,
});
reducers[ACTIONS.SYNC_APPLY_BAD_PASSWORD] = state =>
Object.assign({}, state, {
syncApplyPasswordError: true,
});
reducers[ACTIONS.SYNC_RESET] = () => defaultState;
export function syncReducer(state = defaultState, action) {
const handler = reducers[action.type];
if (handler) return handler(state, action);
return state;
}

View file

@ -1,7 +0,0 @@
import { createSelector } from 'reselect';
const selectState = state => state.auth || {};
export const selectAuthToken = createSelector(selectState, state => state.authToken);
export const selectIsAuthenticating = createSelector(selectState, state => state.authenticating);

View file

@ -1,73 +0,0 @@
// @flow
// TODO: This should be in 'redux/selectors/claim.js'. Temporarily putting it
// here to get past importing issues with 'lbryinc', which the real fix might
// involve moving it from 'extras' to 'ui' (big change).
import { createCachedSelector } from 're-reselect';
import { selectClaimForUri, makeSelectIsBlacklisted } from 'redux/selectors/claims';
import { selectMutedChannels } from 'redux/selectors/blocked';
import { selectModerationBlockList } from 'redux/selectors/comments';
import { selectBlacklistedOutpointMap, selectFilteredOutpointMap } from 'lbryinc';
import { getChannelFromClaim } from 'util/claim';
import { isURIEqual } from 'util/lbryURI';
export const selectBanStateForUri = createCachedSelector(
selectClaimForUri,
selectBlacklistedOutpointMap,
selectFilteredOutpointMap,
selectMutedChannels,
selectModerationBlockList,
(state, uri) => makeSelectIsBlacklisted(uri)(state),
(claim, blackListedOutpointMap, filteredOutpointMap, mutedChannelUris, personalBlocklist, isBlacklisted) => {
const banState = {};
if (!claim) {
return banState;
}
const channelClaim = getChannelFromClaim(claim);
if (isBlacklisted) {
banState['blacklisted'] = true;
}
// This will be replaced once blocking is done at the wallet server level.
if (blackListedOutpointMap) {
if (
(channelClaim && blackListedOutpointMap[`${channelClaim.txid}:${channelClaim.nout}`]) ||
blackListedOutpointMap[`${claim.txid}:${claim.nout}`]
) {
banState['blacklisted'] = true;
}
}
// We're checking to see if the stream outpoint or signing channel outpoint
// is in the filter list.
if (filteredOutpointMap) {
if (
(channelClaim && filteredOutpointMap[`${channelClaim.txid}:${channelClaim.nout}`]) ||
filteredOutpointMap[`${claim.txid}:${claim.nout}`]
) {
banState['filtered'] = true;
}
}
// block stream claims
// block channel claims if we can't control for them in claim search
if (mutedChannelUris.length && channelClaim) {
if (mutedChannelUris.some((blockedUri) => isURIEqual(blockedUri, channelClaim.permanent_url))) {
banState['muted'] = true;
}
}
// Commentron blocklist
if (personalBlocklist.length && channelClaim) {
if (personalBlocklist.some((blockedUri) => isURIEqual(blockedUri, channelClaim.permanent_url))) {
banState['blocked'] = true;
}
}
return banState;
}
)((state, uri) => String(uri));

View file

@ -1,20 +0,0 @@
import { createSelector } from 'reselect';
export const selectState = state => state.blacklist || {};
export const selectBlackListedOutpoints = createSelector(
selectState,
state => state.blackListedOutpoints
);
export const selectBlacklistedOutpointMap = createSelector(
selectBlackListedOutpoints,
outpoints =>
outpoints
? outpoints.reduce((acc, val) => {
const outpoint = `${val.txid}:${val.nout}`;
acc[outpoint] = 1;
return acc;
}, {})
: {}
);

View file

@ -1,16 +0,0 @@
// @flow
type State = { costInfo: any };
export const selectState = (state: State) => state.costInfo || {};
export const selectAllCostInfoByUri = (state: State) => selectState(state).byUri;
export const selectFetchingCostInfo = (state: State) => selectState(state).fetching;
export const selectCostInfoForUri = (state: State, uri: string) => {
const costInfos = selectAllCostInfoByUri(state);
return costInfos && costInfos[uri];
};
export const selectFetchingCostInfoForUri = (state: State, uri: string) => {
const fetchingByUri = selectFetchingCostInfo(state);
return fetchingByUri && fetchingByUri[uri];
};

View file

@ -1,20 +0,0 @@
import { createSelector } from 'reselect';
export const selectState = state => state.filtered || {};
export const selectFilteredOutpoints = createSelector(
selectState,
state => state.filteredOutpoints
);
export const selectFilteredOutpointMap = createSelector(
selectFilteredOutpoints,
outpoints =>
outpoints
? outpoints.reduce((acc, val) => {
const outpoint = `${val.txid}:${val.nout}`;
acc[outpoint] = 1;
return acc;
}, {})
: {}
);

View file

@ -1,20 +0,0 @@
// @flow
import { createSelector } from 'reselect';
import { selectClaimIdForUri } from 'redux/selectors/claims';
type State = { claims: any };
const selectState = state => state.stats || {};
export const selectViewCount = createSelector(selectState, state => state.viewCountById);
export const selectSubCount = createSelector(selectState, state => state.subCountById);
export const selectViewCountForUri = (state: State, uri: string) => {
const claimId = selectClaimIdForUri(state, uri);
const viewCountById = selectViewCount(state);
return claimId ? viewCountById[claimId] || 0 : 0;
};
export const selectSubCountForUri = (state: State, uri: string) => {
const claimId = selectClaimIdForUri(state, uri);
const subCountById = selectSubCount(state);
return claimId ? subCountById[claimId] || 0 : 0;
};

View file

@ -1,40 +0,0 @@
import { createSelector } from 'reselect';
const selectState = state => state.sync || {};
export const selectHasSyncedWallet = createSelector(selectState, state => state.hasSyncedWallet);
export const selectSyncHash = createSelector(selectState, state => state.syncHash);
export const selectSyncData = createSelector(selectState, state => state.syncData);
export const selectSetSyncErrorMessage = createSelector(
selectState,
state => state.setSyncErrorMessage
);
export const selectGetSyncErrorMessage = createSelector(
selectState,
state => state.getSyncErrorMessage
);
export const selectGetSyncIsPending = createSelector(selectState, state => state.getSyncIsPending);
export const selectSetSyncIsPending = createSelector(selectState, state => state.setSyncIsPending);
export const selectHashChanged = createSelector(selectState, state => state.hashChanged);
export const selectSyncApplyIsPending = createSelector(
selectState,
state => state.syncApplyIsPending
);
export const selectSyncApplyErrorMessage = createSelector(
selectState,
state => state.syncApplyErrorMessage
);
export const selectSyncApplyPasswordError = createSelector(
selectState,
state => state.syncApplyPasswordError
);

View file

@ -1,17 +0,0 @@
// util for creating reducers
// based off of redux-actions
// https://redux-actions.js.org/docs/api/handleAction.html#handleactions
// eslint-disable-next-line import/prefer-default-export
export const handleActions = (actionMap, defaultState) => (state = defaultState, action) => {
const handler = actionMap[action.type];
if (handler) {
const newState = handler(state, action);
return Object.assign({}, state, newState);
}
// just return the original state if no handler
// returning a copy here breaks redux-persist
return state;
};

View file

@ -1,10 +0,0 @@
export function swapKeyAndValue(dict) {
const ret = {};
// eslint-disable-next-line no-restricted-syntax
for (const key in dict) {
if (dict.hasOwnProperty(key)) {
ret[dict[key]] = key;
}
}
return ret;
}

View file

@ -1,78 +0,0 @@
const apiBaseUrl = 'https://www.transifex.com/api/2/project';
const resource = 'app-strings';
export function doTransifexUpload(contents, project, token, success, fail) {
const url = `${apiBaseUrl}/${project}/resources/`;
const updateUrl = `${apiBaseUrl}/${project}/resource/${resource}/content/`;
const headers = {
Authorization: `Basic ${Buffer.from(`api:${token}`).toString('base64')}`,
'Content-Type': 'application/json',
};
const req = {
accept_translations: true,
i18n_type: 'KEYVALUEJSON',
name: resource,
slug: resource,
content: contents,
};
function handleResponse(text) {
let json;
try {
// transifex api returns Python dicts for some reason.
// Any way to get the api to return valid JSON?
json = JSON.parse(text);
} catch (e) {
// ignore
}
if (success) {
success(json || text);
}
}
function handleError(err) {
if (fail) {
fail(err.message ? err.message : 'Could not upload strings resource to Transifex');
}
}
// check if the resource exists
fetch(updateUrl, { headers })
.then(response => response.json())
.then(() => {
// perform an update
fetch(updateUrl, {
method: 'PUT',
headers,
body: JSON.stringify({ content: contents }),
})
.then(response => {
if (response.status !== 200 && response.status !== 201) {
throw new Error('failed to update transifex');
}
return response.text();
})
.then(handleResponse)
.catch(handleError);
})
.catch(() => {
// resource doesn't exist, create a fresh resource
fetch(url, {
method: 'POST',
headers,
body: JSON.stringify(req),
})
.then(response => {
if (response.status !== 200 && response.status !== 201) {
throw new Error('failed to upload to transifex');
}
return response.text();
})
.then(handleResponse)
.catch(handleError);
});
}

View file

@ -1,3 +0,0 @@
import Recsys from './recsys';
export default Recsys;

View file

@ -1,257 +0,0 @@
import { selectUser } from 'redux/selectors/user';
import { makeSelectRecommendedRecsysIdForClaimId } from 'redux/selectors/search';
import { v4 as Uuidv4 } from 'uuid';
import { parseURI } from 'util/lbryURI';
import * as SETTINGS from 'constants/settings';
import { makeSelectClaimForUri } from 'redux/selectors/claims';
import { selectPlayingUri, selectPrimaryUri } from 'redux/selectors/content';
import { makeSelectClientSetting, selectDaemonSettings } from 'redux/selectors/settings';
import { history } from 'ui/store';
const recsysEndpoint = 'https://clickstream.odysee.com/log/video/view';
const recsysId = 'lighthouse-v0';
const getClaimIdsFromUris = (uris) => {
return uris
? uris.map((uri) => {
try {
const { claimId } = parseURI(uri);
return claimId;
} catch (e) {
return [];
}
})
: [];
};
const recsys = {
entries: {},
debug: false,
/**
* Provides for creating, updating, and sending Clickstream data object Entries.
* Entries are Created either when recommendedContent loads, or when recommendedContent is clicked.
* If recommended content is clicked, An Entry with parentUuid is created.
* On page load, find an empty entry with your claimId, or create a new entry and record to it.
* The entry will be populated with the following:
* - parentUuid // optional
* - Uuid
* - claimId
* - recommendedClaims [] // optionally empty
* - playerEvents [] // optionally empty
* - recommendedClaimsIndexClicked [] // optionally empty
* - UserId
* - pageLoadedAt
* - isEmbed
* - pageExitedAt
* - recsysId // optional
*/
/**
* Function: onClickedRecommended()
* Called when RecommendedContent was clicked.
* Adds index of clicked recommendation to parent entry
* Adds new Entry with parentUuid for destination page
* @param parentClaimId: string,
* @param newClaimId: string,
*/
onClickedRecommended: function (parentClaimId, newClaimId) {
const parentEntry = recsys.entries[parentClaimId] ? recsys.entries[parentClaimId] : null;
const parentUuid = parentEntry['uuid'];
const parentRecommendedClaims = parentEntry['recClaimIds'] || [];
const parentClickedIndexes = parentEntry['recClickedVideoIdx'] || [];
const indexClicked = parentRecommendedClaims.indexOf(newClaimId);
if (parentUuid) {
recsys.createRecsysEntry(newClaimId, parentUuid);
}
parentClickedIndexes.push(indexClicked);
recsys.log('onClickedRecommended', { parentClaimId, newClaimId });
},
/**
* Page was loaded. Get or Create entry and populate it with default data, plus recommended content, recsysId, etc.
* Called from recommendedContent component
*/
onRecsLoaded: function (claimId, uris) {
if (window.store) {
const state = window.store.getState();
if (!recsys.entries[claimId]) {
recsys.createRecsysEntry(claimId);
}
const claimIds = getClaimIdsFromUris(uris);
recsys.entries[claimId]['recsysId'] = makeSelectRecommendedRecsysIdForClaimId(claimId)(state) || recsysId;
recsys.entries[claimId]['pageLoadedAt'] = Date.now();
recsys.entries[claimId]['recClaimIds'] = claimIds;
}
recsys.log('onRecsLoaded', claimId);
},
/**
* Creates an Entry with optional parentUuid
* @param: claimId: string
* @param: parentUuid: string (optional)
*/
createRecsysEntry: function (claimId, parentUuid) {
if (window.store && claimId) {
const state = window.store.getState();
const user = selectUser(state);
const userId = user ? user.id : null;
if (parentUuid) {
// Make a stub entry that will be filled out on page load
recsys.entries[claimId] = {
uuid: Uuidv4(),
parentUuid: parentUuid,
uid: userId || null, // selectUser
claimId: claimId,
recClickedVideoIdx: [],
pageLoadedAt: Date.now(),
events: [],
};
} else {
recsys.entries[claimId] = {
uuid: Uuidv4(),
uid: userId, // selectUser
claimId: claimId,
pageLoadedAt: Date.now(),
recsysId: null,
recClaimIds: [],
recClickedVideoIdx: [],
events: [],
};
}
}
recsys.log('createRecsysEntry', claimId);
},
/**
* Send event for claimId
* @param claimId
* @param isTentative
*/
sendRecsysEntry: function (claimId, isTentative) {
const shareTelemetry =
IS_WEB || (window && window.store && selectDaemonSettings(window.store.getState()).share_usage_data);
if (recsys.entries[claimId] && shareTelemetry) {
const data = JSON.stringify(recsys.entries[claimId]);
try {
navigator.sendBeacon(recsysEndpoint, data);
if (!isTentative) {
delete recsys.entries[claimId];
}
} catch (error) {
console.log('no beacon for you', error);
}
}
recsys.log('sendRecsysEntry', claimId);
},
/**
* A player event fired. Get the Entry for the claimId, and add the events
* @param claimId
* @param event
*/
onRecsysPlayerEvent: function (claimId, event, isEmbedded) {
if (!recsys.entries[claimId]) {
recsys.createRecsysEntry(claimId);
// do something to show it's floating or autoplay
}
if (isEmbedded) {
recsys.entries[claimId]['isEmbed'] = true;
}
recsys.entries[claimId].events.push(event);
recsys.log('onRecsysPlayerEvent', claimId);
},
log: function (callName, claimId) {
if (recsys.debug) {
console.log(`Call: ***${callName}***, ClaimId: ${claimId}, Recsys Entries`, Object.assign({}, recsys.entries));
}
},
/**
* Player closed. Check to see if primaryUri = playingUri
* if so, send the Entry.
*/
onPlayerDispose: function (claimId, isEmbedded) {
if (window.store) {
const state = window.store.getState();
const playingUri = selectPlayingUri(state);
const primaryUri = selectPrimaryUri(state);
const onFilePage = playingUri === primaryUri;
if (!onFilePage || isEmbedded) {
if (isEmbedded) {
recsys.entries[claimId]['isEmbed'] = true;
}
recsys.sendRecsysEntry(claimId);
}
}
recsys.log('PlayerDispose', claimId);
},
// /**
// * File page unmount or change event
// * Check to see if playingUri, floatingEnabled, primaryUri === playingUri
// * If not, send the Entry.
// * If floating enabled, leaving file page will pop out player, leading to
// * more events until player is disposed. Don't send unless floatingPlayer playingUri
// */
// onLeaveFilePage: function (primaryUri) {
// if (window.store) {
// const state = window.store.getState();
// const claim = makeSelectClaimForUri(primaryUri)(state);
// const claimId = claim ? claim.claim_id : null;
// const playingUri = selectPlayingUri(state);
// const actualPlayingUri = playingUri && playingUri.uri;
// // const primaryUri = selectPrimaryUri(state);
// const floatingPlayer = makeSelectClientSetting(SETTINGS.FLOATING_PLAYER)(state);
// // When leaving page, if floating player is enabled, play will continue.
// if (claimId) {
// recsys.entries[claimId]['pageExitedAt'] = Date.now();
// }
// const shouldSend =
// (claimId && floatingPlayer && actualPlayingUri && actualPlayingUri !== primaryUri) || !floatingPlayer || !actualPlayingUri;
// if (shouldSend) {
// recsys.sendRecsysEntry(claimId);
// }
// recsys.log('LeaveFile', claimId);
// }
// },
/**
* Navigate event
* Send all claimIds that aren't currently playing.
*/
onNavigate: function () {
if (window.store) {
const state = window.store.getState();
const playingUri = selectPlayingUri(state);
const actualPlayingUri = playingUri && playingUri.uri;
const claim = makeSelectClaimForUri(actualPlayingUri)(state);
const playingClaimId = claim ? claim.claim_id : null;
// const primaryUri = selectPrimaryUri(state);
const floatingPlayer = makeSelectClientSetting(SETTINGS.FLOATING_PLAYER)(state);
// When leaving page, if floating player is enabled, play will continue.
Object.keys(recsys.entries).forEach((claimId) => {
const shouldSkip = recsys.entries[claimId].parentUuid && !recsys.entries[claimId].recClaimIds;
if (!shouldSkip && ((claimId !== playingClaimId && floatingPlayer) || !floatingPlayer)) {
recsys.entries[claimId]['pageExitedAt'] = Date.now();
recsys.sendRecsysEntry(claimId);
}
recsys.log('OnNavigate', claimId);
});
}
},
};
// @if TARGET='web'
document.addEventListener('visibilitychange', function logData() {
if (document.visibilityState === 'hidden') {
Object.keys(recsys.entries).map((claimId) => recsys.sendRecsysEntry(claimId, true));
}
});
// @endif
history.listen(() => {
recsys.onNavigate();
});
export default recsys;

View file

@ -1,10 +0,0 @@
declare type BlocklistState = {
blockedChannels: Array<string>
};
declare type BlocklistAction = {
type: string,
data: {
uri: string,
},
};

251
flow-typed/Claim.js vendored
View file

@ -1,251 +0,0 @@
// @flow
declare type Claim = StreamClaim | ChannelClaim | CollectionClaim;
declare type ChannelClaim = GenericClaim & {
value: ChannelMetadata,
};
declare type CollectionClaim = GenericClaim & {
value: CollectionMetadata,
};
declare type StreamClaim = GenericClaim & {
value: StreamMetadata,
};
declare type GenericClaim = {
address: string, // address associated with tx
amount: string, // bid amount at time of tx
canonical_url: string, // URL with short id, includes channel with short id
claim_id: string, // unique claim identifier
claim_sequence: number, // not being used currently
claim_op: 'create' | 'update',
confirmations: number,
decoded_claim: boolean, // Not available currently https://github.com/lbryio/lbry/issues/2044
timestamp?: number, // date of last transaction
height: number, // block height the tx was confirmed
is_channel_signature_valid?: boolean,
is_my_output: boolean,
name: string,
normalized_name: string, // `name` normalized via unicode NFD spec,
nout: number, // index number for an output of a tx
permanent_url: string, // name + claim_id
short_url: string, // permanent_url with short id, no channel
txid: string, // unique tx id
type: 'claim' | 'update' | 'support',
value_type: 'stream' | 'channel' | 'collection',
signing_channel?: ChannelClaim,
reposted_claim?: GenericClaim,
repost_channel_url?: string,
repost_url?: string,
repost_bid_amount?: string,
purchase_receipt?: PurchaseReceipt,
meta: {
activation_height: number,
claims_in_channel?: number,
creation_height: number,
creation_timestamp: number,
effective_amount: string,
expiration_height: number,
is_controlling: boolean,
support_amount: string,
reposted: number,
trending_global: number,
trending_group: number,
trending_local: number,
trending_mixed: number,
},
};
declare type GenericMetadata = {
title?: string,
description?: string,
thumbnail?: {
url?: string,
},
languages?: Array<string>,
tags?: Array<string>,
locations?: Array<Location>,
};
declare type ChannelMetadata = GenericMetadata & {
public_key: string,
public_key_id: string,
cover_url?: string,
email?: string,
website_url?: string,
featured?: Array<string>,
};
declare type CollectionMetadata = GenericMetadata & {
claims: Array<string>,
}
declare type StreamMetadata = GenericMetadata & {
license?: string, // License "title" ex: Creative Commons, Custom copyright
license_url?: string, // Link to full license
release_time?: number, // linux timestamp
author?: string,
source: {
sd_hash: string,
media_type?: string,
hash?: string,
name?: string, // file name
size?: number, // size of file in bytes
},
// Only exists if a stream has a fee
fee?: Fee,
stream_type: 'video' | 'audio' | 'image' | 'software',
// Below correspond to `stream_type`
video?: {
duration: number,
height: number,
width: number,
},
audio?: {
duration: number,
},
image?: {
height: number,
width: number,
},
software?: {
os: string,
},
};
declare type Location = {
latitude?: number,
longitude?: number,
country?: string,
state?: string,
city?: string,
code?: string,
};
declare type Fee = {
amount: string,
currency: string,
address: string,
};
declare type PurchaseReceipt = {
address: string,
amount: string,
claim_id: string,
confirmations: number,
height: number,
nout: number,
timestamp: number,
txid: string,
type: 'purchase',
};
declare type ClaimErrorCensor = {
address: string,
amount: string,
canonical_url: string,
claim_id: string,
claim_op: string,
confirmations: number,
has_signing_key: boolean,
height: number,
meta: {
activation_height: number,
claims_in_channel: number,
creation_height: number,
creation_timestamp: number,
effective_amount: string,
expiration_height: number,
is_controlling: boolean,
reposted: number,
support_amount: string,
take_over_height: number,
},
name: string,
normalized_name: string,
nout: number,
permanent_url: string,
short_url: string,
timestamp: number,
txid: string,
type: string,
value: {
public_key: string,
public_key_id: string,
},
value_type: string,
}
declare type ClaimActionResolveInfo = {
[string]: {
stream: ?StreamClaim,
channel: ?ChannelClaim,
claimsInChannel: ?number,
collection: ?CollectionClaim,
errorCensor: ?ClaimErrorCensor,
},
}
declare type ChannelUpdateParams = {
claim_id: string,
bid?: string,
title?: string,
cover_url?: string,
thumbnail_url?: string,
description?: string,
website_url?: string,
email?: string,
tags?: Array<string>,
replace?: boolean,
languages?: Array<string>,
locations?: Array<string>,
blocking?: boolean,
}
declare type ChannelPublishParams = {
name: string,
bid: string,
blocking?: true,
title?: string,
cover_url?: string,
thumbnail_url?: string,
description?: string,
website_url?: string,
email?: string,
tags?: Array<string>,
languages?: Array<string>,
}
declare type CollectionUpdateParams = {
claim_id: string,
claim_ids?: Array<string>,
bid?: string,
title?: string,
cover_url?: string,
thumbnail_url?: string,
description?: string,
website_url?: string,
email?: string,
tags?: Array<string>,
replace?: boolean,
languages?: Array<string>,
locations?: Array<string>,
blocking?: boolean,
}
declare type CollectionPublishParams = {
name: string,
bid: string,
claim_ids: Array<string>,
blocking?: true,
title?: string,
thumbnail_url?: string,
description?: string,
tags?: Array<string>,
languages?: Array<string>,
}

View file

@ -1,29 +0,0 @@
declare type CoinSwapInfo = {
chargeCode: string,
coins: Array<string>,
sendAddresses: { [string]: string},
sendAmounts: { [string]: any },
lbcAmount: number,
status?: {
status: string,
receiptCurrency: string,
receiptTxid: string,
lbcTxid: string,
},
}
declare type CoinSwapState = {
coinSwaps: Array<CoinSwapInfo>,
};
declare type CoinSwapAddAction = {
type: string,
data: CoinSwapInfo,
};
declare type CoinSwapRemoveAction = {
type: string,
data: {
chargeCode: string,
},
};

View file

@ -1,33 +0,0 @@
declare type Collection = {
id: string,
items: Array<?string>,
name: string,
type: string,
updatedAt: number,
totalItems?: number,
sourceId?: string, // if copied, claimId of original collection
};
declare type CollectionState = {
unpublished: CollectionGroup,
resolved: CollectionGroup,
pending: CollectionGroup,
edited: CollectionGroup,
builtin: CollectionGroup,
saved: Array<string>,
isResolvingCollectionById: { [string]: boolean },
error?: string | null,
};
declare type CollectionGroup = {
[string]: Collection,
}
declare type CollectionEditParams = {
uris?: Array<string>,
remove?: boolean,
replace?: boolean,
order?: { from: number, to: number },
type?: string,
name?: string,
}

301
flow-typed/Comment.js vendored
View file

@ -1,301 +0,0 @@
declare type Comment = {
comment: string, // comment body
comment_id: string, // sha256 digest
claim_id: string, // id linking to the claim this comment
timestamp: number, // integer representing unix-time
is_hidden: boolean, // claim owner may enable/disable this
channel_id: string, // claimId of channel signing this comment
channel_name?: string, // name of channel claim
channel_url?: string, // full lbry url to signing channel
signature?: string, // signature of comment by originating channel
signing_ts?: string, // timestamp used when signing this comment
is_channel_signature_valid?: boolean, // whether or not the signature could be validated
parent_id?: number, // comment_id of comment this is in reply to
is_pinned: boolean,
support_amount: number,
replies: number, // number of direct replies (i.e. excluding nested replies).
is_moderator: boolean,
is_creator: boolean,
is_global_mod: boolean,
is_fiat?: boolean,
};
declare type PerChannelSettings = {
words?: Array<string>,
comments_enabled?: boolean,
min_tip_amount_comment?: number,
min_tip_amount_super_chat?: number,
slow_mode_min_gap?: number,
};
// todo: relate individual comments to their commentId
declare type CommentsState = {
commentsByUri: { [string]: string },
superChatsByUri: { [string]: { totalAmount: number, comments: Array<Comment> } },
byId: { [string]: Array<string> }, // ClaimID -> list of fetched comment IDs.
totalCommentsById: {}, // ClaimId -> ultimate total (including replies) in commentron.
repliesByParentId: { [string]: Array<string> }, // ParentCommentID -> list of fetched replies.
repliesTotalPagesByParentId: {}, // ParentCommentID -> total number of reply pages for a parentId in commentron.
topLevelCommentsById: { [string]: Array<string> }, // ClaimID -> list of fetched top level comments.
topLevelTotalPagesById: { [string]: number }, // ClaimID -> total number of top-level pages in commentron. Based on COMMENT_PAGE_SIZE_TOP_LEVEL.
topLevelTotalCommentsById: { [string]: number }, // ClaimID -> total top level comments in commentron.
commentById: { [string]: Comment },
linkedCommentAncestors: { [string]: Array<string> }, // {"linkedCommentId": ["parentId", "grandParentId", ...]}
pinnedCommentsById: {}, // ClaimId -> array of pinned comment IDs
isLoading: boolean,
isLoadingById: boolean,
isLoadingByParentId: { [string]: boolean },
isCommenting: boolean,
myComments: ?Set<string>,
isFetchingReacts: boolean,
myReactsByCommentId: ?{ [string]: Array<string> }, // {"CommentId:MyChannelId": ["like", "dislike", ...]}
othersReactsByCommentId: ?{ [string]: { [string]: number } }, // {"CommentId:MyChannelId": {"like": 2, "dislike": 2, ...}}
pendingCommentReactions: Array<string>,
moderationBlockList: ?Array<string>, // @KP rename to "personalBlockList"?
adminBlockList: ?Array<string>,
moderatorBlockList: ?Array<string>,
moderatorBlockListDelegatorsMap: {[string]: Array<string>}, // {"blockedUri": ["delegatorUri1", ""delegatorUri2", ...]}
fetchingModerationBlockList: boolean,
moderationDelegatesById: { [string]: Array<{ channelId: string, channelName: string }> },
fetchingModerationDelegates: boolean,
moderationDelegatorsById: { [string]: { global: boolean, delegators: { name: string, claimId: string } }},
fetchingModerationDelegators: boolean,
blockingByUri: {},
unBlockingByUri: {},
personalTimeoutMap: { [uri: string]: { blockedAt: string, bannedFor: number, banRemaining: number } },
adminTimeoutMap: { [uri: string]: { blockedAt: string, bannedFor: number, banRemaining: number } },
moderatorTimeoutMap: { [uri: string]: { blockedAt: string, bannedFor: number, banRemaining: number } },
togglingForDelegatorMap: {[string]: Array<string>}, // {"blockedUri": ["delegatorUri1", ""delegatorUri2", ...]}
settingsByChannelId: { [string]: PerChannelSettings }, // ChannelID -> settings
fetchingSettings: boolean,
fetchingBlockedWords: boolean,
};
declare type CommentReactParams = {
comment_ids: string,
channel_name: string,
channel_id: string,
react_type: string,
clear_types?: string,
remove?: boolean,
};
declare type ReactionReactParams = {
comment_ids: string,
signature?: string,
signing_ts?: string,
remove?: boolean,
clear_types?: string,
type: string,
channel_id: string,
channel_name: string,
};
declare type ReactionReactResponse = {
Reactions: { [string]: { [string]: number} },
};
declare type ReactionListParams = {
comment_ids: string, // CSV of IDs
channel_id?: string,
channel_name?: string,
signature?: string,
signing_ts?: string,
types?: string,
};
declare type ReactionListResponse = {
my_reactions: Array<MyReactions>,
others_reactions: Array<OthersReactions>,
};
declare type CommentListParams = {
page: number, // pagination: which page of results
page_size: number, // pagination: nr of comments to show in a page (max 200)
claim_id?: string, // claim id of claim being commented on
channel_name?: string, // signing channel name of claim (enables 'commentsEnabled' check)
channel_id?: string, // signing channel claim id of claim (enables 'commentsEnabled' check)
author_claim_id?: string, // filters comments to just this author
parent_id?: string, // filters comments to those under this thread
top_level?: boolean, // filters to only top level comments
hidden?: boolean, // if true, will show hidden comments as well
sort_by?: number, // @see: ui/constants/comments.js::SORT_BY
};
declare type CommentListResponse = {
items: Array<Comment>,
page: number,
page_size: number,
total_items: number, // Grand total for the claim being commented on.
total_filtered_items: number, // Total for filtered queries (e.g. top_level=true, parent_id=xxx, etc.).
total_pages: number,
has_hidden_comments: boolean,
};
declare type CommentByIdParams = {
comment_id: string,
with_ancestors: boolean,
}
declare type CommentByIdResponse = {
item: Comment,
items: Comment,
ancestors: Array<Comment>,
}
declare type CommentPinParams = {
comment_id: string,
channel_id: string,
channel_name: string,
remove?: boolean,
signature: string,
signing_ts: string,
}
declare type CommentPinResponse = {
items: Comment, // "items" is an inherited typo to match SDK. Will be "item" in a new version.
}
declare type CommentEditParams = {
comment: string,
comment_id: string,
signature: string,
signing_ts: string,
}
declare type CommentEditResponse = Comment
declare type CommentAbandonParams = {
comment_id: string,
creator_channel_id?: string,
creator_channel_name?: string,
channel_id?: string,
hexdata?: string,
};
declare type CommentCreateParams = {
comment: string,
claim_id: string,
parent_id?: string,
signature: string,
signing_ts: string,
support_tx_id?: string,
};
declare type SuperListParams = {};
declare type SuperListResponse = {
page: number,
page_size: number,
total_pages: number,
total_items: number,
total_amount: number,
items: Array<Comment>,
has_hidden_comments: boolean,
};
declare type ModerationBlockParams = {
// Publisher, Moderator, or Commentron Admin
mod_channel_id: string,
mod_channel_name: string,
// Offender being blocked
blocked_channel_id: string,
blocked_channel_name: string,
// Creator that Moderator is delegated from. Used for delegated moderation
creator_channel_id?: string,
creator_channel_name?: string,
// Blocks identity from comment universally, requires Admin rights on commentron instance
block_all?: boolean,
time_out?: number,
// If true will delete all comments of the offender, requires Admin rights on commentron for universal delete
delete_all?: boolean,
// The usual signature stuff
signature: string,
signing_ts: string,
};
declare type ModerationBlockResponse = {
deleted_comment_ids: Array<string>,
banned_channel_id: string,
all_blocked: boolean,
banned_from: string,
};
declare type BlockedListArgs = {
// Publisher, Moderator or Commentron Admin
mod_channel_id: string,
mod_channel_name: string,
// Creator that Moderator is delegated from. Used for delegated moderation
creator_channel_id?: string,
creator_channel_name?: string,
signature: string,
signing_ts: string,
};
declare type ModerationAddDelegateParams = {
mod_channel_id: string,
mod_channel_name: string,
creator_channel_id: string,
creator_channel_name: string,
signature: string,
signing_ts: string,
};
declare type ModerationRemoveDelegateParams = {
mod_channel_id: string,
mod_channel_name: string,
creator_channel_id: string,
creator_channel_name: string,
signature: string,
signing_ts: string,
};
declare type ModerationListDelegatesParams = {
creator_channel_id: string,
creator_channel_name: string,
signature: string,
signing_ts: string,
};
declare type ModerationAmIParams = {
channel_name: string,
channel_id: string,
signature: string,
signing_ts: string
};
declare type SettingsParams = {
channel_name?: string,
channel_id: string,
signature?: string,
signing_ts?: string,
};
declare type SettingsResponse = {
words?: string,
comments_enabled: boolean,
min_tip_amount_comment: number,
min_tip_amount_super_chat: number,
slow_mode_min_gap: number,
curse_jar_amount: number,
filters_enabled?: boolean,
};
declare type UpdateSettingsParams = {
channel_name: string,
channel_id: string,
signature: string,
signing_ts: string,
comments_enabled?: boolean,
min_tip_amount_comment?: number,
min_tip_amount_super_chat?: number,
slow_mode_min_gap?: number,
}
declare type BlockWordParams = {
channel_name: string,
channel_id: string,
signature: string,
signing_ts: string,
words: string, // CSV list of containing words to block comment on content
};

78
flow-typed/File.js vendored
View file

@ -1,78 +0,0 @@
// @flow
declare type FileListItem = {
metadata: StreamMetadata,
added_on: number,
blobs_completed: number,
blobs_in_stream: number,
blobs_remaining: number,
channel_claim_id: string,
channel_name: string,
claim_id: string,
claim_name: string,
completed: false,
content_fee?: { txid: string },
purchase_receipt?: { txid: string, amount: string },
download_directory: string,
download_path: string,
file_name: string,
key: string,
mime_type: string,
nout: number,
outpoint: string,
points_paid: number,
protobuf: string,
reflector_progress: number,
sd_hash: string,
status: string,
stopped: false,
stream_hash: string,
stream_name: string,
streaming_url: string,
suggested_file_name: string,
total_bytes: number,
total_bytes_lower_bound: number,
is_fully_reflected: boolean,
// TODO: sdk plans to change `tx`
// It isn't currently used by the apps
tx: {},
txid: string,
uploading_to_reflector: boolean,
written_bytes: number,
};
declare type FileState = {
failedPurchaseUris: Array<string>,
purchasedUris: Array<string>,
};
declare type PurchaseUriCompleted = {
type: ACTIONS.PURCHASE_URI_COMPLETED,
data: {
uri: string,
streamingUrl: string,
},
};
declare type PurchaseUriFailed = {
type: ACTIONS.PURCHASE_URI_FAILED,
data: {
uri: string,
error: any,
},
};
declare type PurchaseUriStarted = {
type: ACTIONS.PURCHASE_URI_STARTED,
data: {
uri: string,
streamingUrl: string,
},
};
declare type DeletePurchasedUri = {
type: ACTIONS.CLEAR_PURCHASED_URI_SUCCESS,
data: {
uri: string,
},
};

369
flow-typed/Lbry.js vendored
View file

@ -1,369 +0,0 @@
// @flow
declare type StatusResponse = {
blob_manager: {
finished_blobs: number,
},
blockchain_headers: {
download_progress: number,
downloading_headers: boolean,
},
dht: {
node_id: string,
peers_in_routing_table: number,
},
hash_announcer: {
announce_queue_size: number,
},
installation_id: string,
is_running: boolean,
skipped_components: Array<string>,
startup_status: {
blob_manager: boolean,
blockchain_headers: boolean,
database: boolean,
dht: boolean,
exchange_rate_manager: boolean,
hash_announcer: boolean,
peer_protocol_server: boolean,
stream_manager: boolean,
upnp: boolean,
wallet: boolean,
},
stream_manager: {
managed_files: number,
},
upnp: {
aioupnp_version: string,
dht_redirect_set: boolean,
external_ip: string,
gateway: string,
peer_redirect_set: boolean,
redirects: {},
},
wallet: ?{
connected: string,
best_blockhash: string,
blocks: number,
blocks_behind: number,
is_encrypted: boolean,
is_locked: boolean,
headers_synchronization_progress: number,
available_servers: number,
},
};
declare type VersionResponse = {
build: string,
lbrynet_version: string,
os_release: string,
os_system: string,
platform: string,
processor: string,
python_version: string,
};
declare type BalanceResponse = {
available: string,
reserved: string,
reserved_subtotals: ?{
claims: string,
supports: string,
tips: string,
},
total: string,
};
declare type ResolveResponse = {
// Keys are the url(s) passed to resolve
[string]: { error?: {}, stream?: StreamClaim, channel?: ChannelClaim, collection?: CollectionClaim, claimsInChannel?: number },
};
declare type GetResponse = FileListItem & { error?: string };
declare type GenericTxResponse = {
height: number,
hex: string,
inputs: Array<{}>,
outputs: Array<{}>,
total_fee: string,
total_input: string,
total_output: string,
txid: string,
};
declare type PublishResponse = GenericTxResponse & {
// Only first value in outputs is a claim
// That's the only value we care about
outputs: Array<Claim>,
};
declare type ClaimSearchResponse = {
items: Array<Claim>,
page: number,
page_size: number,
total_items: number,
total_pages: number,
};
declare type ClaimListResponse = {
items: Array<ChannelClaim | Claim>,
page: number,
page_size: number,
total_items: number,
total_pages: number,
};
declare type ChannelCreateResponse = GenericTxResponse & {
outputs: Array<ChannelClaim>,
};
declare type ChannelUpdateResponse = GenericTxResponse & {
outputs: Array<ChannelClaim>,
};
declare type CommentCreateResponse = Comment;
declare type CommentUpdateResponse = Comment;
declare type MyReactions = {
// Keys are the commentId
[string]: Array<string>,
};
declare type OthersReactions = {
// Keys are the commentId
[string]: {
// Keys are the reaction_type, e.g. 'like'
[string]: number,
},
};
declare type CommentReactListResponse = {
my_reactions: Array<MyReactions>,
others_reactions: Array<OthersReactions>,
};
declare type CommentHideResponse = {
// keyed by the CommentIds entered
[string]: { hidden: boolean },
};
declare type CommentPinResponse = {
// keyed by the CommentIds entered
items: Comment,
};
declare type CommentAbandonResponse = {
// keyed by the CommentId given
abandoned: boolean,
};
declare type ChannelListResponse = {
items: Array<ChannelClaim>,
page: number,
page_size: number,
total_items: number,
total_pages: number,
};
declare type ChannelSignResponse = {
signature: string,
signing_ts: string,
};
declare type CollectionCreateResponse = {
outputs: Array<Claim>,
page: number,
page_size: number,
total_items: number,
total_pages: number,
}
declare type CollectionListResponse = {
items: Array<Claim>,
page: number,
page_size: number,
total_items: number,
total_pages: number,
};
declare type CollectionResolveResponse = {
items: Array<Claim>,
total_items: number,
};
declare type CollectionResolveOptions = {
claim_id: string,
};
declare type CollectionListOptions = {
page: number,
page_size: number,
resolve?: boolean,
};
declare type FileListResponse = {
items: Array<FileListItem>,
page: number,
page_size: number,
total_items: number,
total_pages: number,
};
declare type TxListResponse = {
items: Array<Transaction>,
page: number,
page_size: number,
total_items: number,
total_pages: number,
};
declare type SupportListResponse = {
items: Array<Support>,
page: number,
page_size: number,
total_items: number,
total_pages: number,
};
declare type BlobListResponse = { items: Array<string> };
declare type WalletListResponse = Array<{
id: string,
name: string,
}>;
declare type WalletStatusResponse = {
is_encrypted: boolean,
is_locked: boolean,
is_syncing: boolean,
};
declare type SyncApplyResponse = {
hash: string,
data: string,
};
declare type SupportAbandonResponse = GenericTxResponse;
declare type StreamListResponse = {
items: Array<StreamClaim>,
page: number,
page_size: number,
total_items: number,
total_pages: number,
};
declare type StreamRepostOptions = {
name: string,
bid: string,
claim_id: string,
channel_id?: string,
};
declare type StreamRepostResponse = GenericTxResponse;
declare type PurchaseListResponse = {
items: Array<PurchaseReceipt & { claim: StreamClaim }>,
page: number,
page_size: number,
total_items: number,
total_pages: number,
};
declare type PurchaseListOptions = {
page: number,
page_size: number,
resolve: boolean,
claim_id?: string,
channel_id?: string,
};
//
// Types used in the generic Lbry object that is exported
//
declare type LbryTypes = {
isConnected: boolean,
connectPromise: any, // null |
connect: () => any, // void | Promise<any> ?
daemonConnectionString: string,
alternateConnectionString: string,
methodsUsingAlternateConnectionString: Array<string>,
apiRequestHeaders: { [key: string]: string },
setDaemonConnectionString: string => void,
setApiHeader: (string, string) => void,
unsetApiHeader: string => void,
overrides: { [string]: ?Function },
setOverride: (string, Function) => void,
// getMediaType: (?string, ?string) => string,
// Lbry Methods
stop: () => Promise<string>,
status: () => Promise<StatusResponse>,
version: () => Promise<VersionResponse>,
resolve: (params: {}) => Promise<ResolveResponse>,
get: (params: {}) => Promise<GetResponse>,
publish: (params: {}) => Promise<PublishResponse>,
claim_search: (params: {}) => Promise<ClaimSearchResponse>,
claim_list: (params: {}) => Promise<ClaimListResponse>,
channel_create: (params: {}) => Promise<ChannelCreateResponse>,
channel_update: (params: {}) => Promise<ChannelUpdateResponse>,
channel_import: (params: {}) => Promise<string>,
channel_list: (params: {}) => Promise<ChannelListResponse>,
channel_sign: (params: {}) => Promise<ChannelSignResponse>,
stream_abandon: (params: {}) => Promise<GenericTxResponse>,
stream_list: (params: {}) => Promise<StreamListResponse>,
channel_abandon: (params: {}) => Promise<GenericTxResponse>,
support_create: (params: {}) => Promise<GenericTxResponse>,
support_list: (params: {}) => Promise<SupportListResponse>,
support_abandon: (params: {}) => Promise<SupportAbandonResponse>,
stream_repost: (params: StreamRepostOptions) => Promise<StreamRepostResponse>,
purchase_list: (params: PurchaseListOptions) => Promise<PurchaseListResponse>,
collection_resolve: (params: CollectionResolveOptions) => Promise<CollectionResolveResponse>,
collection_list: (params: CollectionListOptions) => Promise<CollectionListResponse>,
collection_create: (params: {}) => Promise<CollectionCreateResponse>,
collection_update: (params: {}) => Promise<CollectionCreateResponse>,
// File fetching and manipulation
file_list: (params: {}) => Promise<FileListResponse>,
file_delete: (params: {}) => Promise<boolean>,
blob_delete: (params: {}) => Promise<string>,
blob_list: (params: {}) => Promise<BlobListResponse>,
file_set_status: (params: {}) => Promise<any>,
file_reflect: (params: {}) => Promise<any>,
// Preferences
preference_get: (params?: {}) => Promise<any>,
preference_set: (params: {}) => Promise<any>,
// Commenting
comment_update: (params: {}) => Promise<CommentUpdateResponse>,
comment_hide: (params: {}) => Promise<CommentHideResponse>,
comment_abandon: (params: {}) => Promise<CommentAbandonResponse>,
comment_list: (params: {}) => Promise<any>,
comment_create: (params: {}) => Promise<any>,
// Wallet utilities
wallet_balance: (params: {}) => Promise<BalanceResponse>,
wallet_decrypt: (prams: {}) => Promise<boolean>,
wallet_encrypt: (params: {}) => Promise<boolean>,
wallet_unlock: (params: {}) => Promise<boolean>,
wallet_list: (params: {}) => Promise<WalletListResponse>,
wallet_send: (params: {}) => Promise<GenericTxResponse>,
wallet_status: (params?: {}) => Promise<WalletStatusResponse>,
address_is_mine: (params: {}) => Promise<boolean>,
address_unused: (params: {}) => Promise<string>, // New address
address_list: (params: {}) => Promise<string>,
transaction_list: (params: {}) => Promise<TxListResponse>,
txo_list: (params: {}) => Promise<any>,
account_set: (params: {}) => Promise<any>,
account_list: (params?: {}) => Promise<any>,
// Sync
sync_hash: (params?: {}) => Promise<string>,
sync_apply: (params: {}) => Promise<SyncApplyResponse>,
// syncGet
// The app shouldn't need to do this
utxo_release: () => Promise<any>,
};

View file

@ -1,99 +0,0 @@
// @flow
declare type LbryFirstStatusResponse = {
Version: string,
Message: string,
Running: boolean,
Commit: string,
};
declare type LbryFirstVersionResponse = {
build: string,
lbrynet_version: string,
os_release: string,
os_system: string,
platform: string,
processor: string,
python_version: string,
};
/* SAMPLE UPLOAD RESPONSE (FULL)
"Video": {
"etag": "\"Dn5xIderbhAnUk5TAW0qkFFir0M/xlGLrlTox7VFTRcR8F77RbKtaU4\"",
"id": "8InjtdvVmwE",
"kind": "youtube#video",
"snippet": {
"categoryId": "22",
"channelId": "UCXiVsGTU88fJjheB2rqF0rA",
"channelTitle": "Mark Beamer",
"liveBroadcastContent": "none",
"localized": {
"title": "my title"
},
"publishedAt": "2020-05-05T04:17:53.000Z",
"thumbnails": {
"default": {
"height": 90,
"url": "https://i9.ytimg.com/vi/8InjtdvVmwE/default.jpg?sqp=CMTQw_UF&rs=AOn4CLB6dlhZMSMrazDlWRsitPgCsn8fVw",
"width": 120
},
"high": {
"height": 360,
"url": "https://i9.ytimg.com/vi/8InjtdvVmwE/hqdefault.jpg?sqp=CMTQw_UF&rs=AOn4CLB-Je_7l6qvASRAR_bSGWZHaXaJWQ",
"width": 480
},
"medium": {
"height": 180,
"url": "https://i9.ytimg.com/vi/8InjtdvVmwE/mqdefault.jpg?sqp=CMTQw_UF&rs=AOn4CLCvSnDLqVznRNMKuvJ_0misY_chPQ",
"width": 320
}
},
"title": "my title"
},
"status": {
"embeddable": true,
"license": "youtube",
"privacyStatus": "private",
"publicStatsViewable": true,
"uploadStatus": "uploaded"
}
}
*/
declare type UploadResponse = {
Video: {
id: string,
snippet: {
channelId: string,
},
status: {
uploadStatus: string,
},
},
};
declare type HasYTAuthResponse = {
HashAuth: boolean,
};
declare type YTSignupResponse = {};
//
// Types used in the generic LbryFirst object that is exported
//
declare type LbryFirstTypes = {
isConnected: boolean,
connectPromise: ?Promise<any>,
connect: () => void,
lbryFirstConnectionString: string,
apiRequestHeaders: { [key: string]: string },
setApiHeader: (string, string) => void,
unsetApiHeader: string => void,
overrides: { [string]: ?Function },
setOverride: (string, Function) => void,
// LbryFirst Methods
stop: () => Promise<string>,
status: () => Promise<StatusResponse>,
version: () => Promise<VersionResponse>,
upload: any => Promise<?UploadResponse>,
hasYTAuth: string => Promise<HasYTAuthResponse>,
ytSignup: () => Promise<YTSignupResponse>,
};

View file

@ -1,136 +0,0 @@
// @flow
import * as ACTIONS from 'constants/action_types';
/*
Toasts:
- First-in, first-out queue
- Simple messages that are shown in response to user interactions
- Never saved
- If they are the result of errors, use the isError flag when creating
- For errors that should interrupt user behavior, use Error
*/
declare type ToastParams = {
message: string,
title?: string,
linkText?: string,
linkTarget?: string,
isError?: boolean,
};
declare type Toast = {
id: string,
params: ToastParams,
};
declare type DoToast = {
type: ACTIONS.CREATE_TOAST,
data: Toast,
};
/*
Notifications:
- List of notifications based on user interactions/app notifications
- Always saved, but can be manually deleted
- Can happen in the background, or because of user interaction (ex: publish confirmed)
*/
declare type Notification = {
id: string, // Unique id
dateCreated: number,
isRead: boolean, // Used to display "new" notifications that a user hasn't seen yet
source?: string, // The type/area an notification is from. Used for sorting (ex: publishes, transactions)
// We may want to use priority/isDismissed in the future to specify how urgent a notification is
// and if the user should see it immediately
// isDissmied: boolean,
// priority?: number
};
declare type DoNotification = {
type: ACTIONS.CREATE_NOTIFICATION,
data: Notification,
};
declare type DoEditNotification = {
type: ACTIONS.EDIT_NOTIFICATION,
data: {
notification: Notification,
},
};
declare type DoDeleteNotification = {
type: ACTIONS.DELETE_NOTIFICATION,
data: {
id: string, // The id to delete
},
};
/*
Errors:
- First-in, first-out queue
- Errors that should interupt user behavior
- For errors that can be shown without interrupting a user, use Toast with the isError flag
*/
declare type ErrorNotification = {
title: string,
text: string,
};
declare type DoError = {
type: ACTIONS.CREATE_ERROR,
data: ErrorNotification,
};
declare type DoDismissError = {
type: ACTIONS.DISMISS_ERROR,
};
/*
NotificationState
*/
declare type NotificationState = {
notifications: Array<Notification>,
errors: Array<ErrorNotification>,
toasts: Array<Toast>,
};
declare type WebNotification = {
active_at: string,
created_at: string,
id: number,
is_app_readable: boolean,
is_device_notified: boolean,
is_emailed: boolean,
is_read: boolean,
is_seen: boolean,
notification_parameters: {
device: {
analytics_label: string,
image_url: string,
is_data_only: boolean,
name: string,
placeholders: ?string,
target: string,
text: string,
title: string,
type: string,
},
dynamic: {
comment_author: string,
reply_author: string,
hash: string,
claim_title: string,
comment?: string,
channel_url: string,
},
email: {},
},
notification_rule: string,
type: string,
updated_at: string,
user_id: number,
group_count?: number,
};
declare type NotificationCategory = {
name: string,
types: ?Array<string>,
};

Some files were not shown because too many files have changed in this diff Show more