From f390320030487afb6450b0bd38e5080b301587da Mon Sep 17 00:00:00 2001 From: DispatchCommit Date: Thu, 8 Jul 2021 01:26:58 -0700 Subject: [PATCH 01/24] add retargeting pixel --- ui/page/home/view.jsx | 7 +++++++ ui/page/signInVerify/view.jsx | 7 ++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/ui/page/home/view.jsx b/ui/page/home/view.jsx index 1941091a3..78754d67b 100644 --- a/ui/page/home/view.jsx +++ b/ui/page/home/view.jsx @@ -128,6 +128,13 @@ function HomePage(props: Props) { {rowData.map(({ title, route, link, icon, help, options = {} }, index) => { return getRowElements(title, route, link, icon, help, options, index); })} + + {SIMPLE_SITE && ( + + )} ); } diff --git a/ui/page/signInVerify/view.jsx b/ui/page/signInVerify/view.jsx index 8abb8495b..acdf97a74 100644 --- a/ui/page/signInVerify/view.jsx +++ b/ui/page/signInVerify/view.jsx @@ -10,7 +10,7 @@ import I18nMessage from 'component/i18nMessage'; import Card from 'component/common/card'; type Props = { - history: { push: string => void, location: { search: string } }, + history: { push: (string) => void, location: { search: string } }, doToast: ({}) => void, }; @@ -132,6 +132,11 @@ function SignInVerifyPage(props: Props) { } /> + + ); } From 2dbd5d0f00a54ea57c503241241303c1bccfbcdb Mon Sep 17 00:00:00 2001 From: btzr-io Date: Mon, 12 Jul 2021 18:54:32 -0500 Subject: [PATCH 02/24] fix sidebar shortcut activation on textarea: #6326 --- static/app-strings.json | 3 +++ ui/component/sideNavigation/view.jsx | 12 ++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/static/app-strings.json b/static/app-strings.json index ec9a7c743..d56d79474 100644 --- a/static/app-strings.json +++ b/static/app-strings.json @@ -2040,5 +2040,8 @@ "Tip Creators": "Tip Creators", "Only select creators can receive tips at this time": "Only select creators can receive tips at this time", "The payment will be made from your saved card": "The payment will be made from your saved card", + "Trending for #Art": "Trending for #Art", + "Trending for #Education": "Trending for #Education", + "Trending for #Technology": "Trending for #Technology", "--end--": "--end--" } diff --git a/ui/component/sideNavigation/view.jsx b/ui/component/sideNavigation/view.jsx index 14acaf966..bc443a689 100644 --- a/ui/component/sideNavigation/view.jsx +++ b/ui/component/sideNavigation/view.jsx @@ -9,7 +9,15 @@ import Icon from 'component/common/icon'; import NotificationBubble from 'component/notificationBubble'; import I18nMessage from 'component/i18nMessage'; import ChannelThumbnail from 'component/channelThumbnail'; -import { PINNED_LABEL_1, PINNED_URI_1, PINNED_URI_2, PINNED_LABEL_2, SIMPLE_SITE, DOMAIN, ENABLE_UI_NOTIFICATIONS } from 'config'; +import { + PINNED_LABEL_1, + PINNED_URI_1, + PINNED_URI_2, + PINNED_LABEL_2, + SIMPLE_SITE, + DOMAIN, + ENABLE_UI_NOTIFICATIONS, +} from 'config'; // @if TARGET='app' import { IS_MAC } from 'component/app/view'; // @endif @@ -275,7 +283,7 @@ function SideNavigation(props: Props) { if (e.keyCode === ESCAPE_KEY_CODE && isAbsolute) { setSidebarOpen(false); } else if (e.keyCode === BACKSLASH_KEY_CODE) { - const hasActiveInput = document.querySelector('input:focus'); + const hasActiveInput = document.querySelector('input:focus, textarea:focus'); if (!hasActiveInput) { setSidebarOpen(!sidebarOpen); } From 2009be519e96f92d27d1c77a4b1fa0424b3f557e Mon Sep 17 00:00:00 2001 From: btzr-io Date: Tue, 13 Jul 2021 00:36:15 -0500 Subject: [PATCH 03/24] remove localization entries --- static/app-strings.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/static/app-strings.json b/static/app-strings.json index d56d79474..ec9a7c743 100644 --- a/static/app-strings.json +++ b/static/app-strings.json @@ -2040,8 +2040,5 @@ "Tip Creators": "Tip Creators", "Only select creators can receive tips at this time": "Only select creators can receive tips at this time", "The payment will be made from your saved card": "The payment will be made from your saved card", - "Trending for #Art": "Trending for #Art", - "Trending for #Education": "Trending for #Education", - "Trending for #Technology": "Trending for #Technology", "--end--": "--end--" } From 2391f66d90986eea4b220113a21974a82fa3cd6b Mon Sep 17 00:00:00 2001 From: btzr-io Date: Mon, 12 Jul 2021 21:20:54 -0500 Subject: [PATCH 04/24] fix upload button on creator analytics: #6323 --- static/app-strings.json | 3 +++ ui/component/creatorAnalytics/view.jsx | 14 ++++---------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/static/app-strings.json b/static/app-strings.json index ec9a7c743..d56d79474 100644 --- a/static/app-strings.json +++ b/static/app-strings.json @@ -2040,5 +2040,8 @@ "Tip Creators": "Tip Creators", "Only select creators can receive tips at this time": "Only select creators can receive tips at this time", "The payment will be made from your saved card": "The payment will be made from your saved card", + "Trending for #Art": "Trending for #Art", + "Trending for #Education": "Trending for #Education", + "Trending for #Technology": "Trending for #Technology", "--end--": "--end--" } diff --git a/ui/component/creatorAnalytics/view.jsx b/ui/component/creatorAnalytics/view.jsx index 1d9f1040d..912c826e7 100644 --- a/ui/component/creatorAnalytics/view.jsx +++ b/ui/component/creatorAnalytics/view.jsx @@ -15,14 +15,13 @@ import analytics from 'analytics'; type Props = { claim: ?ChannelClaim, fetchingChannels: boolean, - prepareEdit: string => void, }; const UNAUTHENTICATED_ERROR = 'unauthenticated'; const GENERIC_ERROR = 'error'; export default function CreatorAnalytics(props: Props) { - const { prepareEdit, claim } = props; + const { claim } = props; const history = useHistory(); const [stats, setStats] = React.useState(); const [error, setError] = React.useState(); @@ -39,11 +38,11 @@ export default function CreatorAnalytics(props: Props) { if (claimId && channelForEffect && channelHasClaims) { setFetchingStats(true); Lbryio.call('reports', 'content', { claim_id: claimId }) - .then(res => { + .then((res) => { setFetchingStats(false); setStats(res); }) - .catch(error => { + .catch((error) => { if (error.response.status === 401) { setError(UNAUTHENTICATED_ERROR); const channelToSend = JSON.parse(channelForEffect); @@ -218,12 +217,7 @@ export default function CreatorAnalytics(props: Props) { button="primary" icon={ICONS.PUBLISH} label={__('Upload')} - onClick={() => { - if (claim) { - prepareEdit(claim.name); - history.push(`/$/${PAGES.UPLOAD}`); - } - }} + onClick={() => history.push(`/$/${PAGES.UPLOAD}`)} /> } From 7398120c0cc8b69a949a26333d2e3480b790bf06 Mon Sep 17 00:00:00 2001 From: btzr-io Date: Mon, 12 Jul 2021 21:55:08 -0500 Subject: [PATCH 05/24] fix incorrect aria-label in rewards page --- static/app-strings.json | 3 +++ ui/component/rewardLink/view.jsx | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/static/app-strings.json b/static/app-strings.json index d56d79474..9229c2196 100644 --- a/static/app-strings.json +++ b/static/app-strings.json @@ -2043,5 +2043,8 @@ "Trending for #Art": "Trending for #Art", "Trending for #Education": "Trending for #Education", "Trending for #Technology": "Trending for #Technology", + "Watch content and earn more Credits for each level unlocked! 10 views required for level 1 (Current Score: 0). Only up to 10 views per day count.": "Watch content and earn more Credits for each level unlocked! 10 views required for level 1 (Current Score: 0). Only up to 10 views per day count.", + "Follow your favorite creators and earn more Credits for each level unlocked! Follow 1 creators for level 1 (Current Score: 0).": "Follow your favorite creators and earn more Credits for each level unlocked! Follow 1 creators for level 1 (Current Score: 0).", + "Gain a following to earn this reward. 1 validated followers needed for level 1 (Current Score: 0). This amount may not match your total followers.": "Gain a following to earn this reward. 1 validated followers needed for level 1 (Current Score: 0). This amount may not match your total followers.", "--end--": "--end--" } diff --git a/ui/component/rewardLink/view.jsx b/ui/component/rewardLink/view.jsx index 79491b0a9..fba7f1758 100644 --- a/ui/component/rewardLink/view.jsx +++ b/ui/component/rewardLink/view.jsx @@ -13,7 +13,7 @@ type Props = { label: ?string, reward: Reward, button: ?boolean, - claimReward: Reward => void, + claimReward: (Reward) => void, }; const RewardLink = (props: Props) => { @@ -36,6 +36,7 @@ const RewardLink = (props: Props) => { button={button ? 'primary' : 'link'} disabled={isPending} label={{displayLabel}} + aria-label={displayLabel} onClick={() => { claimReward(reward); }} From 6e5e2b75782a346e31a34d1b6cd67e0b6dfffc42 Mon Sep 17 00:00:00 2001 From: btzr-io Date: Mon, 12 Jul 2021 23:12:10 -0500 Subject: [PATCH 06/24] remove localization entries --- static/app-strings.json | 6 ------ 1 file changed, 6 deletions(-) diff --git a/static/app-strings.json b/static/app-strings.json index 9229c2196..ec9a7c743 100644 --- a/static/app-strings.json +++ b/static/app-strings.json @@ -2040,11 +2040,5 @@ "Tip Creators": "Tip Creators", "Only select creators can receive tips at this time": "Only select creators can receive tips at this time", "The payment will be made from your saved card": "The payment will be made from your saved card", - "Trending for #Art": "Trending for #Art", - "Trending for #Education": "Trending for #Education", - "Trending for #Technology": "Trending for #Technology", - "Watch content and earn more Credits for each level unlocked! 10 views required for level 1 (Current Score: 0). Only up to 10 views per day count.": "Watch content and earn more Credits for each level unlocked! 10 views required for level 1 (Current Score: 0). Only up to 10 views per day count.", - "Follow your favorite creators and earn more Credits for each level unlocked! Follow 1 creators for level 1 (Current Score: 0).": "Follow your favorite creators and earn more Credits for each level unlocked! Follow 1 creators for level 1 (Current Score: 0).", - "Gain a following to earn this reward. 1 validated followers needed for level 1 (Current Score: 0). This amount may not match your total followers.": "Gain a following to earn this reward. 1 validated followers needed for level 1 (Current Score: 0). This amount may not match your total followers.", "--end--": "--end--" } From d32af5606bd591e84a2d187e73141725bf76e7cb Mon Sep 17 00:00:00 2001 From: DispatchCommit Date: Tue, 13 Jul 2021 01:00:25 -0700 Subject: [PATCH 07/24] move pre-roll ad logic into it's designated video.js plugin --- .../plugins/videojs-aniview/plugin.js | 86 ++++++++++++------- .../viewers/videoViewer/internal/videojs.jsx | 33 +------ 2 files changed, 61 insertions(+), 58 deletions(-) diff --git a/ui/component/viewers/videoViewer/internal/plugins/videojs-aniview/plugin.js b/ui/component/viewers/videoViewer/internal/plugins/videojs-aniview/plugin.js index 24da49bcb..f51502be8 100644 --- a/ui/component/viewers/videoViewer/internal/plugins/videojs-aniview/plugin.js +++ b/ui/component/viewers/videoViewer/internal/plugins/videojs-aniview/plugin.js @@ -1,10 +1,52 @@ // Created by xander on 6/21/2021 import videojs from 'video.js'; +import 'videojs-contrib-ads'; import 'videojs-ima'; const VERSION = '0.0.1'; +/* Macro provided by aniview +* const macroUrl = + `https://vast.aniview.com/api/adserver61/vast/` + + `?AV_PUBLISHERID=60afcbc58cfdb065440d2426` + + `&AV_CHANNELID=60b354389c7adb506d0bd9a4` + + `&AV_URL=[URL_MACRO]` + + `&cb=[TIMESTAMP_MACRO]` + + `&AV_WIDTH=[WIDTH_MACRO]` + + `&AV_HEIGHT=[HEIGHT_MACRO]` + + `&AV_SCHAIN=[SCHAIN_MACRO]` + + `&AV_CCPA=[CCPA_MACRO]` + + `&AV_GDPR=[GDPR_MACRO]` + + `&AV_CONSENT=[CONSENT_MACRO]` + + `&skip=true` + + `&skiptimer=5` + + `&logo=false` + + `&usevslot=true` + + `&vastretry=3` + + `&hidecontrols=false`; +* */ + +// TEST PRE-ROLL WITH THIS TAG: +// https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpreonly&cmsid=496&vid=short_onecue&correlator= + +// Modified to work with IMA const macroUrl = - 'https://vast.aniview.com/api/adserver61/vast/?AV_PUBLISHERID=60afcbc58cfdb065440d2426&AV_CHANNELID=60b354389c7adb506d0bd9a4&AV_URL=[URL_MACRO]&cb=[TIMESTAMP_MACRO]&AV_WIDTH=[WIDTH_MACRO]&AV_HEIGHT=[HEIGHT_MACRO]&AV_SCHAIN=[SCHAIN_MACRO]&AV_CCPA=[CCPA_MACRO]&AV_GDPR=[GDPR_MACRO]&AV_CONSENT=[CONSENT_MACRO]&skip=true&skiptimer=5&usevslot=true&hidecontrols=false'; + `https://vast.aniview.com/api/adserver61/vast/` + + `?AV_PUBLISHERID=60afcbc58cfdb065440d2426` + + `&AV_CHANNELID=60b354389c7adb506d0bd9a4` + + `&AV_URL=[URL]` + + `&cb=[CACHEBUSTING]` + + `&AV_WIDTH=[WIDTH]` + + `&AV_HEIGHT=[HEIGHT]` + + // `&AV_SCHAIN=[SCHAIN_MACRO]` + + // `&AV_CCPA=[CCPA_MACRO]` + + // `&AV_GDPR=[GDPR_MACRO]` + + // `&AV_CONSENT=[CONSENT_MACRO]` + + `&skip=true` + + `&skiptimer=5` + + `&logo=true` + + `&usevslot=true` + + `&vastretry=5` + + `&hidecontrols=false`; const defaults = { adTagUrl: macroUrl, @@ -19,46 +61,24 @@ class AniviewPlugin extends Component { super(player, options); // Plugin started - if (options.debug) { - this.log(`Created aniview plugin.`); - } + if (options.debug) this.log(`Created aniview plugin.`); // To help with debugging, we'll add a global vjs object with the video js player window.aniview = player; this.player = player; - const google = window.google; - - player.ima({ - // adTagUrl: macroUrl, - id: 'ad_content_video', - vpaidMode: google.ima.ImaSdkSettings.VpaidMode.INSECURE, - adTagUrl: - 'https://vast.aniview.com/api/adserver61/vast/?AV_PUBLISHERID=60afcbc58cfdb065440d2426&AV_CHANNELID=60b354389c7adb506d0bd9a4', - }); - - // this.player.ads(); - // const serverUrl = this.player.ads.adMacroReplacement(macroUrl); - // this.log(serverUrl); - // request ads whenever there's new video content - player.on('contentchanged', () => { + /* player.on('contentchanged', () => { // in a real plugin, you might fetch your ad inventory here player.trigger('adsready'); - }); + }); */ // Plugin event listeners - player.on('readyforpreroll', (event) => this.onReadyForPreroll(event)); + // player.on('readyforpreroll', (event) => this.onReadyForPreroll(event)); } - onReadyForPreroll(event) { - this.player.ads.startLinearAdMode(); - - // play your linear ad content - // in this example, we use a static mp4 - this.player.src('kitteh.mp4'); - + /* onReadyForPreroll(event) { // send event when ad is playing to remove loading spinner this.player.one('adplaying', () => { this.player.trigger('ads-ad-started'); @@ -68,7 +88,7 @@ class AniviewPlugin extends Component { this.player.one('adended', () => { this.player.ads.endLinearAdMode(); }); - } + } */ log(...args) { if (this.options_.debug) { @@ -90,6 +110,14 @@ const onPlayerReady = (player, options) => { * @param {Object} [options={}] */ const plugin = function (options) { + const google = window.google; + + this.ima({ + // $FlowFixMe + vpaidMode: google.ima.ImaSdkSettings.VpaidMode.INSECURE, + adTagUrl: macroUrl, + }); + this.ready(() => { onPlayerReady(this, videojs.mergeOptions(defaults, options)); }); diff --git a/ui/component/viewers/videoViewer/internal/videojs.jsx b/ui/component/viewers/videoViewer/internal/videojs.jsx index 3315d7f36..57457da37 100644 --- a/ui/component/viewers/videoViewer/internal/videojs.jsx +++ b/ui/component/viewers/videoViewer/internal/videojs.jsx @@ -13,29 +13,9 @@ import hlsQualitySelector from './plugins/videojs-hls-quality-selector/plugin'; import recsys from './plugins/videojs-recsys/plugin'; import qualityLevels from 'videojs-contrib-quality-levels'; import isUserTyping from 'util/detect-typing'; -import 'videojs-contrib-ads'; -import 'videojs-ima'; -// import aniview from './plugins/videojs-aniview/plugin'; +import './plugins/videojs-aniview/plugin'; const isDev = process.env.NODE_ENV !== 'production'; -const macroUrl = - `https://vast.aniview.com/api/adserver61/vast/` + - `?AV_PUBLISHERID=60afcbc58cfdb065440d2426` + - `&AV_CHANNELID=60b354389c7adb506d0bd9a4` + - `&AV_URL=[URL_MACRO]` + - `&cb=[TIMESTAMP_MACRO]` + - `&AV_WIDTH=[WIDTH_MACRO]` + - `&AV_HEIGHT=[HEIGHT_MACRO]` + - `&AV_SCHAIN=[SCHAIN_MACRO]` + - `&AV_CCPA=[CCPA_MACRO]` + - `&AV_GDPR=[GDPR_MACRO]` + - `&AV_CONSENT=[CONSENT_MACRO]` + - `&skip=true` + - `&skiptimer=5` + - `&logo=false` + - `&usevslot=true` + - `&vastretry=3` + - `&hidecontrols=false`; export type Player = { on: (string, (any) => void) => void, @@ -605,14 +585,9 @@ export default React.memo(function VideoJs(props: Props) { // pre-roll ads // This must be initialized earlier than everything else // otherwise a race condition occurs if we place this in the onReady call back - if (allowPreRoll && SIMPLE_SITE && window.google) { - const google = window.google; - // player.aniview(); - vjs.ima({ - // $FlowFixMe - vpaidMode: google.ima.ImaSdkSettings.VpaidMode.INSECURE, - adTagUrl: macroUrl, - }); + // allow if isDev because otherwise you'll never see ads when basing to master + if ((allowPreRoll && SIMPLE_SITE) || isDev) { + vjs.aniview(); } // fixes #3498 (https://github.com/lbryio/lbry-desktop/issues/3498) From 052aa87ddd1767e64fce38d1cd6a285413b7a8a9 Mon Sep 17 00:00:00 2001 From: infinite-persistence Date: Tue, 13 Jul 2021 13:17:48 +0800 Subject: [PATCH 08/24] Remove unused items --- ui/component/searchOptions/index.js | 5 ++--- ui/component/wunderbarSuggestions/index.js | 2 -- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/ui/component/searchOptions/index.js b/ui/component/searchOptions/index.js index 125535276..3d4902bad 100644 --- a/ui/component/searchOptions/index.js +++ b/ui/component/searchOptions/index.js @@ -1,14 +1,13 @@ import { connect } from 'react-redux'; import { doUpdateSearchOptions } from 'redux/actions/search'; -import { selectSearchOptions, makeSelectQueryWithOptions } from 'redux/selectors/search'; +import { selectSearchOptions } from 'redux/selectors/search'; import { doToggleSearchExpanded } from 'redux/actions/app'; import { selectSearchOptionsExpanded } from 'redux/selectors/app'; import SearchOptions from './view'; -const select = state => ({ +const select = (state) => ({ options: selectSearchOptions(state), expanded: selectSearchOptionsExpanded(state), - query: makeSelectQueryWithOptions(undefined, {})(state), }); const perform = (dispatch, ownProps) => { diff --git a/ui/component/wunderbarSuggestions/index.js b/ui/component/wunderbarSuggestions/index.js index 5a43cc271..e849fd5a1 100644 --- a/ui/component/wunderbarSuggestions/index.js +++ b/ui/component/wunderbarSuggestions/index.js @@ -2,7 +2,6 @@ import * as MODALS from 'constants/modal_types'; import { connect } from 'react-redux'; import { selectLanguage, selectShowMatureContent } from 'redux/selectors/settings'; import { doToast } from 'redux/actions/notifications'; -import { doSearch } from 'redux/actions/search'; import { doOpenModal, doHideModal } from 'redux/actions/app'; import { withRouter } from 'react-router'; import { doResolveUris } from 'lbry-redux'; @@ -16,7 +15,6 @@ const select = (state, props) => ({ const perform = (dispatch, ownProps) => ({ doResolveUris: (uris) => dispatch(doResolveUris(uris)), - doSearch: (query, options) => dispatch(doSearch(query, options)), navigateToSearchPage: (query) => { let encodedQuery = encodeURIComponent(query); ownProps.history.push({ pathname: `/$/search`, search: `?q=${encodedQuery}` }); From b822ae39e11727999d503f1faf25a831e3972f97 Mon Sep 17 00:00:00 2001 From: infinite-persistence Date: Tue, 13 Jul 2021 14:28:09 +0800 Subject: [PATCH 09/24] Remove hardcoded filters in `doSearch` ## Issue 6414 `doSearch` was used in Wunderbar and Recommended, but it was adding in Wunderbar options all the time. ## Changes - Fix `doSearch` to not use hardcoded options (take from arguments). - Refactors the searchOption-creation code so that we don't duplicate the logic in both .js and .jsx. --- ui/page/search/index.js | 23 ++++++++++----------- ui/page/search/view.jsx | 40 ++++++++---------------------------- ui/redux/actions/search.js | 15 +++++--------- ui/redux/selectors/search.js | 19 ----------------- 4 files changed, 25 insertions(+), 72 deletions(-) diff --git a/ui/page/search/index.js b/ui/page/search/index.js index 7be30b4c8..30558e61a 100644 --- a/ui/page/search/index.js +++ b/ui/page/search/index.js @@ -1,43 +1,42 @@ import { connect } from 'react-redux'; import { withRouter } from 'react-router'; import { doSearch } from 'redux/actions/search'; -import { SIMPLE_SITE } from 'config'; import { selectIsSearching, makeSelectSearchUris, - makeSelectQueryWithOptions, selectSearchOptions, makeSelectHasReachedMaxResultsLength, } from 'redux/selectors/search'; import { selectShowMatureContent } from 'redux/selectors/settings'; import { selectUserVerifiedEmail } from 'redux/selectors/user'; +import { getSearchQueryString } from 'util/query-params'; import SearchPage from './view'; const select = (state, props) => { const showMature = selectShowMatureContent(state); const urlParams = new URLSearchParams(props.location.search); + let urlQuery = urlParams.get('q') || null; if (urlQuery) { urlQuery = urlQuery.replace(/^lbry:\/\//i, '').replace(/\//, ' '); } - const query = makeSelectQueryWithOptions( - urlQuery, - SIMPLE_SITE - ? { nsfw: false, isBackgroundSearch: false } - : showMature === false - ? { nsfw: false, isBackgroundSearch: false } - : { isBackgroundSearch: false } - )(state); + const searchOptions = { + ...selectSearchOptions(state), + isBackgroundSearch: false, + nsfw: showMature, + }; + + const query = getSearchQueryString(urlQuery, searchOptions); const uris = makeSelectSearchUris(query)(state); const hasReachedMaxResultsLength = makeSelectHasReachedMaxResultsLength(query)(state); return { + urlQuery, + searchOptions, isSearching: selectIsSearching(state), - showNsfw: showMature, uris: uris, isAuthenticated: selectUserVerifiedEmail(state), - searchOptions: selectSearchOptions(state), hasReachedMaxResultsLength: hasReachedMaxResultsLength, }; }; diff --git a/ui/page/search/view.jsx b/ui/page/search/view.jsx index 87731f257..1499e7c33 100644 --- a/ui/page/search/view.jsx +++ b/ui/page/search/view.jsx @@ -11,43 +11,21 @@ import { formatLbryUrlForWeb } from 'util/url'; import { useHistory } from 'react-router'; import { SEARCH_PAGE_SIZE } from 'constants/search'; -type AdditionalOptions = { - isBackgroundSearch: boolean, - nsfw?: boolean, - from?: number, -}; - type Props = { - search: (string, AdditionalOptions) => void, - searchOptions: {}, + urlQuery: string, + searchOptions: SearchOptions, + search: (string, SearchOptions) => void, isSearching: boolean, - location: UrlLocation, uris: Array, - showNsfw: boolean, isAuthenticated: boolean, hasReachedMaxResultsLength: boolean, }; export default function SearchPage(props: Props) { - const { - search, - uris, - location, - isSearching, - showNsfw, - isAuthenticated, - searchOptions, - hasReachedMaxResultsLength, - } = props; + const { urlQuery, searchOptions, search, uris, isSearching, isAuthenticated, hasReachedMaxResultsLength } = props; const { push } = useHistory(); - const urlParams = new URLSearchParams(location.search); - const urlQuery = urlParams.get('q') || ''; - const additionalOptions: AdditionalOptions = { isBackgroundSearch: false }; const [from, setFrom] = React.useState(0); - additionalOptions['nsfw'] = SIMPLE_SITE ? false : showNsfw; - additionalOptions['from'] = from; - const modifiedUrlQuery = urlQuery.trim().replace(/\s+/g, '').replace(/:/g, '#'); const uriFromQuery = `lbry://${modifiedUrlQuery}`; @@ -78,14 +56,14 @@ export default function SearchPage(props: Props) { } catch (e) {} } - const stringifiedOptions = JSON.stringify(additionalOptions); const stringifiedSearchOptions = JSON.stringify(searchOptions); + useEffect(() => { if (urlQuery) { - const jsonOptions = JSON.parse(stringifiedOptions); - search(urlQuery, jsonOptions); + const searchOptions = JSON.parse(stringifiedSearchOptions); + search(urlQuery, { ...searchOptions, from: from }); } - }, [search, urlQuery, stringifiedOptions, stringifiedSearchOptions]); + }, [search, urlQuery, stringifiedSearchOptions, from]); function loadMore() { if (!isSearching && !hasReachedMaxResultsLength) { @@ -114,7 +92,7 @@ export default function SearchPage(props: Props) { header={ } diff --git a/ui/redux/actions/search.js b/ui/redux/actions/search.js index 036c5c614..129afbecf 100644 --- a/ui/redux/actions/search.js +++ b/ui/redux/actions/search.js @@ -1,13 +1,9 @@ // @flow import * as ACTIONS from 'constants/action_types'; import { buildURI, doResolveUris, batchActions, isURIValid, makeSelectClaimForUri } from 'lbry-redux'; -import { - makeSelectSearchUris, - makeSelectQueryWithOptions, - selectSearchValue, - selectSearchOptions, -} from 'redux/selectors/search'; +import { makeSelectSearchUris, selectSearchValue } from 'redux/selectors/search'; import handleFetchResponse from 'util/handle-fetch'; +import { getSearchQueryString } from 'util/query-params'; type Dispatch = (action: any) => any; type GetState = () => { search: SearchState }; @@ -44,10 +40,9 @@ export const doSearch = (rawQuery: string, searchOptions: SearchOptions) => ( const state = getState(); - const mainOptions: any = selectSearchOptions(state); - const queryWithOptions = makeSelectQueryWithOptions(query, searchOptions)(state); + const queryWithOptions = getSearchQueryString(query, searchOptions); - const size = mainOptions.size; + const size = searchOptions.size; const from = searchOptions.from; // If we have already searched for something, we don't need to do anything @@ -101,7 +96,7 @@ export const doSearch = (rawQuery: string, searchOptions: SearchOptions) => ( }); dispatch(batchActions(...actions)); }) - .catch((e) => { + .catch(() => { dispatch({ type: ACTIONS.SEARCH_FAIL, }); diff --git a/ui/redux/selectors/search.js b/ui/redux/selectors/search.js index b36717076..d864ea00d 100644 --- a/ui/redux/selectors/search.js +++ b/ui/redux/selectors/search.js @@ -57,25 +57,6 @@ export const makeSelectHasReachedMaxResultsLength = (query: string): ((state: St return hasReachedMaxResultsLength[query]; }); -// Creates a query string based on the state in the search reducer -// Can be overrided by passing in custom sizes/from values for other areas pagination - -type CustomOptions = { - isBackgroundSearch?: boolean, - size?: number, - from?: number, - related_to?: string, - nsfw?: boolean, -}; - -export const makeSelectQueryWithOptions = (customQuery: ?string, options: CustomOptions) => - createSelector(selectSearchValue, selectSearchOptions, (query, defaultOptions) => { - const searchOptions = { ...defaultOptions, ...options }; - const queryString = getSearchQueryString(customQuery || query, searchOptions); - - return queryString; - }); - export const makeSelectRecommendedContentForUri = (uri: string) => createSelector( makeSelectClaimForUri(uri), From e6376871d53713eadf552f53fc9ff1f1275d7793 Mon Sep 17 00:00:00 2001 From: zeppi Date: Tue, 13 Jul 2021 23:40:49 -0400 Subject: [PATCH 10/24] move pixel to web --- ui/page/home/view.jsx | 14 +++++++------- ui/page/signInVerify/view.jsx | 9 ++++----- web/component/pixel.jsx | 36 +++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 12 deletions(-) create mode 100644 web/component/pixel.jsx diff --git a/ui/page/home/view.jsx b/ui/page/home/view.jsx index 78754d67b..bbfd1aad0 100644 --- a/ui/page/home/view.jsx +++ b/ui/page/home/view.jsx @@ -13,6 +13,10 @@ import LbcSymbol from 'component/common/lbc-symbol'; import WaitUntilOnPage from 'component/common/wait-until-on-page'; import useGetLivestreams from 'effects/use-get-livestreams'; +// @if TARGET='web' +import Pixel from 'web/component/pixel'; +// @endif + type Props = { authenticated: boolean, followedTags: Array, @@ -128,13 +132,9 @@ function HomePage(props: Props) { {rowData.map(({ title, route, link, icon, help, options = {} }, index) => { return getRowElements(title, route, link, icon, help, options, index); })} - - {SIMPLE_SITE && ( - - )} + {/* @if TARGET='web' */} + + {/* @endif */} ); } diff --git a/ui/page/signInVerify/view.jsx b/ui/page/signInVerify/view.jsx index acdf97a74..f201ff683 100644 --- a/ui/page/signInVerify/view.jsx +++ b/ui/page/signInVerify/view.jsx @@ -8,6 +8,7 @@ import Button from 'component/button'; import { Lbryio } from 'lbryinc'; import I18nMessage from 'component/i18nMessage'; import Card from 'component/common/card'; +import Pixel from 'web/component/pixel'; type Props = { history: { push: (string) => void, location: { search: string } }, @@ -132,11 +133,9 @@ function SignInVerifyPage(props: Props) { } /> - - + {/* @if TARGET='web' */} + + {/* @endif */} ); } diff --git a/web/component/pixel.jsx b/web/component/pixel.jsx new file mode 100644 index 000000000..697e378d3 --- /dev/null +++ b/web/component/pixel.jsx @@ -0,0 +1,36 @@ +// @flow +import React from 'react'; +import { SIMPLE_SITE } from 'config'; +type Props = { + type: string, +}; + +const Pixel = (props: Props) => { + const { type } = props; + if (!SIMPLE_SITE) { + return null; + } + if (type === 'retargeting') { + return ( + <> + + + ); + } else if (type === 'kill') { + return ( + <> + + + ); + } else { + return null; + } +}; + +export default Pixel; From f1c9aa7907690239811ed62eddbd7ff34ce5b37a Mon Sep 17 00:00:00 2001 From: zeppi Date: Tue, 13 Jul 2021 23:45:51 -0400 Subject: [PATCH 11/24] review this --- ui/page/signInVerify/view.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/page/signInVerify/view.jsx b/ui/page/signInVerify/view.jsx index f201ff683..39964e677 100644 --- a/ui/page/signInVerify/view.jsx +++ b/ui/page/signInVerify/view.jsx @@ -8,8 +8,9 @@ import Button from 'component/button'; import { Lbryio } from 'lbryinc'; import I18nMessage from 'component/i18nMessage'; import Card from 'component/common/card'; +// @if TARGET='web' import Pixel from 'web/component/pixel'; - +// @endif type Props = { history: { push: (string) => void, location: { search: string } }, doToast: ({}) => void, From 0cf6fe3df5e40c5aef9733846a0f88caf8486fbc Mon Sep 17 00:00:00 2001 From: infinite-persistence Date: Thu, 17 Jun 2021 11:11:40 +0800 Subject: [PATCH 12/24] Comments Pagination ## Issue 6158 - Support Comment Pagination --- flow-typed/Comment.js | 65 ++++- ui/comments.js | 1 + ui/component/channelDiscussion/index.js | 4 +- ui/component/channelDiscussion/view.jsx | 6 +- ui/component/comment/index.js | 43 ++-- ui/component/comment/view.jsx | 80 +++++- ui/component/commentCreate/view.jsx | 11 +- ui/component/commentMenuList/index.js | 4 +- ui/component/commentMenuList/view.jsx | 13 +- ui/component/commentReactions/index.js | 23 +- ui/component/commentsList/index.js | 40 +-- ui/component/commentsList/view.jsx | 201 +++++++++------ ui/component/commentsReplies/index.js | 3 +- ui/component/commentsReplies/view.jsx | 80 +++--- ui/component/livestreamComments/view.jsx | 4 +- ui/constants/action_types.js | 2 + ui/constants/comment.js | 10 + ui/page/file/index.js | 4 +- ui/page/file/view.jsx | 8 +- ui/redux/actions/comments.js | 105 +++++++- ui/redux/reducers/comments.js | 315 ++++++++++++++++++++--- ui/redux/selectors/comments.js | 31 ++- ui/util/comments.js | 9 +- 23 files changed, 793 insertions(+), 269 deletions(-) diff --git a/flow-typed/Comment.js b/flow-typed/Comment.js index adbecea26..b0a09af3c 100644 --- a/flow-typed/Comment.js +++ b/flow-typed/Comment.js @@ -13,6 +13,7 @@ declare type Comment = { 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). }; declare type PerChannelSettings = { @@ -27,15 +28,21 @@ declare type PerChannelSettings = { declare type CommentsState = { commentsByUri: { [string]: string }, superChatsByUri: { [string]: { totalAmount: number, comments: Array } }, - byId: { [string]: Array }, - repliesByParentId: { [string]: Array }, // ParentCommentID -> list of reply comments - topLevelCommentsById: { [string]: Array }, // ClaimID -> list of top level comments + byId: { [string]: Array }, // ClaimID -> list of fetched comment IDs. + totalCommentsById: {}, // ClaimId -> ultimate total (including replies) in commentron. + repliesByParentId: { [string]: Array }, // ParentCommentID -> list of fetched replies. + totalRepliesByParentId: {}, // ParentCommentID -> total replies in commentron. + topLevelCommentsById: { [string]: Array }, // ClaimID -> list of fetched top level comments. + topLevelTotalPagesById: { [string]: number }, // ClaimID -> total number of top-level pages in commentron. Based on COMMENT_PAGE_SIZE_TOP_LEVEL. + topLevelTotalCommentsById: { [string]: number }, // ClaimID -> total top level comments in commentron. commentById: { [string]: Comment }, + linkedCommentAncestors: { [string]: Array }, // {"linkedCommentId": ["parentId", "grandParentId", ...]} isLoading: boolean, + isLoadingByParentId: { [string]: boolean }, myComments: ?Set, isFetchingReacts: boolean, - myReactsByCommentId: any, - othersReactsByCommentId: any, + myReactsByCommentId: ?{ [string]: Array }, // {"CommentId:MyChannelId": ["like", "dislike", ...]} + othersReactsByCommentId: ?{ [string]: { [string]: number } }, // {"CommentId:MyChannelId": {"like": 2, "dislike": 2, ...}} pendingCommentReactions: Array, moderationBlockList: ?Array, // @KP rename to "personalBlockList"? adminBlockList: ?Array, @@ -64,17 +71,47 @@ declare type CommentReactParams = { remove?: boolean, }; +declare type CommentReactListParams = { + comment_ids?: string, + channel_id?: string, + channel_name?: string, + wallet_id?: string, + react_types?: string, +}; + declare type CommentListParams = { - page: number, - page_size: number, - claim_id: string, + 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, // NEWEST=0, OLDEST=1, CONTROVERSY=2, POPULARITY=3, }; declare type CommentListResponse = { items: Array, - total_amount: number, + 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 = { + items: Comment, + ancestors: Array, +} + declare type CommentAbandonParams = { comment_id: string, creator_channel_id?: string, @@ -94,6 +131,16 @@ declare type CommentCreateParams = { declare type SuperListParams = {}; +declare type SuperListResponse = { + page: number, + page_size: number, + total_pages: number, + total_items: number, + total_amount: number, + items: Array, + has_hidden_comments: boolean, +}; + declare type ModerationBlockParams = {}; declare type ModerationAddDelegateParams = { diff --git a/ui/comments.js b/ui/comments.js index 912c7266e..885d54715 100644 --- a/ui/comments.js +++ b/ui/comments.js @@ -17,6 +17,7 @@ const Comments = { comment_list: (params: CommentListParams) => fetchCommentsApi('comment.List', params), comment_abandon: (params: CommentAbandonParams) => fetchCommentsApi('comment.Abandon', params), comment_create: (params: CommentCreateParams) => fetchCommentsApi('comment.Create', params), + comment_by_id: (params: CommentByIdParams) => fetchCommentsApi('comment.ByID', params), setting_list: (params: SettingsParams) => fetchCommentsApi('setting.List', params), setting_block_word: (params: BlockWordParams) => fetchCommentsApi('setting.BlockWord', params), setting_unblock_word: (params: BlockWordParams) => fetchCommentsApi('setting.UnBlockWord', params), diff --git a/ui/component/channelDiscussion/index.js b/ui/component/channelDiscussion/index.js index cb0c52704..ff198701f 100644 --- a/ui/component/channelDiscussion/index.js +++ b/ui/component/channelDiscussion/index.js @@ -1,6 +1,5 @@ import { connect } from 'react-redux'; import { withRouter } from 'react-router'; -import { makeSelectCommentForCommentId } from 'redux/selectors/comments'; import { DISABLE_COMMENTS_TAG } from 'constants/tags'; import ChannelDiscussion from './view'; import { makeSelectTagInClaimOrChannelForUri } from 'lbry-redux'; @@ -8,10 +7,9 @@ import { makeSelectTagInClaimOrChannelForUri } from 'lbry-redux'; const select = (state, props) => { const { search } = props.location; const urlParams = new URLSearchParams(search); - const linkedCommentId = urlParams.get('lc'); return { - linkedComment: makeSelectCommentForCommentId(linkedCommentId)(state), + linkedCommentId: urlParams.get('lc'), commentsDisabled: makeSelectTagInClaimOrChannelForUri(props.uri, DISABLE_COMMENTS_TAG)(state), }; }; diff --git a/ui/component/channelDiscussion/view.jsx b/ui/component/channelDiscussion/view.jsx index 50a308a70..15b215f50 100644 --- a/ui/component/channelDiscussion/view.jsx +++ b/ui/component/channelDiscussion/view.jsx @@ -5,19 +5,19 @@ import Empty from 'component/common/empty'; type Props = { uri: string, - linkedComment: ?any, + linkedCommentId?: string, commentsDisabled: boolean, }; function ChannelDiscussion(props: Props) { - const { uri, linkedComment, commentsDisabled } = props; + const { uri, linkedCommentId, commentsDisabled } = props; if (commentsDisabled) { return ; } return (
- +
); } diff --git a/ui/component/comment/index.js b/ui/component/comment/index.js index 0e92f94f5..9e4ed5825 100644 --- a/ui/component/comment/index.js +++ b/ui/component/comment/index.js @@ -1,30 +1,43 @@ import { connect } from 'react-redux'; -import { makeSelectStakedLevelForChannelUri, makeSelectClaimForUri, makeSelectThumbnailForUri, selectMyChannelClaims } from 'lbry-redux'; -import { doCommentUpdate } from 'redux/actions/comments'; +import { + makeSelectStakedLevelForChannelUri, + makeSelectClaimForUri, + makeSelectThumbnailForUri, + selectMyChannelClaims, +} from 'lbry-redux'; +import { doCommentUpdate, doCommentList } from 'redux/actions/comments'; import { makeSelectChannelIsMuted } from 'redux/selectors/blocked'; import { doToast } from 'redux/actions/notifications'; import { doSetPlayingUri } from 'redux/actions/content'; import { selectUserVerifiedEmail } from 'redux/selectors/user'; -import { makeSelectOthersReactionsForComment } from 'redux/selectors/comments'; -import { selectActiveChannelClaim } from 'redux/selectors/app'; +import { selectLinkedCommentAncestors, makeSelectOthersReactionsForComment } from 'redux/selectors/comments'; +import { selectActiveChannelId, selectActiveChannelClaim } from 'redux/selectors/app'; import { selectPlayingUri } from 'redux/selectors/content'; import Comment from './view'; -const select = (state, props) => ({ - claim: makeSelectClaimForUri(props.uri)(state), - thumbnail: props.authorUri && makeSelectThumbnailForUri(props.authorUri)(state), - channelIsBlocked: props.authorUri && makeSelectChannelIsMuted(props.authorUri)(state), - commentingEnabled: IS_WEB ? Boolean(selectUserVerifiedEmail(state)) : true, - othersReacts: makeSelectOthersReactionsForComment(props.commentId)(state), - activeChannelClaim: selectActiveChannelClaim(state), - myChannels: selectMyChannelClaims(state), - playingUri: selectPlayingUri(state), - stakedLevel: makeSelectStakedLevelForChannelUri(props.authorUri)(state), -}); +const select = (state, props) => { + const activeChannelId = selectActiveChannelId(state); + const reactionKey = activeChannelId ? `${props.commentId}:${activeChannelId}` : props.commentId; + + return { + claim: makeSelectClaimForUri(props.uri)(state), + thumbnail: props.authorUri && makeSelectThumbnailForUri(props.authorUri)(state), + channelIsBlocked: props.authorUri && makeSelectChannelIsMuted(props.authorUri)(state), + commentingEnabled: IS_WEB ? Boolean(selectUserVerifiedEmail(state)) : true, + othersReacts: makeSelectOthersReactionsForComment(reactionKey)(state), + activeChannelClaim: selectActiveChannelClaim(state), + myChannels: selectMyChannelClaims(state), + playingUri: selectPlayingUri(state), + stakedLevel: makeSelectStakedLevelForChannelUri(props.authorUri)(state), + linkedCommentAncestors: selectLinkedCommentAncestors(state), + }; +}; const perform = (dispatch) => ({ clearPlayingUri: () => dispatch(doSetPlayingUri({ uri: null })), updateComment: (commentId, comment) => dispatch(doCommentUpdate(commentId, comment)), + fetchReplies: (uri, parentId, page, pageSize, sortBy) => + dispatch(doCommentList(uri, parentId, page, pageSize, sortBy)), doToast: (options) => dispatch(doToast(options)), }); diff --git a/ui/component/comment/view.jsx b/ui/component/comment/view.jsx index 421e4f7b4..9dbb20deb 100644 --- a/ui/component/comment/view.jsx +++ b/ui/component/comment/view.jsx @@ -1,6 +1,7 @@ // @flow import * as ICONS from 'constants/icons'; import * as PAGES from 'constants/pages'; +import { SORT_BY, COMMENT_PAGE_SIZE_REPLIES } from 'constants/comment'; import { FF_MAX_CHARS_IN_COMMENT } from 'constants/form-field'; import { SITE_NAME, SIMPLE_SITE, ENABLE_COMMENT_REACTIONS } from 'config'; import React, { useEffect, useState } from 'react'; @@ -36,8 +37,10 @@ type Props = { claimIsMine: boolean, // if you control the claim which this comment was posted on commentIsMine: boolean, // if this comment was signed by an owned channel updateComment: (string, string) => void, + fetchReplies: (string, string, number, number, number) => void, commentModBlock: (string) => void, - linkedComment?: any, + linkedCommentId?: string, + linkedCommentAncestors: { [string]: Array }, myChannels: ?Array, commentingEnabled: boolean, doToast: ({ message: string }) => void, @@ -53,6 +56,7 @@ type Props = { playingUri: ?PlayingUri, stakedLevel: number, supportAmount: number, + numDirectReplies: number, }; const LENGTH_TO_COLLAPSE = 300; @@ -71,7 +75,9 @@ function Comment(props: Props) { commentIsMine, commentId, updateComment, - linkedComment, + fetchReplies, + linkedCommentId, + linkedCommentAncestors, commentingEnabled, myChannels, doToast, @@ -82,18 +88,23 @@ function Comment(props: Props) { playingUri, stakedLevel, supportAmount, + numDirectReplies, } = props; + const { push, replace, location: { pathname, search }, } = useHistory(); + const [isReplying, setReplying] = React.useState(false); const [isEditing, setEditing] = useState(false); const [editedMessage, setCommentValue] = useState(message); const [charCount, setCharCount] = useState(editedMessage.length); // used for controlling the visibility of the menu icon const [mouseIsHovering, setMouseHover] = useState(false); + const [showReplies, setShowReplies] = useState(false); + const [page, setPage] = useState(0); const [advancedEditor] = usePersistedState('comment-editor-mode', false); const [displayDeadComment, setDisplayDeadComment] = React.useState(false); const hasChannels = myChannels && myChannels.length > 0; @@ -111,6 +122,18 @@ function Comment(props: Props) { } } catch (e) {} + // Auto-expand (limited to linked-comments for now, but can be for all) + useEffect(() => { + if ( + linkedCommentId && + linkedCommentAncestors[linkedCommentId] && + linkedCommentAncestors[linkedCommentId].includes(commentId) + ) { + setShowReplies(true); + setPage(1); + } + }, []); // eslint-disable-line react-hooks/exhaustive-deps + useEffect(() => { if (isEditing) { setCharCount(editedMessage.length); @@ -131,6 +154,12 @@ function Comment(props: Props) { } }, [author, authorUri, editedMessage, isEditing, setEditing]); + useEffect(() => { + if (page > 0) { + fetchReplies(uri, commentId, page, COMMENT_PAGE_SIZE_REPLIES, SORT_BY.OLDEST); + } + }, [page, uri, commentId, fetchReplies]); + function handleEditMessageChanged(event) { setCommentValue(!SIMPLE_SITE && advancedEditor ? event : event.target.value); } @@ -176,7 +205,7 @@ function Comment(props: Props) { >
@@ -302,13 +331,43 @@ function Comment(props: Props) { {ENABLE_COMMENT_REACTIONS && }
+ {numDirectReplies > 0 && !showReplies && ( +
+
+ )} + + {numDirectReplies > 0 && showReplies && ( +
+
+ )} + {isReplying && ( setReplying(false)} - onCancelReplying={() => setReplying(false)} + onDoneReplying={() => { + setShowReplies(true); + setReplying(false); + }} + onCancelReplying={() => { + setReplying(false); + }} /> )} @@ -317,7 +376,16 @@ function Comment(props: Props) { - + {showReplies && ( + setPage(page + 1)} + /> + )} ); } diff --git a/ui/component/commentCreate/view.jsx b/ui/component/commentCreate/view.jsx index e683d9b20..4c32eca47 100644 --- a/ui/component/commentCreate/view.jsx +++ b/ui/component/commentCreate/view.jsx @@ -35,7 +35,6 @@ type Props = { toast: (string) => void, claimIsMine: boolean, sendTip: ({}, (any) => void, (any) => void) => void, - justCommented: Array, }; export function CommentCreate(props: Props) { @@ -54,7 +53,6 @@ export function CommentCreate(props: Props) { livestream, claimIsMine, sendTip, - justCommented, } = props; const buttonref: ElementRef = React.useRef(); const { @@ -153,7 +151,6 @@ export function CommentCreate(props: Props) { setIsReviewingSupportComment(false); setIsSupportComment(false); setCommentFailure(false); - justCommented.push(res.comment_id); if (onDoneReplying) { onDoneReplying(); @@ -217,7 +214,13 @@ export function CommentCreate(props: Props) { autoFocus button="primary" disabled={disabled} - label={isSubmitting ? __('Sending...') : (commentFailure && tipAmount === successTip.tipAmount) ? __('Re-submit') : __('Send')} + label={ + isSubmitting + ? __('Sending...') + : commentFailure && tipAmount === successTip.tipAmount + ? __('Re-submit') + : __('Send') + } onClick={handleSupportComment} />