Batch resolve pin urls in 2 ways
Expanded homepage pins to support 2 types of input (if both are passed in, pinnedUrls take precedence): (1) pinnedUrls --> uses doResolveUris (resolve) (2) pinnedClaimIds --> uses doResolveClaimIds (claim_search) Instead of injecting the pinned URLs directly, we inject from `resolvedPinUris`, which will be blank until the uris are resolved, thus preventing it from being resolved individually from the tiles. There's additional complexity with the `claim_search` method, as the rest of the code deals with uris instead of IDs. Fortunately, it's all contained with `useResolvePins`, so removal would be easier when the batch `resolve` issue is fixed.
This commit is contained in:
parent
dd5f4872fc
commit
f5034f74ca
10 changed files with 134 additions and 31 deletions
1
flow-typed/homepage.js
vendored
1
flow-typed/homepage.js
vendored
|
@ -19,6 +19,7 @@ declare type RowDataItem = {
|
|||
icon?: string,
|
||||
extra?: any,
|
||||
pinnedUrls?: Array<string>,
|
||||
pinnedClaimIds?: Array<string>, // pinnedUrls takes precedence
|
||||
options?: {
|
||||
channelIds?: Array<string>,
|
||||
limitClaimsPerChannel?: number,
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import { connect } from 'react-redux';
|
||||
import {
|
||||
selectById,
|
||||
selectClaimsByUri,
|
||||
selectClaimSearchByQuery,
|
||||
selectClaimSearchByQueryLastPageReached,
|
||||
selectFetchingClaimSearch,
|
||||
} from 'redux/selectors/claims';
|
||||
import { doClaimSearch } from 'redux/actions/claims';
|
||||
import { doClaimSearch, doResolveClaimIds, doResolveUris } from 'redux/actions/claims';
|
||||
import * as SETTINGS from 'constants/settings';
|
||||
import { selectFollowedTags } from 'redux/selectors/tags';
|
||||
import { selectMutedChannels } from 'redux/selectors/blocked';
|
||||
|
@ -20,6 +21,7 @@ const select = (state, props) => ({
|
|||
claimSearchByQuery: selectClaimSearchByQuery(state),
|
||||
claimSearchByQueryLastPageReached: selectClaimSearchByQueryLastPageReached(state),
|
||||
claimsByUri: selectClaimsByUri(state),
|
||||
claimsById: selectById(state),
|
||||
loading: props.loading !== undefined ? props.loading : selectFetchingClaimSearch(state),
|
||||
showNsfw: selectShowMatureContent(state),
|
||||
hideReposts: selectClientSetting(state, SETTINGS.HIDE_REPOSTS),
|
||||
|
@ -33,6 +35,8 @@ const perform = {
|
|||
doClaimSearch,
|
||||
doFetchViewCount,
|
||||
doFetchUserMemberships,
|
||||
doResolveClaimIds,
|
||||
doResolveUris,
|
||||
};
|
||||
|
||||
export default connect(select, perform)(ClaimListDiscover);
|
||||
|
|
|
@ -18,13 +18,14 @@ import I18nMessage from 'component/i18nMessage';
|
|||
import LangFilterIndicator from 'component/langFilterIndicator';
|
||||
import ClaimListHeader from 'component/claimListHeader';
|
||||
import useFetchViewCount from 'effects/use-fetch-view-count';
|
||||
import useResolvePins from 'effects/use-resolve-pins';
|
||||
import { useIsLargeScreen } from 'effects/use-screensize';
|
||||
import useGetUserMemberships from 'effects/use-get-user-memberships';
|
||||
|
||||
type Props = {
|
||||
uris: Array<string>,
|
||||
prefixUris?: Array<string>,
|
||||
pins?: { urls: Array<string>, onlyPinForOrder?: string },
|
||||
pins?: { urls?: Array<string>, claimIds?: Array<string>, onlyPinForOrder?: string },
|
||||
name?: string,
|
||||
type: string,
|
||||
pageSize?: number,
|
||||
|
@ -88,6 +89,7 @@ type Props = {
|
|||
claimSearchByQuery: { [string]: Array<string> },
|
||||
claimSearchByQueryLastPageReached: { [string]: boolean },
|
||||
claimsByUri: { [string]: any },
|
||||
claimsById: { [string]: any },
|
||||
loading: boolean,
|
||||
showNsfw: boolean,
|
||||
hideReposts: boolean,
|
||||
|
@ -100,6 +102,8 @@ type Props = {
|
|||
doClaimSearch: ({}) => void,
|
||||
doFetchViewCount: (claimIdCsv: string) => void,
|
||||
doFetchUserMemberships: (claimIdCsv: string) => void,
|
||||
doResolveClaimIds: (Array<string>) => Promise<any>,
|
||||
doResolveUris: (Array<string>, boolean) => Promise<any>,
|
||||
|
||||
hideLayoutButton?: boolean,
|
||||
loadedCallback?: (number) => void,
|
||||
|
@ -173,6 +177,7 @@ function ClaimListDiscover(props: Props) {
|
|||
showNoSourceClaims,
|
||||
empty,
|
||||
claimsByUri,
|
||||
claimsById,
|
||||
doFetchViewCount,
|
||||
hideLayoutButton = false,
|
||||
loadedCallback,
|
||||
|
@ -181,7 +186,11 @@ function ClaimListDiscover(props: Props) {
|
|||
excludeUris = [],
|
||||
doFetchUserMemberships,
|
||||
swipeLayout = false,
|
||||
doResolveUris,
|
||||
doResolveClaimIds,
|
||||
} = props;
|
||||
|
||||
const resolvedPinUris = useResolvePins({ pins, claimsById, doResolveClaimIds, doResolveUris });
|
||||
const didNavigateForward = history.action === 'PUSH';
|
||||
const { search } = location;
|
||||
const prevUris = React.useRef();
|
||||
|
@ -525,7 +534,7 @@ function ClaimListDiscover(props: Props) {
|
|||
if (uris) {
|
||||
// --- direct uris
|
||||
finalUris = uris;
|
||||
injectPinUrls(finalUris, orderParam, pins);
|
||||
injectPinUrls(finalUris, orderParam, pins, resolvedPinUris);
|
||||
finalUris = filterExcludedUris(finalUris, excludeUris);
|
||||
} else {
|
||||
// --- searched uris
|
||||
|
@ -533,7 +542,7 @@ function ClaimListDiscover(props: Props) {
|
|||
finalUris = prevUris.current;
|
||||
} else {
|
||||
finalUris = claimSearchResult;
|
||||
injectPinUrls(finalUris, orderParam, pins);
|
||||
injectPinUrls(finalUris, orderParam, pins, resolvedPinUris);
|
||||
finalUris = filterExcludedUris(finalUris, excludeUris);
|
||||
prevUris.current = finalUris;
|
||||
}
|
||||
|
@ -611,14 +620,13 @@ function ClaimListDiscover(props: Props) {
|
|||
return order_by;
|
||||
}
|
||||
|
||||
function injectPinUrls(uris, order, pins) {
|
||||
if (!pins || !pins.urls || (pins.onlyPinForOrder && pins.onlyPinForOrder !== order)) {
|
||||
function injectPinUrls(uris, order, pins, resolvedPinUris) {
|
||||
if (!pins || !uris || uris.length <= 2 || (pins.onlyPinForOrder && pins.onlyPinForOrder !== order)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pinUrls = pins.urls;
|
||||
if (pinUrls && uris && uris.length > 2) {
|
||||
pinUrls.forEach((pin) => {
|
||||
if (resolvedPinUris) {
|
||||
resolvedPinUris.forEach((pin) => {
|
||||
if (uris.includes(pin)) {
|
||||
uris.splice(uris.indexOf(pin), 1);
|
||||
} else {
|
||||
|
@ -626,7 +634,7 @@ function ClaimListDiscover(props: Props) {
|
|||
}
|
||||
});
|
||||
|
||||
uris.splice(2, 0, ...pinUrls);
|
||||
uris.splice(2, 0, ...resolvedPinUris);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
// @flow
|
||||
import { connect } from 'react-redux';
|
||||
import { withRouter } from 'react-router';
|
||||
import { selectClaimSearchByQuery, selectFetchingClaimSearchByQuery, selectClaimsByUri } from 'redux/selectors/claims';
|
||||
import { doClaimSearch } from 'redux/actions/claims';
|
||||
import {
|
||||
selectClaimSearchByQuery,
|
||||
selectFetchingClaimSearchByQuery,
|
||||
selectClaimsByUri,
|
||||
selectById,
|
||||
} from 'redux/selectors/claims';
|
||||
import { doClaimSearch, doResolveClaimIds, doResolveUris } from 'redux/actions/claims';
|
||||
import { doFetchUserMemberships } from 'redux/actions/user';
|
||||
import * as SETTINGS from 'constants/settings';
|
||||
import { MATURE_TAGS } from 'constants/tags';
|
||||
|
@ -35,6 +40,7 @@ const select = (state, props) => {
|
|||
return {
|
||||
claimSearchResults: selectClaimSearchByQuery(state)[searchKey],
|
||||
claimsByUri: selectClaimsByUri(state),
|
||||
claimsById: selectById(state),
|
||||
fetchingClaimSearch: selectFetchingClaimSearchByQuery(state)[searchKey],
|
||||
showNsfw,
|
||||
hideReposts,
|
||||
|
@ -47,6 +53,8 @@ const perform = {
|
|||
doClaimSearch,
|
||||
doFetchViewCount,
|
||||
doFetchUserMemberships,
|
||||
doResolveClaimIds,
|
||||
doResolveUris,
|
||||
};
|
||||
|
||||
export default withRouter(connect(select, perform)(ClaimListDiscover));
|
||||
|
|
|
@ -4,6 +4,7 @@ import React from 'react';
|
|||
import ClaimPreviewTile from 'component/claimPreviewTile';
|
||||
import useFetchViewCount from 'effects/use-fetch-view-count';
|
||||
import useLastVisibleItem from 'effects/use-last-visible-item';
|
||||
import useResolvePins from 'effects/use-resolve-pins';
|
||||
import useGetUserMemberships from 'effects/use-get-user-memberships';
|
||||
|
||||
function urisEqual(prev: ?Array<string>, next: ?Array<string>) {
|
||||
|
@ -25,7 +26,7 @@ function urisEqual(prev: ?Array<string>, next: ?Array<string>) {
|
|||
|
||||
type Props = {
|
||||
prefixUris?: Array<string>,
|
||||
pinUrls?: Array<string>,
|
||||
pins?: { urls?: Array<string>, claimIds?: Array<string>, onlyPinForOrder?: string },
|
||||
uris: Array<string>,
|
||||
injectedItem?: { node: Node, index?: number, replace?: boolean },
|
||||
showNoSourceClaims?: boolean,
|
||||
|
@ -51,6 +52,7 @@ type Props = {
|
|||
location: { search: string },
|
||||
claimSearchResults: Array<string>,
|
||||
claimsByUri: { [string]: any },
|
||||
claimsById: { [string]: any },
|
||||
fetchingClaimSearch: boolean,
|
||||
showNsfw: boolean,
|
||||
hideReposts: boolean,
|
||||
|
@ -59,6 +61,8 @@ type Props = {
|
|||
doClaimSearch: ({}) => void,
|
||||
doFetchViewCount: (claimIdCsv: string) => void,
|
||||
doFetchUserMemberships: (claimIdCsv: string) => void,
|
||||
doResolveClaimIds: (Array<string>) => Promise<any>,
|
||||
doResolveUris: (Array<string>, boolean) => Promise<any>,
|
||||
};
|
||||
|
||||
function ClaimTilesDiscover(props: Props) {
|
||||
|
@ -66,12 +70,13 @@ function ClaimTilesDiscover(props: Props) {
|
|||
doClaimSearch,
|
||||
claimSearchResults,
|
||||
claimsByUri,
|
||||
claimsById,
|
||||
fetchViewCount,
|
||||
fetchingClaimSearch,
|
||||
hasNoSource,
|
||||
// forceShowReposts = false,
|
||||
renderProperties,
|
||||
pinUrls,
|
||||
pins,
|
||||
prefixUris,
|
||||
injectedItem,
|
||||
showNoSourceClaims,
|
||||
|
@ -79,6 +84,8 @@ function ClaimTilesDiscover(props: Props) {
|
|||
pageSize = 8,
|
||||
optionsStringified,
|
||||
doFetchUserMemberships,
|
||||
doResolveClaimIds,
|
||||
doResolveUris,
|
||||
} = props;
|
||||
|
||||
const listRef = React.useRef();
|
||||
|
@ -87,21 +94,15 @@ function ClaimTilesDiscover(props: Props) {
|
|||
const prevUris = React.useRef();
|
||||
const claimSearchUris = claimSearchResults || [];
|
||||
const isUnfetchedClaimSearch = claimSearchResults === undefined;
|
||||
const resolvedPinUris = useResolvePins({ pins, claimsById, doResolveClaimIds, doResolveUris });
|
||||
|
||||
const shouldPerformSearch = !fetchingClaimSearch && claimSearchUris.length === 0;
|
||||
|
||||
const uris = (prefixUris || []).concat(claimSearchUris);
|
||||
if (prefixUris && prefixUris.length) uris.splice(prefixUris.length * -1, prefixUris.length);
|
||||
|
||||
if (pinUrls && uris && uris.length > 2 && window.location.pathname === '/') {
|
||||
pinUrls.forEach((pin) => {
|
||||
if (uris.indexOf(pin) !== -1) {
|
||||
uris.splice(uris.indexOf(pin), 1);
|
||||
} else {
|
||||
uris.pop();
|
||||
}
|
||||
});
|
||||
uris.splice(2, 0, ...pinUrls);
|
||||
if (window.location.pathname === '/') {
|
||||
injectPinUrls(uris, pins, resolvedPinUris);
|
||||
}
|
||||
|
||||
if (uris.length > 0 && uris.length < pageSize && shouldPerformSearch) {
|
||||
|
@ -117,11 +118,31 @@ function ClaimTilesDiscover(props: Props) {
|
|||
// --------------------------------------------------------------------------
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
function injectPinUrls(uris, pins, resolvedPinUris) {
|
||||
if (!pins || !uris || uris.length <= 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (resolvedPinUris) {
|
||||
resolvedPinUris.forEach((pin) => {
|
||||
if (uris.includes(pin)) {
|
||||
uris.splice(uris.indexOf(pin), 1);
|
||||
} else {
|
||||
uris.pop();
|
||||
}
|
||||
});
|
||||
|
||||
uris.splice(2, 0, ...resolvedPinUris);
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
useFetchViewCount(fetchViewCount, uris, claimsByUri, doFetchViewCount);
|
||||
|
||||
useGetUserMemberships(true, uris, claimsByUri, doFetchUserMemberships);
|
||||
|
||||
// Run `doClaimSearch`
|
||||
React.useEffect(() => {
|
||||
if (shouldPerformSearch) {
|
||||
const searchOptions = JSON.parse(optionsStringified);
|
||||
|
@ -129,6 +150,9 @@ function ClaimTilesDiscover(props: Props) {
|
|||
}
|
||||
}, [doClaimSearch, shouldPerformSearch, optionsStringified]);
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
return (
|
||||
<ul ref={listRef} className="claim-grid">
|
||||
{finalUris && finalUris.length
|
||||
|
|
|
@ -174,7 +174,9 @@ function SideNavigation(props: Props) {
|
|||
|
||||
const isLargeScreen = useIsLargeScreen();
|
||||
|
||||
const EXTRA_SIDEBAR_LINKS = GetLinksData(homepageData, isLargeScreen).map(({ pinnedUrls, ...theRest }) => theRest);
|
||||
const EXTRA_SIDEBAR_LINKS = GetLinksData(homepageData, isLargeScreen).map(
|
||||
({ pinnedUrls, pinnedClaimIds, ...theRest }) => theRest
|
||||
);
|
||||
|
||||
const MOBILE_LINKS: Array<SideNavLink> = [
|
||||
{
|
||||
|
|
51
ui/effects/use-resolve-pins.js
Normal file
51
ui/effects/use-resolve-pins.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import useFetched from 'effects/use-fetched';
|
||||
|
||||
type Props = {
|
||||
pins?: { urls?: Array<string>, claimIds?: Array<string>, onlyPinForOrder?: string },
|
||||
claimsById: { [string]: Claim },
|
||||
doResolveClaimIds: (Array<string>) => Promise<any>,
|
||||
doResolveUris: (Array<string>, boolean) => Promise<any>,
|
||||
};
|
||||
|
||||
export default function useResolvePins(props: Props) {
|
||||
const { pins, claimsById, doResolveClaimIds, doResolveUris } = props;
|
||||
|
||||
const [resolvedPinUris, setResolvedPinUris] = React.useState(pins ? undefined : null);
|
||||
const [resolvingPinUris, setResolvingPinUris] = React.useState(false);
|
||||
const hasResolvedPinUris = useFetched(resolvingPinUris);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (resolvedPinUris === undefined && pins && !resolvingPinUris) {
|
||||
if (pins.urls) {
|
||||
doResolveUris(pins.urls, true).finally(() => setResolvedPinUris(pins.urls));
|
||||
} else if (pins.claimIds) {
|
||||
// setResolvingPinUris is only needed for claim_search.
|
||||
// doResolveUris uses selectResolvingUris internally to prevent double call.
|
||||
setResolvingPinUris(true);
|
||||
|
||||
// We can't use .then() here to grab the `claim_search` uris directly,
|
||||
// because we skip those that are already resolved. Instead, we mark a
|
||||
// flag here, then populate the array in the other effect below in the
|
||||
// next render cycle (redux would be updated by then). Pretty dumb.
|
||||
// $FlowFixMe: already checked for null `pins`, but flow can't see it when there's code above it? Wow.
|
||||
doResolveClaimIds(pins.claimIds).finally(() => setResolvingPinUris(false));
|
||||
} else {
|
||||
setResolvedPinUris(null);
|
||||
}
|
||||
}
|
||||
}, [resolvedPinUris, pins, doResolveUris, doResolveClaimIds, resolvingPinUris]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (hasResolvedPinUris) {
|
||||
if (pins && pins.claimIds) {
|
||||
setResolvedPinUris(pins.claimIds.map<string>((id) => claimsById[id]?.canonical_url));
|
||||
}
|
||||
}
|
||||
// Only do this over a false->true->false transition for hasResolvedPinUris.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [hasResolvedPinUris]);
|
||||
|
||||
return resolvedPinUris;
|
||||
}
|
|
@ -128,9 +128,10 @@ function DiscoverPage(props: Props) {
|
|||
}
|
||||
|
||||
function getPins(routeProps) {
|
||||
if (routeProps && routeProps.pinnedUrls) {
|
||||
if (routeProps && (routeProps.pinnedUrls || routeProps.pinnedClaimIds)) {
|
||||
return {
|
||||
urls: routeProps.pinnedUrls,
|
||||
claimIds: routeProps.pinnedClaimIds,
|
||||
onlyPinForOrder: CS.ORDER_BY_TRENDING,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -146,7 +146,7 @@ function HomePage(props: Props) {
|
|||
);
|
||||
};
|
||||
|
||||
function getRowElements(id, title, route, link, icon, help, options, index, pinUrls) {
|
||||
function getRowElements(id, title, route, link, icon, help, options, index, pinUrls, pinnedClaimIds) {
|
||||
const tilePlaceholder = (
|
||||
<ul className="claim-grid">
|
||||
{new Array(options.pageSize || 8).fill(1).map((x, i) => (
|
||||
|
@ -161,7 +161,7 @@ function HomePage(props: Props) {
|
|||
showNoSourceClaims={ENABLE_NO_SOURCE_CLAIMS}
|
||||
hasSource
|
||||
prefixUris={getLivestreamUris(activeLivestreams, options.channelIds)}
|
||||
pinUrls={pinUrls}
|
||||
pins={{ urls: pinUrls, claimIds: pinnedClaimIds }}
|
||||
injectedItem={
|
||||
index === 0 && {
|
||||
node: <Ads small type="video" tileLayout />,
|
||||
|
@ -249,9 +249,11 @@ function HomePage(props: Props) {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{sortedRowData.map(({ id, title, route, link, icon, help, pinnedUrls: pinUrls, options = {} }, index) => {
|
||||
return getRowElements(id, title, route, link, icon, help, options, index, pinUrls);
|
||||
})}
|
||||
{sortedRowData.map(
|
||||
({ id, title, route, link, icon, help, pinnedUrls: pinUrls, pinnedClaimIds, options = {} }, index) => {
|
||||
return getRowElements(id, title, route, link, icon, help, options, index, pinUrls, pinnedClaimIds);
|
||||
}
|
||||
)}
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ export type HomepageCat = {
|
|||
order?: string,
|
||||
tags?: Array<string>,
|
||||
pinnedUrls?: Array<string>,
|
||||
pinnedClaimIds?: Array<string>, // pinnedUrls takes precedence
|
||||
mixIn?: Array<string>,
|
||||
};
|
||||
|
||||
|
@ -89,6 +90,7 @@ export const getHomepageRowForCat = (key: string, cat: HomepageCat) => {
|
|||
icon: cat.icon || '', // some default
|
||||
title: cat.label,
|
||||
pinnedUrls: cat.pinnedUrls,
|
||||
pinnedClaimIds: cat.pinnedClaimIds,
|
||||
options: {
|
||||
claimType: cat.claimType || ['stream', 'repost'],
|
||||
channelIds: cat.channelIds,
|
||||
|
|
Loading…
Reference in a new issue