Compare commits

..

2 commits

Author SHA1 Message Date
DispatchCommit
4c7623f065 disable pointer events while animating 2021-01-29 17:50:19 -08:00
DispatchCommit
f991d5c87f Add dropdown animation
Fixed issue causing mis-clicks
2021-01-29 17:44:12 -08:00
1018 changed files with 37279 additions and 80074 deletions

View file

@ -1,29 +1,33 @@
####### .env.default #########
# Copy this file to .env to make modifications
# Base config
MATOMO_URL=https://analytics.lbry.com/
MATOMO_ID=4
WEBPACK_WEB_PORT=9090
WEBPACK_ELECTRON_PORT=9091
WEB_SERVER_PORT=1337
## APIS
LBRY_WEB_API=https://api.na-backend.odysee.com
LBRY_WEB_API=https://api.lbry.tv
LBRY_WEB_STREAMING_API=https://cdn.lbryplayer.xyz
LBRY_WEB_BUFFER_API=https://collector-service.api.lbry.tv/api/v1/events/video
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
WELCOME_VERSION=1.0
# STRIPE
# STRIPE_PUBLIC_KEY='pk_test_NoL1JWL7i1ipfhVId5KfDZgo'
# Custom Site info
DOMAIN=lbry.tv
URL=https://lbry.tv
THUMBNAIL_CDN_URL=https://image-optimizer.vanwanet.com/?address=
# Analytics
MATOMO_URL=https://analytics.lbry.com/
MATOMO_ID=4
# UI
SITE_TITLE=lbry.tv
SITE_NAME=lbry.tv
SITE_DESCRIPTION=Meet LBRY, an open, free, and community-controlled content wonderland.
LOGO_TITLE=lbry.tv
SIMPLE_SITE=false
SHOW_ADS=true
YRBL_HAPPY_IMG_URL=https://cdn.lbryplayer.xyz/api/v3/streams/free/yrbl-happy/7aa50a7e5adaf48691935d55e45d697547392929/839d9a
YRBL_SAD_IMG_URL=https://cdn.lbryplayer.xyz/api/v3/streams/free/yrbl-sad/c2d9649633d974e5ffb503925e1f17d951f1bd0f/f262dd
ENABLE_COMMENT_REACTIONS=true
ENABLE_FILE_REACTIONS=false
ENABLE_CREATOR_REACTIONS=false
# OG
OG_TITLE_SUFFIX=| lbry.tv
@ -31,79 +35,23 @@ OG_HOMEPAGE_TITLE=lbry.tv
OG_IMAGE_URL=
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
DEFAULT_LANGUAGE=en
## CUSTOM SETTINGS
# Custom Settings
# 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
# Custom Content
# If the following is true, copy custom/homepage.example.js to custom/homepage.js and modify
CUSTOM_HOMEPAGE=false
# Add channels to auto-follow on first run
AUTO_FOLLOW_CHANNELS=lbry://@lbry#3fda836a92faaceedfe398225fb9b2ee2ed1f01a
# 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
## 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

@ -35,14 +35,13 @@
"object-curly-spacing": 0,
"one-var": 0,
"prefer-promise-reject-errors": 0,
"promise/param-names": 0,
"react/jsx-indent": 0,
"react/jsx-no-comment-textnodes": 0,
"react-hooks/exhaustive-deps": "warn",
"react-hooks/rules-of-hooks": "error",
"react/no-unescaped-entities": 0,
"space-before-function-paren": [
"warn",
"error",
{
"anonymous": "never",
"named": "never",

View file

@ -1,14 +1,18 @@
[ignore]
.*\.typeface\.json
.*/node_modules/findup/.*
.*/node_modules/react-plastic/.*
.*/node_modules/raf-schd/.*
.*/node_modules/react-beautiful-dnd/.*
.*/node_modules/resolve/test/.*
[include]
[libs]
./flow-typed
node_modules/lbry-redux/flow-typed/
node_modules/lbryinc/flow-typed/
[untyped]
.*/node_modules/lbry-redux
.*/node_modules/lbryinc
[lints]
@ -26,27 +30,13 @@ module.name_mapper='^modal\(.*\)$' -> '<PROJECT_ROOT>/ui/modal\1'
module.name_mapper='^app\(.*\)$' -> '<PROJECT_ROOT>/ui/app\1'
module.name_mapper='^native\(.*\)$' -> '<PROJECT_ROOT>/ui/native\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='^i18n\(.*\)$' -> '<PROJECT_ROOT>/ui/i18n\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='^web\/component\(.*\)$' -> '<PROJECT_ROOT>/web/component\1'
module.name_mapper='^web\/effects\(.*\)$' -> '<PROJECT_ROOT>/web/effects\1'
module.name_mapper='^web\/page\(.*\)$' -> '<PROJECT_ROOT>/web/page\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]

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
<!-- 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?
@ -31,11 +19,14 @@ What kind of change does this PR introduce?
- [ ] Documentation changes
- [ ] 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)
- [ ] 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
Issue Number:
</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

@ -11,9 +11,8 @@ jobs:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: corepack enable
- run: yarn
- uses: actions/checkout@v2
- uses: Borales/actions-yarn@v2.3.0
- run: yarn lint
build:
@ -21,39 +20,23 @@ jobs:
name: 'build'
strategy:
matrix:
node-version: [16.x]
node-version: [12.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
- uses: actions/checkout@v2
- uses: actions/setup-node@v2-beta
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
xcode-version: '10.3.0'
- name: Download blockchain headers
run: |
@ -63,18 +46,18 @@ jobs:
- name: Build
run: |
yarn dlx cross-env
yarn --network-timeout 600000
yarn global add cross-env
yarn
yarn build
node ./build/afterSignHook.js
env:
GH_TOKEN: ${{ secrets.GH_TOKEN_NEW }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
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
WIN_CSC_LINK: https://s3.amazonaws.com/files.lbry.io/cert/win-csc-2020-2021-08.p12
CSC_LINK: https://s3.amazonaws.com/files.lbry.io/cert/osx-cert.p12
# UI
MATOMO_URL: https://analytics.lbry.com/
@ -86,42 +69,29 @@ jobs:
SITE_TITLE: lbry.tv
SITE_NAME: lbry.tv
SHOW_ADS: false
YRBL_HAPPY_IMG_URL: https://cdn.lbryplayer.xyz/api/v3/streams/free/yrbl-happy/7aa50a7e5adaf48691935d55e45d697547392929/839d9a
YRBL_SAD_IMG_URL: https://cdn.lbryplayer.xyz/api/v3/streams/free/yrbl-sad/c2d9649633d974e5ffb503925e1f17d951f1bd0f/f262dd
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
- uses: actions/upload-artifact@v2
if: |
startsWith(runner.os, 'linux')
with:
name: Linux
path: ./dist/electron/*.*
- uses: actions/upload-artifact@v2.2.4
- uses: actions/upload-artifact@v2
if: |
startsWith(runner.os, 'mac')
with:
name: macOS
path: ./dist/electron/*.*
- uses: actions/upload-artifact@v2.2.4
- uses: actions/upload-artifact@v2
if: |
startsWith(runner.os, 'windows')
github.event.pull_request.head.repo.full_name == github.repository
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'

13
.gitignore vendored
View file

@ -23,22 +23,9 @@ package-lock.json
/web/.env.defaults
/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/robots.disallowall
!/custom/robots.allowall
.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": {
"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"]
}

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,388 +1,21 @@
# Changelog
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/).
## [0.53.9] - [2023-2-8]
### 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]
## [Unreleased on desktop]
### 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]
@ -398,7 +31,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- 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))
- Improve how spendable balance is displayed ([#5276](https://github.com/lbryio/lbry-desktop/pull/5276))
### Fixed

View file

@ -1,19 +1,21 @@
# 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.
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?
- [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.
- Comment on an issue to let us know if you are going to work on it, don't take an issue that someone reserved less than 3 days ago.
- Submit a pull request and get paid in LBC.
- Don't hesitate to contact us with any questions or comments.
* Comment on an issue to let us know if you are going to work on it, don't take an issue that someone reserved less than 3 days ago.
* Submit a pull request and get paid in LBC.
* Don't hesitate to contact us with any questions or comments.
* [Check out this video for a quickstart guide to running and developing the desktop app](https://spee.ch/7da73fc508ffc4ff8b2711e3c3950110430b0c5f/LBRYAppDesign.mp4).
## Contents
- [Choose an Issue](#choose-an-issue)
- [Code Overview](#code-overview)
- [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:
- 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
add an issue if you think we have missed something (and you might earn some LBC in the process
because we do tip people for reporting bugs).
- Once on the [Issues](https://github.com/lbryio/lbry-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
[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.
- Every
* Every
[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.
| 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 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. |
@ -78,7 +80,7 @@ our styling rules and code best practices.
### 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
@ -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:
- [Chrome DevTools](https://developer.chrome.com/devtools)
- Also available for the main process as a [remote target](chrome://inspect/#devices).
- [Electron Devtron](https://electronjs.org/devtron)
- [React DevTools](https://github.com/facebook/react-devtools)
- [Redux DevTools](https://github.com/gaearon/redux-devtools)
* [Chrome DevTools](https://developer.chrome.com/devtools)
* Also available for the main process as a [remote target](chrome://inspect/#devices).
* [Electron Devtron](https://electronjs.org/devtron)
* [React DevTools](https://github.com/facebook/react-devtools)
* [Redux DevTools](https://github.com/gaearon/redux-devtools)
## Submit a Pull Request
- After deciding what to work on, a potential contributor can
* After deciding what to work on, a potential contributor can
[fork](https://help.github.com/articles/fork-a-repo/) this repository, make his or her changes,
and submit a
[pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/). A
@ -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.
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.
- 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.
- 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
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.
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
| 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 |
| [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 |
| 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 |
| [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/).

View file

@ -1,6 +1,6 @@
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
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" />
# 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 daemon](https://github.com/lbryio/lbry) bundled with a UI using
[Electron](https://electron.atom.io/).
@ -63,35 +63,98 @@ 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.
_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 |
| -------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------- |
| 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) |
| Maintainers | N/A | [@RubenKelevra](https://github.com/RubenKelevra) | [@Enderger](https://github.com/enderger) | [@Madiator2011](https://github.com/kodxana) |
| | Flatpak | Arch | ARM/ARM64 |
| -------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------- |
| Latest Release | [FlatHub Page](https://flathub.org/apps/details/io.lbry.lbry-app) | [AUR Package](https://aur.archlinux.org/packages/lbry-app-bin/) | [Build Guide](https://lbry.tv/@LBRYarm:5) |
| Maintainers | [@kcSeb](https://keybase.io/kcseb) | [@kcSeb](https://keybase.io/kcseb)/[@TimurKiyivinski](https://github.com/TimurKiyivinski) | [@Madiator2011](https://github.com/kodxana) |
## 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
You can run the web version (lbry.tv), the electron app, or both at the same time.
#### Prerequisites
- [Git](https://git-scm.com/downloads)
- [Node.js](https://nodejs.org/en/download/) (v16 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)
- [Node.js](https://nodejs.org/en/download/) (v10 required)
- [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`
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)
4. Install the dependencies: `yarn`
3. Install the dependencies: `yarn`
#### Run the electron app
`yarn compile:electron` (this is only needed the first time you run the app)
`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.
#### 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, duplicate the .env.default file and rename it to .env then copy the code below and paste it anywhere in the .env file.
```
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
- To specify your own channels to be followed on first run
`AUTO_FOLLOW_URLS=lbry://@chan#123...a lbry://@chan2#456...a`
- 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
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 +173,8 @@ This project is MIT licensed. For the full license, see [LICENSE](LICENSE).
## 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).
New Releases are signed by [Jessop Breth](https://keybase.io/jessopb/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).
## 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).

View file

@ -7,8 +7,6 @@ module.exports = api => {
'import-glob',
'@babel/plugin-transform-runtime',
['@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-proposal-class-properties',
'react-hot-loader/babel',

Binary file not shown.

Binary file not shown.

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

@ -8,16 +8,10 @@ const config = {
WEBPACK_WEB_PORT: process.env.WEBPACK_WEB_PORT,
WEBPACK_ELECTRON_PORT: process.env.WEBPACK_ELECTRON_PORT,
WEB_SERVER_PORT: process.env.WEB_SERVER_PORT,
LBRY_WEB_API: process.env.LBRY_WEB_API, //api.na-backend.odysee.com',
LBRY_WEB_PUBLISH_API: process.env.LBRY_WEB_PUBLISH_API,
LBRY_WEB_API: process.env.LBRY_WEB_API, //api.lbry.tv',
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_STREAMING_API: process.env.LBRY_WEB_STREAMING_API, //cdn.lbryplayer.xyz',
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,
DOMAIN: process.env.DOMAIN,
SHARE_DOMAIN_URL: process.env.SHARE_DOMAIN_URL,
@ -26,40 +20,19 @@ const config = {
SITE_TITLE: process.env.SITE_TITLE,
SITE_NAME: process.env.SITE_NAME,
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,
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_HOMEPAGE_TITLE: process.env.OG_HOMEPAGE_TITLE,
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,
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,
@ -67,16 +40,9 @@ const config = {
PINNED_URI_2: process.env.PINNED_URI_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_DEV = `http://localhost:${config.WEBPACK_WEB_PORT}`;
module.exports = config;

View file

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

View file

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

View file

@ -1,5 +1,5 @@
import { WEBPACK_ELECTRON_PORT } from 'config';
import { app, BrowserWindow, dialog, screen, nativeImage } from 'electron';
import { app, BrowserWindow, dialog, shell, screen, nativeImage } from 'electron';
import isDev from 'electron-is-dev';
import windowStateKeeper from 'electron-window-state';
import SUPPORTED_LANGUAGES from 'constants/supported_languages';
@ -9,8 +9,7 @@ import { TO_TRAY_WHEN_CLOSED } from 'constants/settings';
import setupBarMenu from './menu/setupBarMenu';
import * as PAGES from 'constants/pages';
const remote = require('@electron/remote/main');
const shell = require('electron').shell;
function GetAppLangCode() {
// https://www.electronjs.org/docs/api/locales
// 1. Gets the user locale.
@ -55,8 +54,6 @@ export default appState => {
webSecurity: !isDev,
plugins: true,
nodeIntegration: true,
contextIsolation: false,
enableRemoteModule: true, // see about removing this
},
};
const lbryProto = 'lbry://';
@ -64,7 +61,6 @@ export default appState => {
const rendererURL = isDev ? `http://localhost:${WEBPACK_ELECTRON_PORT}` : `file://${__dirname}/index.html`;
let window = new BrowserWindow(windowConfiguration);
remote.enable(window.webContents);
// Let us register listeners on the window, so we can update the state
// automatically (the listeners will be removed when the window is closed)
@ -95,7 +91,7 @@ export default appState => {
// is it a lbry://? pointing to an app page
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;
if (Object.values(PAGES).includes(page)) {
deepLinkingURI = deepLinkingURI.replace(lbryProtoQ, '#/$/');
@ -190,13 +186,9 @@ export default appState => {
window = null;
});
window.webContents.setWindowOpenHandler((details) => {
// Only open http and https links to prevent
// security issues.
if (['https:', 'http:'].includes(new URL(details.url).protocol)) {
shell.openExternal(details.url);
}
return { action: 'deny' };
window.webContents.on('new-window', (event, url) => {
event.preventDefault();
shell.openExternal(url);
});
window.webContents.on('update-target-url', (event, url) => {

View file

@ -4,9 +4,9 @@
import '@babel/polyfill';
import SemVer from 'semver';
import https from 'https';
import { app, dialog, ipcMain, session, shell, BrowserWindow } from 'electron';
import { app, dialog, ipcMain, session, shell } from 'electron';
import { autoUpdater } from 'electron-updater';
import Lbry from 'lbry';
import { Lbry } from 'lbry-redux';
import LbryFirstInstance from './LbryFirstInstance';
import Daemon from './Daemon';
import isDev from 'electron-is-dev';
@ -17,17 +17,6 @@ import startSandbox from './startSandbox';
import installDevtools from './installDevtools';
import fs from 'fs';
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');
let upgradeDisabled;
try {
@ -37,18 +26,11 @@ try {
upgradeDisabled = false;
}
autoUpdater.autoDownload = !upgradeDisabled;
autoUpdater.allowPrerelease = false;
const UPDATE_STATE_INIT = 0;
const UPDATE_STATE_CHECKING = 1;
const UPDATE_STATE_UPDATES_FOUND = 2;
const UPDATE_STATE_NO_UPDATES_FOUND = 3;
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 set to true if an auto update has been downloaded through the Electron
// auto-update system and is ready to install. If the user declined an update earlier,
// it will still install on shutdown.
let autoUpdateDownloaded = false;
// 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.
@ -63,17 +45,8 @@ let daemon;
let lbryFirst;
const appState = {};
const PROTOCOL = 'lbry';
if (isDev && process.platform === 'win32') {
// Setting this is required to get this working in dev mode.
app.setAsDefaultProtocolClient(PROTOCOL, process.execPath, [
path.resolve(process.argv[1]),
]);
} else if (process.platform !== 'linux') {
app.setAsDefaultProtocolClient(PROTOCOL);
}
app.setAsDefaultProtocolClient('lbry');
app.name = 'LBRY';
app.setAppUserModelId('io.lbry.LBRY');
app.commandLine.appendSwitch('force-color-profile', 'srgb');
@ -173,15 +146,9 @@ if (!gotSingleInstanceLock) {
app.on('second-instance', (event, argv) => {
// Send the url to the app to navigate first, then focus
if (rendererWindow) {
// External uri (last item on argv):
const EXTERNAL_URI = (argv.length) ? argv[argv.length - 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;
if ((process.platform === 'win32' || process.platform === 'linux') && String(argv[1]).startsWith('lbry')) {
let URI = argv[1];
// Keep only command line / deep linked arguments
// Windows normalizes URIs when they're passed in from other apps. On Windows, this tries to
// restore the original URI that was typed.
@ -241,8 +208,7 @@ app.on('activate', () => {
app.on('will-quit', event => {
if (
process.platform === 'win32' &&
updateState === UPDATE_STATE_DOWNLOADED &&
isAutoUpdateSupported &&
autoUpdateDownloaded &&
!appState.autoUpdateAccepted &&
!showingAutoUpdateCloseAlert
) {
@ -302,118 +268,27 @@ app.on('before-quit', () => {
appState.isQuitting = true;
});
// Get the content of a file as a raw buffer of bytes.
// Useful to convert a file path to a File instance.
// Example:
// const result = await ipcMain.invoke('get-file-from-path', 'path/to/file');
// const file = new File([result.buffer], result.name);
// NOTE: if path points to a folder, an empty
// 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,
});
});
});
ipcMain.on('upgrade', (event, installerPath) => {
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);
});
// 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) => {
const isFfMp4 = (ffprobeResults) => {
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' };
}
autoUpdater.on('update-downloaded', () => {
autoUpdateDownloaded = true;
});
ipcMain.on('get-disk-space', async (event) => {
try {
const { data_dir } = await Lbry.settings_get();
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('autoUpdateAccepted', () => {
appState.autoUpdateAccepted = true;
autoUpdater.quitAndInstall();
});
ipcMain.on('version-info-requested', () => {
@ -508,162 +383,3 @@ process.on('uncaughtException', error => {
if (daemon) daemon.quit();
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,4 +1,5 @@
// import express from 'express';
import express from 'express';
import unpackByOutpoint from './unpackByOutpoint';
// Polyfills and `lbry-redux`
global.fetch = require('node-fetch');
@ -7,31 +8,31 @@ if (typeof global.fetch === 'object') {
global.fetch = global.fetch.default;
}
// const Lbry = require('lbry');
const { Lbry } = require('lbry-redux');
delete global.window;
export default async function startSandbox() {
// const port = 5278;
// const sandbox = express();
const port = 5278;
const sandbox = express();
// sandbox.get('/set/:outpoint', async (req, res) => {
// const { outpoint } = req.params;
//
// const resolvedPath = await unpackByOutpoint(Lbry, outpoint);
//
// sandbox.use(`/sandbox/${outpoint}/`, express.static(resolvedPath));
//
// res.send(`/sandbox/${outpoint}/`);
// });
//
// sandbox
// .listen(port, 'localhost', () => console.log(`Sandbox listening on port ${port}.`))
// .on('error', err => {
// if (err.code === 'EADDRINUSE') {
// console.log(
// `Server already listening at localhost:${port}. This is probably another LBRY app running. If not, games in the app will not work.`
// );
// }
// });
sandbox.get('/set/:outpoint', async (req, res) => {
const { outpoint } = req.params;
const resolvedPath = await unpackByOutpoint(Lbry, outpoint);
sandbox.use(`/sandbox/${outpoint}/`, express.static(resolvedPath));
res.send(`/sandbox/${outpoint}/`);
});
sandbox
.listen(port, 'localhost', () => console.log(`Sandbox listening on port ${port}.`))
.on('error', err => {
if (err.code === 'EADDRINUSE') {
console.log(
`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,
}

272
flow-typed/Comment.js vendored
View file

@ -12,63 +12,21 @@ declare type 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.
byId: { [string]: Array<string> },
repliesByParentId: { [string]: Array<string> }, // ParentCommentID -> list of reply comments
topLevelCommentsById: { [string]: Array<string> }, // ClaimID -> list of top level comments
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, ...}}
myReactsByCommentId: any,
othersReactsByCommentId: any,
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 = {
@ -79,223 +37,3 @@ declare type CommentReactParams = {
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>,
};

6
flow-typed/Redux.js vendored
View file

@ -1,6 +0,0 @@
// @flow
/* eslint-disable no-use-before-define */
declare type GetState = () => any;
declare type ThunkAction = (dispatch: Dispatch, getState: GetState) => any;
declare type Dispatch = (action: {} | Promise<*> | Array<{}> | ThunkAction) => any; // Need to refer to ThunkAction
/* eslint-enable */

View file

@ -1,5 +0,0 @@
declare type ReflectingUpdate = {
fileListItem: FileListItem,
progress: number | boolean,
stalled: boolean,
};

View file

@ -1,13 +0,0 @@
declare type CommentServerDetails = {
name: string,
url: string,
}
declare type WalletServerDetails = {
};
declare type DiskSpace = {
total: number,
free: number,
};

21
flow-typed/Tags.js vendored
View file

@ -1,21 +0,0 @@
declare type TagState = {
followedTags: FollowedTags,
knownTags: KnownTags,
};
declare type Tag = {
name: string,
};
declare type KnownTags = {
[string]: Tag,
};
declare type FollowedTags = Array<string>;
declare type TagAction = {
type: string,
data: {
name: string,
},
};

View file

@ -1,28 +0,0 @@
// @flow
declare type Transaction = {
amount: number,
claim_id: string,
claim_name: string,
fee: number,
nout: number,
txid: string,
type: string,
date: Date,
};
declare type Support = {
address: string,
amount: string,
claim_id: string,
confirmations: number,
height: string,
is_change: string,
is_mine: string,
name: string,
normalized_name: string,
nout: string,
permanent_url: string,
timestamp: number,
txid: string,
type: string,
};

27
flow-typed/Txo.js vendored
View file

@ -1,27 +0,0 @@
declare type Txo = {
amount: number,
claim_id: string,
normalized_name: string,
nout: number,
txid: string,
type: string,
value_type: string,
timestamp: number,
is_my_output: boolean,
is_my_input: boolean,
is_spent: boolean,
signing_channel?: {
channel_id: string,
},
};
declare type TxoListParams = {
page: number,
page_size: number,
type: string,
is_my_input?: boolean,
is_my_output?: boolean,
is_not_my_input?: boolean,
is_not_my_output?: boolean,
is_spent?: boolean,
};

View file

@ -2,7 +2,6 @@
declare type PlayingUri = {
uri: string,
primaryUri: string,
pathname: string,
commentId?: string,
source?: string,

View file

@ -1,10 +0,0 @@
// @flow
declare type FileData = {
file?: Blob,
path: string,
duration?: number,
size?: number,
mimeType: string,
error?: string,
}

View file

@ -1,9 +0,0 @@
// @flow
declare type FileWithPath = {
file: File,
// The full path will only be available in
// the application. For browser, the name
// of the file will be used.
path: string,
}

View file

@ -4,12 +4,12 @@ declare type HomepageObject = {
options: any,
route: string,
title: string,
};
}
declare type HomepageData = {
[string]: HomepageObject,
default: any => any,
};
}
declare type RowDataItem = {
title: any,
@ -17,12 +17,9 @@ declare type RowDataItem = {
help?: any,
icon?: string,
extra?: any,
pinnedUrls?: Array<string>,
options?: {
channelIds?: Array<string>,
limitClaimsPerChannel?: number,
pageSize?: number,
languages?: Array<string>,
pageSize: number,
},
route?: string,
hideForUnauth?: boolean,

2
flow-typed/i18n.js vendored
View file

@ -1,2 +0,0 @@
// @flow
declare function __(a: string, b?: {}): string;

21
flow-typed/lbryURI.js vendored
View file

@ -1,21 +0,0 @@
// @flow
declare type LbryUrlObj = {
// Path and channel will always exist when calling parseURI
// But they may not exist when code calls buildURI
isChannel?: boolean,
path?: string,
streamName?: string,
streamClaimId?: string,
channelName?: string,
channelClaimId?: string,
primaryClaimSequence?: number,
secondaryClaimSequence?: number,
primaryBidPosition?: number,
secondaryBidPosition?: number,
startTime?: number,
// Below are considered deprecated and should not be used due to unreliableness with claim.canonical_url
claimName?: string,
claimId?: string,
contentName?: string,
};

View file

@ -1,41 +0,0 @@
// @flow
declare type LivestreamReplayItem = {
data: {
claimId: string,
deleted: boolean,
deletedAt: ?string,
ffprobe: any,
fileDuration: number, // decimal? float? string?
fileType: string,
fileLocation: string,
fileSize: number,
key: string,
published: boolean,
publishedAt: ?string,
service: string,
thumbnails: Array<string>,
uploadedAt: string, // Date?
},
id: string,
}
declare type LivestreamReplayData = Array<LivestreamReplayItem>;
declare type LivestreamState = {
fetchingById: {},
viewersById: {},
fetchingActiveLivestreams: boolean,
activeLivestreams: ?LivestreamInfo,
activeLivestreamsLastFetchedDate: number,
activeLivestreamsLastFetchedOptions: {},
}
declare type LivestreamInfo = {
[/* creatorId */ string]: {
live: boolean,
viewCount: number,
creatorId: string,
latestClaimId: string,
latestClaimUri: string,
}
}

38
flow-typed/notification.js vendored Normal file
View file

@ -0,0 +1,38 @@
// @flow
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,
};

View file

@ -11,7 +11,6 @@ declare type UpdatePublishFormData = {
thumbnail_url?: string,
uploadThumbnailStatus?: string,
thumbnailPath?: string,
thumbnailError?: boolean,
description?: string,
language?: string,
channel?: string,
@ -25,7 +24,7 @@ declare type UpdatePublishFormData = {
licenseType?: string,
uri?: string,
nsfw: boolean,
isMarkdownPost?: boolean,
isMarkdownPost: boolean,
};
declare type PublishParams = {

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