Support x-truth-ad-indexes header

x-truth-ads
Alex Gleason 2022-11-10 19:10:50 -06:00
rodzic d19a93f109
commit f1a4b87dd9
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 7211D1F99744FBB7
6 zmienionych plików z 50 dodań i 11 usunięć

Wyświetl plik

@ -2,6 +2,7 @@ import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutabl
import { getSettings } from 'soapbox/actions/settings';
import { normalizeStatus } from 'soapbox/normalizers';
import { adIndexesFromHeader } from 'soapbox/utils/ads';
import { shouldFilter } from 'soapbox/utils/timelines';
import api, { getLinks } from '../api';
@ -172,8 +173,9 @@ const expandTimeline = (timelineId: string, path: string, params: Record<string,
return api(getState).get(path, { params }).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next');
const adIndexes = adIndexesFromHeader(response);
dispatch(importFetchedStatuses(response.data));
dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.status === 206, isLoadingRecent, isLoadingMore));
dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.status === 206, isLoadingRecent, isLoadingMore, adIndexes));
done();
}).catch(error => {
dispatch(expandTimelineFail(timelineId, error, isLoadingMore));
@ -234,7 +236,15 @@ const expandTimelineRequest = (timeline: string, isLoadingMore: boolean) => ({
skipLoading: !isLoadingMore,
});
const expandTimelineSuccess = (timeline: string, statuses: APIEntity[], next: string | null, partial: boolean, isLoadingRecent: boolean, isLoadingMore: boolean) => ({
const expandTimelineSuccess = (
timeline: string,
statuses: APIEntity[],
next: string | null,
partial: boolean,
isLoadingRecent: boolean,
isLoadingMore: boolean,
adIndexes: number[],
) => ({
type: TIMELINE_EXPAND_SUCCESS,
timeline,
statuses,
@ -242,6 +252,7 @@ const expandTimelineSuccess = (timeline: string, statuses: APIEntity[], next: st
partial,
isLoadingRecent,
skipLoading: !isLoadingMore,
adIndexes,
});
const expandTimelineFail = (timeline: string, error: AxiosError, isLoadingMore: boolean) => ({

Wyświetl plik

@ -13,7 +13,7 @@ import FeedSuggestions from 'soapbox/features/feed-suggestions/feed-suggestions'
import PlaceholderStatus from 'soapbox/features/placeholder/components/placeholder_status';
import { ALGORITHMS } from 'soapbox/features/timeline-insertion';
import PendingStatus from 'soapbox/features/ui/components/pending_status';
import { useSoapboxConfig } from 'soapbox/hooks';
import { useAppSelector, useSoapboxConfig } from 'soapbox/hooks';
import useAds from 'soapbox/queries/ads';
import type { OrderedSet as ImmutableOrderedSet } from 'immutable';
@ -66,6 +66,7 @@ const StatusList: React.FC<IStatusList> = ({
const adsAlgorithm = String(soapboxConfig.extensions.getIn(['ads', 'algorithm', 0]));
const adsOpts = (soapboxConfig.extensions.getIn(['ads', 'algorithm', 1], ImmutableMap()) as ImmutableMap<string, any>).toJS();
const adIndexes = useAppSelector(state => state.timelines.getIn([timelineId, 'adIndexes'], [[]]) as readonly number[][]);
const node = useRef<VirtuosoHandle>(null);
const seed = useRef<string>(uuidv4());
@ -177,12 +178,23 @@ const StatusList: React.FC<IStatusList> = ({
const renderStatuses = (): React.ReactNode[] => {
if (isLoading || statusIds.size > 0) {
let lastAdIndex = 0;
return statusIds.toList().reduce((acc, statusId, index) => {
if (showAds && ads) {
const ad = ALGORITHMS[adsAlgorithm]?.(ads, index, { ...adsOpts, seed: seed.current });
if ((adIndexes[Math.floor(index / 20)] || []).includes(index)) {
const ad = ads[lastAdIndex % ads.length];
if (ad) {
acc.push(renderAd(ad, index));
if (ad) {
acc.push(renderAd(ad, index));
}
lastAdIndex++;
} else {
const ad = ALGORITHMS[adsAlgorithm]?.(ads, index, { ...adsOpts, seed: seed.current });
if (ad) {
acc.push(renderAd(ad, index));
}
}
}

Wyświetl plik

@ -36,7 +36,7 @@ const TestTimeline: React.FC = () => {
React.useEffect(() => {
dispatch(importFetchedStatuses(MOCK_STATUSES));
dispatch(expandTimelineSuccess(timelineId, MOCK_STATUSES, null, false, false, false));
dispatch(expandTimelineSuccess(timelineId, MOCK_STATUSES, null, false, false, false, []));
}, []);
return (

Wyświetl plik

@ -3,7 +3,7 @@ import { linearAlgorithm } from './linear';
import type { PickAlgorithm } from './types';
const ALGORITHMS: Record<any, PickAlgorithm | undefined> = {
const ALGORITHMS: Record<string, PickAlgorithm | undefined> = {
'linear': linearAlgorithm,
'abovefold': abovefoldAlgorithm,
};

Wyświetl plik

@ -53,6 +53,7 @@ const TimelineRecord = ImmutableRecord({
totalQueuedItemsCount: 0, //used for queuedItems overflow for MAX_QUEUED_ITEMS+
loadingFailed: false,
isPartial: false,
adIndexes: [] as readonly number[][],
});
const initialState = ImmutableMap<string, Timeline>();
@ -88,7 +89,7 @@ const setFailed = (state: State, timelineId: string, failed: boolean) => {
return state.update(timelineId, TimelineRecord(), timeline => timeline.set('loadingFailed', failed));
};
const expandNormalizedTimeline = (state: State, timelineId: string, statuses: ImmutableList<ImmutableMap<string, any>>, next: string | null, isPartial: boolean, isLoadingRecent: boolean) => {
const expandNormalizedTimeline = (state: State, timelineId: string, statuses: ImmutableList<ImmutableMap<string, any>>, next: string | null, isPartial: boolean, isLoadingRecent: boolean, adIndexes: number[]) => {
const newIds = getStatusIds(statuses);
return state.update(timelineId, TimelineRecord(), timeline => timeline.withMutations(timeline => {
@ -96,6 +97,10 @@ const expandNormalizedTimeline = (state: State, timelineId: string, statuses: Im
timeline.set('loadingFailed', false);
timeline.set('isPartial', isPartial);
timeline.update('adIndexes', (oldAdIndexes: readonly number[][]) => {
return [...oldAdIndexes, [...adIndexes]];
});
if (!next && !isLoadingRecent) timeline.set('hasMore', false);
// Pinned timelines can be replaced entirely
@ -321,7 +326,7 @@ export default function timelines(state: State = initialState, action: AnyAction
case TIMELINE_EXPAND_FAIL:
return handleExpandFail(state, action.timeline);
case TIMELINE_EXPAND_SUCCESS:
return expandNormalizedTimeline(state, action.timeline, fromJS(action.statuses) as ImmutableList<ImmutableMap<string, any>>, action.next, action.partial, action.isLoadingRecent);
return expandNormalizedTimeline(state, action.timeline, fromJS(action.statuses) as ImmutableList<ImmutableMap<string, any>>, action.next, action.partial, action.isLoadingRecent, action.adIndexes);
case TIMELINE_UPDATE:
return updateTimeline(state, action.timeline, action.statusId);
case TIMELINE_UPDATE_QUEUE:

Wyświetl plik

@ -1,3 +1,4 @@
import type { AxiosResponse } from 'axios';
import type { Ad } from 'soapbox/types/soapbox';
/** Time (ms) window to not display an ad if it's about to expire. */
@ -13,4 +14,14 @@ const isExpired = (ad: Ad, threshold = AD_EXPIRY_THRESHOLD): boolean => {
}
};
export { isExpired };
/** Get ad indexes from X-Truth-Ad-Indexes header. */
const adIndexesFromHeader = (response: AxiosResponse): number[] => {
const header: string = response.headers['x-truth-ad-indexes'];
const strIndexes: string[] = header.split(',');
return strIndexes.map(strIndex => Number(strIndex.trim()));
};
export {
isExpired,
adIndexesFromHeader,
};