diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 2a549f0da..7ac7f60ee 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -24,7 +24,7 @@ jobs: os: - ubuntu-latest - macos-latest - - windows-latest +# - windows-latest runs-on: ${{ matrix.os }} steps: @@ -61,7 +61,7 @@ jobs: ARTIFACTS_SECRET: ${{ secrets.ARTIFACTS_SECRET }} ARTIFACTS_REGION: ${{ secrets.ARTIFACTS_REGION }} - WIN_CSC_LINK: https://raw.githubusercontent.com/lbryio/lbry-desktop/master/build/cert-2021-2022.pfx + WIN_CSC_LINK: https://raw.githubusercontent.com/lbryio/lbry-desktop/master/build/win-csc-2021-2022-08.p12 CSC_LINK: https://s3.amazonaws.com/files.lbry.io/cert/osx-csc-2021-2022.p12 # UI diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a5987b08..8ef9494f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,52 +4,7 @@ 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/). ## [Unreleased for Desktop] -## [0.52.1] - [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.52.0] - [2021-12-31] +## [0.52.0] - [2021-10-24] ### 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 diff --git a/build/cert-2021-2022.pfx b/build/cert-2021-2022.pfx deleted file mode 100644 index ddd4358f4..000000000 Binary files a/build/cert-2021-2022.pfx and /dev/null differ diff --git a/build/win-csc-2021-2022-08.p12 b/build/win-csc-2021-2022-08.p12 new file mode 100644 index 000000000..6c40f2490 Binary files /dev/null and b/build/win-csc-2021-2022-08.p12 differ diff --git a/electron/createWindow.js b/electron/createWindow.js index 042fae9b1..399c8ac82 100644 --- a/electron/createWindow.js +++ b/electron/createWindow.js @@ -1,5 +1,5 @@ import { WEBPACK_ELECTRON_PORT } from 'config'; -import { app, BrowserWindow, dialog, shell, screen, nativeImage } from 'electron'; +import { app, BrowserWindow, dialog, screen, nativeImage } from 'electron'; import isDev from 'electron-is-dev'; import windowStateKeeper from 'electron-window-state'; import SUPPORTED_LANGUAGES from 'constants/supported_languages'; @@ -9,7 +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'); function GetAppLangCode() { // https://www.electronjs.org/docs/api/locales // 1. Gets the user locale. @@ -54,6 +54,7 @@ export default appState => { webSecurity: !isDev, plugins: true, nodeIntegration: true, + contextIsolation: false, enableRemoteModule: true, // see about removing this }, }; @@ -62,6 +63,7 @@ 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) @@ -187,9 +189,8 @@ export default appState => { window = null; }); - window.webContents.on('new-window', (event, url) => { - event.preventDefault(); - shell.openExternal(url); + window.webContents.setWindowOpenHandler((details) => { + return { action: 'deny' }; }); window.webContents.on('update-target-url', (event, url) => { diff --git a/electron/index.js b/electron/index.js index b73653280..a40822e4e 100644 --- a/electron/index.js +++ b/electron/index.js @@ -18,6 +18,8 @@ import installDevtools from './installDevtools'; import fs from 'fs'; import path from 'path'; const { download } = require('electron-dl'); +const remote = require('@electron/remote/main'); +remote.initialize(); const filePath = path.join(process.resourcesPath, 'static', 'upgradeDisabled'); let upgradeDisabled; try { diff --git a/extras/lbryinc/index.js b/extras/lbryinc/index.js index 2d86de5fd..1b23baae0 100644 --- a/extras/lbryinc/index.js +++ b/extras/lbryinc/index.js @@ -50,12 +50,9 @@ export { export { selectFilteredOutpoints, selectFilteredOutpointMap } from './redux/selectors/filtered'; export { selectViewCount, - selectViewCountForUri, - // makeSelectViewCountForUri, // deprecated - selectSubCountForUri, - // makeSelectSubCountForUri, // deprecated + makeSelectViewCountForUri, + makeSelectSubCountForUri, } from './redux/selectors/stats'; -export { selectBanStateForUri } from './redux/selectors/ban'; export { selectHasSyncedWallet, selectSyncData, diff --git a/extras/lbryinc/redux/selectors/ban.js b/extras/lbryinc/redux/selectors/ban.js deleted file mode 100644 index ee0e2b0a0..000000000 --- a/extras/lbryinc/redux/selectors/ban.js +++ /dev/null @@ -1,68 +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 } 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, - (claim, blackListedOutpointMap, filteredOutpointMap, mutedChannelUris, personalBlocklist) => { - const banState = {}; - - if (!claim) { - return banState; - } - - const channelClaim = getChannelFromClaim(claim); - - // 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)); diff --git a/extras/lbryinc/redux/selectors/stats.js b/extras/lbryinc/redux/selectors/stats.js index 755402c07..2b2065285 100644 --- a/extras/lbryinc/redux/selectors/stats.js +++ b/extras/lbryinc/redux/selectors/stats.js @@ -1,20 +1,20 @@ -// @flow import { createSelector } from 'reselect'; -import { selectClaimIdForUri } from 'redux/selectors/claims'; +import { makeSelectClaimForUri } 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 makeSelectViewCountForUri = uri => + createSelector( + makeSelectClaimForUri(uri), + selectViewCount, + (claim, viewCountById) => (claim ? viewCountById[claim.claim_id] || 0 : 0) + ); -export const selectSubCountForUri = (state: State, uri: string) => { - const claimId = selectClaimIdForUri(state, uri); - const subCountById = selectSubCount(state); - return claimId ? subCountById[claimId] || 0 : 0; -}; +export const makeSelectSubCountForUri = uri => + createSelector( + makeSelectClaimForUri(uri), + selectSubCount, + (claim, subCountById) => (claim ? subCountById[claim.claim_id] || 0 : 0) + ); diff --git a/package.json b/package.json index 63ad2964b..471abdb61 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lbry", - "version": "0.52.1-alpha.1", + "version": "0.52.0-alpha.10", "description": "A browser for the LBRY network, a digital marketplace controlled by its users.", "keywords": [ "lbry" @@ -44,6 +44,7 @@ "postinstall:warning": "echo '\n\nWARNING\n\nNot all node modules were installed because NODE_ENV is set to \"production\".\nThis should only be set after installing dependencies with \"yarn\". The app will not work.\n\n'" }, "dependencies": { + "@electron/remote": "^2.0.1", "@ungap/from-entries": "^0.2.1", "auto-launch": "^5.0.5", "electron-dl": "^3.2.0", @@ -54,7 +55,6 @@ "humanize-duration": "^3.27.0", "if-env": "^1.0.4", "match-sorter": "^6.3.0", - "node-html-parser": "^5.1.0", "parse-duration": "^1.0.0", "proxy-polyfill": "0.1.6", "re-reselect": "^4.0.0", @@ -81,7 +81,7 @@ "@babel/preset-react": "^7.0.0", "@babel/register": "^7.0.0", "@datapunt/matomo-tracker-js": "^0.1.4", - "@exponent/electron-cookies": "^2.0.0", + "@meetfranz/electron-cookies": "^3.0.2", "@hot-loader/react-dom": "^16.13", "@reach/auto-id": "^0.13.0", "@reach/combobox": "^0.12.1", @@ -122,7 +122,7 @@ "dom-scroll-into-view": "^1.2.1", "dotenv-defaults": "^2.0.1", "dotenv-webpack": "^1.8.0", - "electron": "11.5.0", + "electron": "15.3.2", "electron-builder": "^22.9.1", "electron-devtools-installer": "^3.1.1", "electron-is-dev": "^0.3.0", diff --git a/static/app-strings.json b/static/app-strings.json index 0d2718541..515bb7550 100644 --- a/static/app-strings.json +++ b/static/app-strings.json @@ -2235,8 +2235,5 @@ "Network Data Hosting allows the p2p network to store blobs unrelated to your browsing.": "Network Data Hosting allows the p2p network to store blobs unrelated to your browsing.", "Content: Limit (GB)": "Content: Limit (GB)", "Network: Allow (GB)": "Network: Allow (GB)", - "Failed to view lbry://@Destiny#6/destiny-crashes-conservative-panel-w#a, please try again. If this problem persists, visit https://lbry.com/faq/support for support.": "Failed to view lbry://@Destiny#6/destiny-crashes-conservative-panel-w#a, please try again. If this problem persists, visit https://lbry.com/faq/support for support.", - "A channel is required to repost on LBRY": "A channel is required to repost on LBRY", - "Failed to view lbry://@gatogalactico#9/gato-galactico-e-as-estrelas-ninja-dos#1, please try again. If this problem persists, visit https://lbry.com/faq/support for support.": "Failed to view lbry://@gatogalactico#9/gato-galactico-e-as-estrelas-ninja-dos#1, please try again. If this problem persists, visit https://lbry.com/faq/support for support.", "--end--": "--end--" } diff --git a/ui/analytics.js b/ui/analytics.js index f90644ae7..d93dd82e3 100644 --- a/ui/analytics.js +++ b/ui/analytics.js @@ -6,7 +6,7 @@ import { history } from './store'; import { SDK_API_PATH } from './index'; // @if TARGET='app' import Native from 'native'; -import ElectronCookies from '@exponent/electron-cookies'; +import ElectronCookies from '@meetfranz/electron-cookies'; import { generateInitialUrl } from 'util/url'; // @endif import { MATOMO_ID, MATOMO_URL } from 'config'; diff --git a/ui/component/channelBlockButton/index.js b/ui/component/channelBlockButton/index.js index 4018ebcc6..9852404d7 100644 --- a/ui/component/channelBlockButton/index.js +++ b/ui/component/channelBlockButton/index.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { selectClaimIdForUri } from 'redux/selectors/claims'; +import { makeSelectClaimIdForUri } from 'redux/selectors/claims'; import { doCommentModUnBlock, doCommentModBlock, @@ -43,7 +43,7 @@ const select = (state, props) => { isBlocked, isToggling, isBlockingOrUnBlocking: makeSelectUriIsBlockingOrUnBlocking(props.uri)(state), - creatorId: selectClaimIdForUri(state, props.creatorUri), + creatorId: makeSelectClaimIdForUri(props.creatorUri)(state), }; }; diff --git a/ui/component/channelContent/index.js b/ui/component/channelContent/index.js index 46e9b29eb..fc1a349f0 100644 --- a/ui/component/channelContent/index.js +++ b/ui/component/channelContent/index.js @@ -3,9 +3,9 @@ import { PAGE_SIZE } from 'constants/claim'; import { makeSelectClaimsInChannelForPage, makeSelectFetchingChannelClaims, - selectClaimIsMine, + makeSelectClaimIsMine, makeSelectTotalPagesInChannelSearch, - selectClaimForUri, + makeSelectClaimForUri, } from 'redux/selectors/claims'; import { doResolveUris } from 'redux/actions/claims'; import * as SETTINGS from 'constants/settings'; @@ -20,15 +20,13 @@ const select = (state, props) => { const { search } = props.location; const urlParams = new URLSearchParams(search); const page = urlParams.get('page') || 0; - const claim = props.uri && selectClaimForUri(state, props.uri); - return { pageOfClaimsInChannel: makeSelectClaimsInChannelForPage(props.uri, page)(state), fetching: makeSelectFetchingChannelClaims(props.uri)(state), totalPages: makeSelectTotalPagesInChannelSearch(props.uri, PAGE_SIZE)(state), - channelIsMine: selectClaimIsMine(state, claim), + channelIsMine: makeSelectClaimIsMine(props.uri)(state), channelIsBlocked: makeSelectChannelIsMuted(props.uri)(state), - claim, + claim: props.uri && makeSelectClaimForUri(props.uri)(state), isAuthenticated: selectUserVerifiedEmail(state), showMature: selectShowMatureContent(state), tileLayout: makeSelectClientSetting(SETTINGS.TILE_LAYOUT)(state), diff --git a/ui/component/channelStakedIndicator/index.js b/ui/component/channelStakedIndicator/index.js index 3a1a41107..d217012a4 100644 --- a/ui/component/channelStakedIndicator/index.js +++ b/ui/component/channelStakedIndicator/index.js @@ -1,15 +1,15 @@ import { connect } from 'react-redux'; import { makeSelectClaimForUri, - selectTotalStakedAmountForChannelUri, - selectStakedLevelForChannelUri, + makeSelectStakedLevelForChannelUri, + makeSelectTotalStakedAmountForChannelUri, } from 'redux/selectors/claims'; import ChannelStakedIndicator from './view'; const select = (state, props) => ({ channelClaim: makeSelectClaimForUri(props.uri)(state), - amount: selectTotalStakedAmountForChannelUri(state, props.uri), - level: selectStakedLevelForChannelUri(state, props.uri), + amount: makeSelectTotalStakedAmountForChannelUri(props.uri)(state), + level: makeSelectStakedLevelForChannelUri(props.uri)(state), }); export default connect(select)(ChannelStakedIndicator); diff --git a/ui/component/claimMenuList/index.js b/ui/component/claimMenuList/index.js index 847270381..2f00b8766 100644 --- a/ui/component/claimMenuList/index.js +++ b/ui/component/claimMenuList/index.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { selectClaimForUri, selectClaimIsMine } from 'redux/selectors/claims'; +import { makeSelectClaimForUri, makeSelectClaimIsMine } from 'redux/selectors/claims'; import { doCollectionEdit, doFetchItemsInCollection } from 'redux/actions/collections'; import { doPrepareEdit } from 'redux/actions/publish'; import { @@ -34,7 +34,7 @@ import ClaimPreview from './view'; import fs from 'fs'; const select = (state, props) => { - const claim = selectClaimForUri(state, props.uri, false); // @KP test no repost! + const claim = makeSelectClaimForUri(props.uri, false)(state); const collectionId = props.collectionId; const repostedClaim = claim && claim.reposted_claim; const contentClaim = repostedClaim || claim; @@ -51,7 +51,7 @@ const select = (state, props) => { contentClaim, contentSigningChannel, contentChannelUri, - claimIsMine: selectClaimIsMine(state, claim), + claimIsMine: makeSelectClaimIsMine(props.uri)(state), hasClaimInWatchLater: makeSelectCollectionForIdHasClaimUrl( COLLECTIONS_CONSTS.WATCH_LATER_ID, contentPermanentUri @@ -92,7 +92,7 @@ const perform = (dispatch) => ({ doChannelUnmute: (channelUri) => dispatch(doChannelUnmute(channelUri)), doCommentModBlock: (channelUri) => dispatch(doCommentModBlock(channelUri)), doCommentModUnBlock: (channelUri) => dispatch(doCommentModUnBlock(channelUri)), - doCommentModBlockAsAdmin: (a, b, c) => dispatch(doCommentModBlockAsAdmin(a, b, c)), + doCommentModBlockAsAdmin: (commenterUri, blockerId) => dispatch(doCommentModBlockAsAdmin(commenterUri, blockerId)), doCommentModUnBlockAsAdmin: (commenterUri, blockerId) => dispatch(doCommentModUnBlockAsAdmin(commenterUri, blockerId)), doChannelSubscribe: (subscription) => dispatch(doChannelSubscribe(subscription)), diff --git a/ui/component/claimPreview/index.js b/ui/component/claimPreview/index.js index 65efab5d6..25ab46a8b 100644 --- a/ui/component/claimPreview/index.js +++ b/ui/component/claimPreview/index.js @@ -2,7 +2,7 @@ import { connect } from 'react-redux'; import { selectClaimForUri, makeSelectIsUriResolving, - selectClaimIsMine, + makeSelectClaimIsMine, makeSelectClaimIsPending, makeSelectClaimIsNsfw, makeSelectReflectingClaimForUri, @@ -20,10 +20,12 @@ import { import { doResolveUri } from 'redux/actions/claims'; import { doCollectionEdit } from 'redux/actions/collections'; import { doFileGet } from 'redux/actions/file'; -import { selectBanStateForUri } from 'lbryinc'; +import { selectMutedChannels, makeSelectChannelIsMuted } from 'redux/selectors/blocked'; +import { selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc'; import { selectShowMatureContent } from 'redux/selectors/settings'; import { makeSelectHasVisitedUri } from 'redux/selectors/content'; import { makeSelectIsSubscribed } from 'redux/selectors/subscriptions'; +import { selectModerationBlockList } from 'redux/selectors/comments'; import ClaimPreview from './view'; import formatMediaDuration from 'util/formatMediaDuration'; @@ -40,12 +42,16 @@ const select = (state, props) => { pending: props.uri && makeSelectClaimIsPending(props.uri)(state), reflectingProgress: props.uri && makeSelectReflectingClaimForUri(props.uri)(state), obscureNsfw: selectShowMatureContent(state) === false, - claimIsMine: props.uri && selectClaimIsMine(state, claim), + claimIsMine: props.uri && makeSelectClaimIsMine(props.uri)(state), isResolvingUri: props.uri && makeSelectIsUriResolving(props.uri)(state), isResolvingRepost: props.uri && makeSelectIsUriResolving(props.repostUrl)(state), nsfw: props.uri && makeSelectClaimIsNsfw(props.uri)(state), - banState: selectBanStateForUri(state, props.uri), + blackListedOutpoints: selectBlackListedOutpoints(state), + filteredOutpoints: selectFilteredOutpoints(state), + mutedUris: selectMutedChannels(state), + blockedUris: selectModerationBlockList(state), hasVisitedUri: props.uri && makeSelectHasVisitedUri(props.uri)(state), + channelIsBlocked: props.uri && makeSelectChannelIsMuted(props.uri)(state), isSubscribed: props.uri && makeSelectIsSubscribed(props.uri, true)(state), streamingUrl: props.uri && makeSelectStreamingUrlForUri(props.uri)(state), wasPurchased: props.uri && makeSelectClaimWasPurchased(props.uri)(state), diff --git a/ui/component/claimPreview/view.jsx b/ui/component/claimPreview/view.jsx index b85ce32aa..8903c28c2 100644 --- a/ui/component/claimPreview/view.jsx +++ b/ui/component/claimPreview/view.jsx @@ -4,7 +4,7 @@ import React, { useEffect, forwardRef } from 'react'; import { NavLink, withRouter } from 'react-router-dom'; import { isEmpty } from 'util/object'; import classnames from 'classnames'; -import { isURIValid } from 'util/lbryURI'; +import { isURIEqual, isURIValid } from 'util/lbryURI'; import * as COLLECTIONS_CONSTS from 'constants/collections'; import { formatLbryUrlForWeb } from 'util/url'; import { formatClaimPreviewTitle } from 'util/formatAriaLabel'; @@ -46,8 +46,17 @@ type Props = { nsfw: boolean, placeholder: string, type: string, - banState: { blacklisted?: boolean, filtered?: boolean, muted?: boolean, blocked?: boolean }, hasVisitedUri: boolean, + blackListedOutpoints: Array<{ + txid: string, + nout: number, + }>, + filteredOutpoints: Array<{ + txid: string, + nout: number, + }>, + mutedUris: Array, + blockedUris: Array, channelIsBlocked: boolean, actions: boolean | Node | string | number, properties: boolean | Node | string | number | ((Claim) => Node), @@ -122,7 +131,10 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { properties, onClick, actions, - banState, + mutedUris, + blockedUris, + blackListedOutpoints, + filteredOutpoints, includeSupportAction, renderActions, hideMenu = false, @@ -224,13 +236,28 @@ const ClaimPreview = forwardRef((props: Props, ref: any) => { ((abandoned && !showUnresolvedClaim) || (!claimIsMine && obscureNsfw && nsfw)); // This will be replaced once blocking is done at the wallet server level - if (claim && !claimIsMine && (banState.blacklisted || banState.filtered)) { - shouldHide = true; + if (claim && !claimIsMine && !shouldHide && blackListedOutpoints) { + shouldHide = blackListedOutpoints.some( + (outpoint) => + (signingChannel && outpoint.txid === signingChannel.txid && outpoint.nout === signingChannel.nout) || + (outpoint.txid === claim.txid && outpoint.nout === claim.nout) + ); + } + // We're checking to see if the stream outpoint + // or signing channel outpoint is in the filter list + if (claim && !claimIsMine && !shouldHide && filteredOutpoints) { + shouldHide = filteredOutpoints.some( + (outpoint) => + (signingChannel && outpoint.txid === signingChannel.txid && outpoint.nout === signingChannel.nout) || + (outpoint.txid === claim.txid && outpoint.nout === claim.nout) + ); } - // block stream claims - if (!shouldHide && !showUserBlocked && (banState.muted || banState.blocked)) { - shouldHide = true; + if (claim && !shouldHide && !showUserBlocked && mutedUris.length && signingChannel) { + shouldHide = mutedUris.some((blockedUri) => isURIEqual(blockedUri, signingChannel.permanent_url)); + } + if (claim && !shouldHide && !showUserBlocked && blockedUris.length && signingChannel) { + shouldHide = blockedUris.some((blockedUri) => isURIEqual(blockedUri, signingChannel.permanent_url)); } if (!shouldHide && customShouldHide && claim) { diff --git a/ui/component/claimPreviewTile/index.js b/ui/component/claimPreviewTile/index.js index 7d1e558d4..eaf64a865 100644 --- a/ui/component/claimPreviewTile/index.js +++ b/ui/component/claimPreviewTile/index.js @@ -11,7 +11,7 @@ import { import { doFileGet } from 'redux/actions/file'; import { doResolveUri } from 'redux/actions/claims'; import { selectMutedChannels } from 'redux/selectors/blocked'; -import { selectViewCountForUri, selectBanStateForUri } from 'lbryinc'; +import { makeSelectViewCountForUri, selectBlackListedOutpoints, selectFilteredOutpoints } from 'lbryinc'; import { selectShowMatureContent } from 'redux/selectors/settings'; import ClaimPreviewTile from './view'; import formatMediaDuration from 'util/formatMediaDuration'; @@ -29,11 +29,12 @@ const select = (state, props) => { isResolvingUri: props.uri && makeSelectIsUriResolving(props.uri)(state), thumbnail: props.uri && makeSelectThumbnailForUri(props.uri)(state), title: props.uri && makeSelectTitleForUri(props.uri)(state), - banState: selectBanStateForUri(state, props.uri), + blackListedOutpoints: selectBlackListedOutpoints(state), + filteredOutpoints: selectFilteredOutpoints(state), blockedChannelUris: selectMutedChannels(state), showMature: selectShowMatureContent(state), isMature: makeSelectClaimIsNsfw(props.uri)(state), - viewCount: selectViewCountForUri(state, props.uri), + viewCount: makeSelectViewCountForUri(props.uri)(state), }; }; diff --git a/ui/component/claimPreviewTile/view.jsx b/ui/component/claimPreviewTile/view.jsx index f68fc1c22..1c921f64e 100644 --- a/ui/component/claimPreviewTile/view.jsx +++ b/ui/component/claimPreviewTile/view.jsx @@ -12,7 +12,7 @@ import SubscribeButton from 'component/subscribeButton'; import useGetThumbnail from 'effects/use-get-thumbnail'; import { formatLbryUrlForWeb, generateListSearchUrlParams } from 'util/url'; import { formatClaimPreviewTitle } from 'util/formatAriaLabel'; -import { parseURI } from 'util/lbryURI'; +import { parseURI, isURIEqual } from 'util/lbryURI'; import PreviewOverlayProperties from 'component/previewOverlayProperties'; import FileDownloadLink from 'component/fileDownloadLink'; import FileWatchLaterLink from 'component/fileWatchLaterLink'; @@ -33,7 +33,15 @@ type Props = { thumbnail: string, title: string, placeholder: boolean, - banState: { blacklisted?: boolean, filtered?: boolean, muted?: boolean, blocked?: boolean }, + blackListedOutpoints: Array<{ + txid: string, + nout: number, + }>, + filteredOutpoints: Array<{ + txid: string, + nout: number, + }>, + blockedChannelUris: Array, getFile: (string) => void, streamingUrl: string, isMature: boolean, @@ -56,9 +64,11 @@ function ClaimPreviewTile(props: Props) { resolveUri, claim, placeholder, - banState, + blackListedOutpoints, + filteredOutpoints, getFile, streamingUrl, + blockedChannelUris, isMature, showMature, showHiddenByUser, @@ -129,9 +139,34 @@ function ClaimPreviewTile(props: Props) { // Unfortunately needed until this is resolved // https://github.com/lbryio/lbry-sdk/issues/2785 shouldHide = true; - } else { - shouldHide = - banState.blacklisted || banState.filtered || (!showHiddenByUser && (banState.muted || banState.blocked)); + } + + // This will be replaced once blocking is done at the wallet server level + if (claim && !shouldHide && blackListedOutpoints) { + shouldHide = blackListedOutpoints.some( + (outpoint) => + (signingChannel && outpoint.txid === signingChannel.txid && outpoint.nout === signingChannel.nout) || + (outpoint.txid === claim.txid && outpoint.nout === claim.nout) + ); + } + // We're checking to see if the stream outpoint + // or signing channel outpoint is in the filter list + if (claim && !shouldHide && filteredOutpoints) { + shouldHide = filteredOutpoints.some( + (outpoint) => + (signingChannel && outpoint.txid === signingChannel.txid && outpoint.nout === signingChannel.nout) || + (outpoint.txid === claim.txid && outpoint.nout === claim.nout) + ); + } + + // block stream claims + if (claim && !shouldHide && !showHiddenByUser && blockedChannelUris.length && signingChannel) { + shouldHide = blockedChannelUris.some((blockedUri) => isURIEqual(blockedUri, signingChannel.permanent_url)); + } + // block channel claims if we can't control for them in claim search + // e.g. fetchRecommendedSubscriptions + if (claim && isChannel && !shouldHide && !showHiddenByUser && blockedChannelUris.length && signingChannel) { + shouldHide = blockedChannelUris.some((blockedUri) => isURIEqual(blockedUri, signingChannel.permanent_url)); } if (shouldHide) { @@ -245,4 +280,34 @@ function ClaimPreviewTile(props: Props) { ); } -export default withRouter(ClaimPreviewTile); +export default React.memo(withRouter(ClaimPreviewTile), areEqual); + +const BLOCKLIST_KEYS = ['blackListedOutpoints', 'filteredOutpoints', 'blockedChannelUris']; +const HANDLED_KEYS = [...BLOCKLIST_KEYS, 'date']; + +function areEqual(prev: Props, next: Props) { + for (let i = 0; i < BLOCKLIST_KEYS.length; ++i) { + const key = BLOCKLIST_KEYS[i]; + const a = prev[key]; + const b = next[key]; + + if (((!a || !b) && a !== b) || (a && b && a.length !== b.length)) { + // The arrays are huge, so just compare the length instead of each entry. + return false; + } + } + + if (Number(prev.date) !== Number(next.date)) { + return false; + } + + const propKeys = Object.keys(next); + for (let i = 0; i < propKeys.length; ++i) { + const pk = propKeys[i]; + if (!HANDLED_KEYS.includes(pk) && prev[pk] !== next[pk]) { + return false; + } + } + + return true; +} diff --git a/ui/component/claimProperties/index.js b/ui/component/claimProperties/index.js index 3b5f3edf6..574ceb0e1 100644 --- a/ui/component/claimProperties/index.js +++ b/ui/component/claimProperties/index.js @@ -1,16 +1,12 @@ import { connect } from 'react-redux'; -import { selectClaimIsMine, selectClaimForUri } from 'redux/selectors/claims'; +import { makeSelectClaimIsMine, makeSelectClaimForUri } from 'redux/selectors/claims'; import { makeSelectIsSubscribed } from 'redux/selectors/subscriptions'; import ClaimProperties from './view'; -const select = (state, props) => { - const claim = selectClaimForUri(state, props.uri); - - return { - claim, - isSubscribed: makeSelectIsSubscribed(props.uri)(state), - claimIsMine: selectClaimIsMine(state, claim), - }; -}; +const select = (state, props) => ({ + claim: makeSelectClaimForUri(props.uri)(state), + isSubscribed: makeSelectIsSubscribed(props.uri)(state), + claimIsMine: makeSelectClaimIsMine(props.uri)(state), +}); export default connect(select, null)(ClaimProperties); diff --git a/ui/component/collectionActions/view.jsx b/ui/component/collectionActions/view.jsx index 0a06d9bb7..73576bf1c 100644 --- a/ui/component/collectionActions/view.jsx +++ b/ui/component/collectionActions/view.jsx @@ -55,20 +55,6 @@ function CollectionActions(props: Props) { const claimId = claim && claim.claim_id; const webShareable = true; // collections have cost? - /* - A bit too much dependency with both ordering and shuffling depending on a single list item index selector - For now when they click edit, we'll toggle shuffle off for them. - */ - const handleSetShowEdit = (setting) => { - doToggleShuffleList(collectionId, false); - setShowEdit(setting); - }; - - const handlePublishMode = () => { - doToggleShuffleList(collectionId, false); - push(`?${PAGE_VIEW_QUERY}=${EDIT_PAGE}`); - }; - const doPlay = React.useCallback( (playUri) => { const navigateUrl = formatLbryUrlForWeb(playUri); @@ -138,7 +124,7 @@ function CollectionActions(props: Props) { title={uri ? __('Update') : __('Publish')} label={uri ? __('Update') : __('Publish')} className={classnames('button--file-action')} - onClick={() => handlePublishMode()} + onClick={() => push(`?${PAGE_VIEW_QUERY}=${EDIT_PAGE}`)} icon={ICONS.PUBLISH} iconColor={collectionHasEdits && 'red'} iconSize={18} @@ -183,7 +169,7 @@ function CollectionActions(props: Props) { 'button-toggle--active': showEdit, })} icon={ICONS.EDIT} - onClick={() => handleSetShowEdit(!showEdit)} + onClick={() => setShowEdit(!showEdit)} /> ); @@ -202,10 +188,12 @@ function CollectionActions(props: Props) { {lhsSection} {rhsSection} -
- {uri && infoButton} - {showEditButton} -
+ {uri && ( +
+ {infoButton} + {showEditButton} +
+ )} ); } diff --git a/ui/component/collectionContentSidebar/index.js b/ui/component/collectionContentSidebar/index.js index e65f79f84..e07304052 100644 --- a/ui/component/collectionContentSidebar/index.js +++ b/ui/component/collectionContentSidebar/index.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import CollectionContent from './view'; -import { selectClaimForUri, selectClaimIsMine } from 'redux/selectors/claims'; +import { makeSelectClaimForUri, makeSelectClaimIsMine } from 'redux/selectors/claims'; import { makeSelectUrlsForCollectionId, makeSelectNameForCollectionId, @@ -12,7 +12,7 @@ import { doToggleLoopList, doToggleShuffleList } from 'redux/actions/content'; const select = (state, props) => { const playingUri = selectPlayingUri(state); const playingUrl = playingUri && playingUri.uri; - const claim = selectClaimForUri(state, playingUrl); + const claim = makeSelectClaimForUri(playingUrl)(state); const url = claim && claim.permanent_url; const loopList = selectListLoop(state); const loop = loopList && loopList.collectionId === props.id && loopList.loop; @@ -24,7 +24,7 @@ const select = (state, props) => { collection: makeSelectCollectionForId(props.id)(state), collectionUrls: makeSelectUrlsForCollectionId(props.id)(state), collectionName: makeSelectNameForCollectionId(props.id)(state), - isMine: selectClaimIsMine(state, claim), + isMine: makeSelectClaimIsMine(url)(state), loop, shuffle, }; diff --git a/ui/component/collectionPreviewOverlay/index.js b/ui/component/collectionPreviewOverlay/index.js index caee0c5c9..8c04f7020 100644 --- a/ui/component/collectionPreviewOverlay/index.js +++ b/ui/component/collectionPreviewOverlay/index.js @@ -1,5 +1,5 @@ import { connect } from 'react-redux'; -import { makeSelectIsUriResolving, selectClaimIdForUri, makeSelectClaimForClaimId } from 'redux/selectors/claims'; +import { makeSelectIsUriResolving, makeSelectClaimIdForUri, makeSelectClaimForClaimId } from 'redux/selectors/claims'; import { makeSelectUrlsForCollectionId, makeSelectNameForCollectionId, @@ -10,7 +10,7 @@ import { doFetchItemsInCollection } from 'redux/actions/collections'; import CollectionPreviewOverlay from './view'; const select = (state, props) => { - const collectionId = props.collectionId || (props.uri && selectClaimIdForUri(state, props.uri)); + const collectionId = props.collectionId || (props.uri && makeSelectClaimIdForUri(props.uri)(state)); const claim = props.collectionId && makeSelectClaimForClaimId(props.collectionId)(state); const collectionUri = props.uri || (claim && (claim.canonical_url || claim.permanent_url)) || null; diff --git a/ui/component/collectionPreviewTile/index.js b/ui/component/collectionPreviewTile/index.js index 706cd974d..c3a5cc65b 100644 --- a/ui/component/collectionPreviewTile/index.js +++ b/ui/component/collectionPreviewTile/index.js @@ -5,7 +5,7 @@ import { makeSelectTitleForUri, makeSelectChannelForClaimUri, makeSelectClaimIsNsfw, - selectClaimIdForUri, + makeSelectClaimIdForUri, makeSelectClaimForClaimId, } from 'redux/selectors/claims'; import { @@ -24,7 +24,7 @@ import { selectShowMatureContent } from 'redux/selectors/settings'; import CollectionPreviewTile from './view'; const select = (state, props) => { - const collectionId = props.collectionId || (props.uri && selectClaimIdForUri(state, props.uri)); + const collectionId = props.collectionId || (props.uri && makeSelectClaimIdForUri(props.uri)(state)); const claim = props.collectionId && makeSelectClaimForClaimId(props.collectionId)(state); const collectionUri = props.uri || (claim && (claim.canonical_url || claim.permanent_url)) || null; diff --git a/ui/component/collectionsListMine/view.jsx b/ui/component/collectionsListMine/view.jsx index e50cc294c..4db77a6b5 100644 --- a/ui/component/collectionsListMine/view.jsx +++ b/ui/component/collectionsListMine/view.jsx @@ -95,36 +95,44 @@ export default function CollectionsListMine(props: Props) { {builtin.map((list: Collection) => { const { items: itemUrls } = list; return ( - <> - {Boolean(itemUrls && itemUrls.length) && ( -
-

-

+ + + )} + {!(itemUrls && itemUrls.length) && ( +

+ {__(`${list.name}`)} +
{__('(Empty) --[indicates empty playlist]--')}

- -
- )} - + )} + + ); })}
diff --git a/ui/component/comment/index.js b/ui/component/comment/index.js index b5c336522..ffe06dd37 100644 --- a/ui/component/comment/index.js +++ b/ui/component/comment/index.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import { - selectTotalStakedAmountForChannelUri, + makeSelectStakedLevelForChannelUri, makeSelectClaimForUri, makeSelectThumbnailForUri, selectMyChannelClaims, @@ -33,7 +33,7 @@ const select = (state, props) => { activeChannelClaim, myChannels: selectMyChannelClaims(state), playingUri: selectPlayingUri(state), - stakedLevel: selectTotalStakedAmountForChannelUri(state, props.authorUri), + stakedLevel: makeSelectStakedLevelForChannelUri(props.authorUri)(state), linkedCommentAncestors: selectLinkedCommentAncestors(state), totalReplyPages: makeSelectTotalReplyPagesForParentId(props.commentId)(state), }; diff --git a/ui/component/commentCreate/index.js b/ui/component/commentCreate/index.js index ff760c3e3..61d209e5d 100644 --- a/ui/component/commentCreate/index.js +++ b/ui/component/commentCreate/index.js @@ -1,8 +1,8 @@ import { connect } from 'react-redux'; import { - selectClaimForUri, - selectClaimIsMine, - selectHasChannels, + makeSelectClaimForUri, + makeSelectClaimIsMine, + selectMyChannelClaims, selectFetchingMyChannels, makeSelectTagInClaimOrChannelForUri, } from 'redux/selectors/claims'; @@ -14,18 +14,15 @@ import { CommentCreate } from './view'; import { doToast } from 'redux/actions/notifications'; import { DISABLE_SUPPORT_TAG } from 'constants/tags'; -const select = (state, props) => { - const claim = selectClaimForUri(state, props.uri); - return { - activeChannelClaim: selectActiveChannelClaim(state), - hasChannels: selectHasChannels(state), - claim, - claimIsMine: selectClaimIsMine(state, claim), - isFetchingChannels: selectFetchingMyChannels(state), - settingsByChannelId: selectSettingsByChannelId(state), - supportDisabled: makeSelectTagInClaimOrChannelForUri(props.uri, DISABLE_SUPPORT_TAG)(state), - }; -}; +const select = (state, props) => ({ + claim: makeSelectClaimForUri(props.uri)(state), + channels: selectMyChannelClaims(state), + isFetchingChannels: selectFetchingMyChannels(state), + activeChannelClaim: selectActiveChannelClaim(state), + claimIsMine: makeSelectClaimIsMine(props.uri)(state), + settingsByChannelId: selectSettingsByChannelId(state), + supportDisabled: makeSelectTagInClaimOrChannelForUri(props.uri, DISABLE_SUPPORT_TAG)(state), +}); const perform = (dispatch, ownProps) => ({ createComment: (comment, claimId, parentId, txid, payment_intent_id, environment) => diff --git a/ui/component/commentCreate/view.jsx b/ui/component/commentCreate/view.jsx index 2ef7f36ad..d1108cd0e 100644 --- a/ui/component/commentCreate/view.jsx +++ b/ui/component/commentCreate/view.jsx @@ -164,7 +164,7 @@ export function CommentCreate(props: Props) { function handleCommentChange(event) { let commentValue; if (isReply) { - commentValue = advancedEditor ? event : event.target.value; + commentValue = event.target.value; } else { commentValue = advancedEditor ? event : event.target.value; } @@ -596,6 +596,20 @@ export function CommentCreate(props: Props) { setActiveTab(TAB_LBC); }} /> + {/* @if TARGET='web' */} + {stripeEnvironment && ( +