merge: codex/fix/all-active-sort

# Conflicts:
#	public/translations/ar/default.json
#	public/translations/bn/default.json
#	public/translations/cs/default.json
#	public/translations/da/default.json
#	public/translations/de/default.json
#	public/translations/el/default.json
#	public/translations/en/default.json
#	public/translations/es/default.json
#	public/translations/fa/default.json
#	public/translations/fi/default.json
#	public/translations/fil/default.json
#	public/translations/fr/default.json
#	public/translations/he/default.json
#	public/translations/hi/default.json
#	public/translations/hu/default.json
#	public/translations/id/default.json
#	public/translations/it/default.json
#	public/translations/ja/default.json
#	public/translations/ko/default.json
#	public/translations/mr/default.json
#	public/translations/nl/default.json
#	public/translations/no/default.json
#	public/translations/pl/default.json
#	public/translations/pt/default.json
#	public/translations/ro/default.json
#	public/translations/ru/default.json
#	public/translations/sq/default.json
#	public/translations/sv/default.json
#	public/translations/te/default.json
#	public/translations/th/default.json
#	public/translations/tr/default.json
#	public/translations/uk/default.json
#	public/translations/ur/default.json
#	public/translations/vi/default.json
#	public/translations/zh/default.json
This commit is contained in:
Tommaso Casaburi
2026-04-18 16:01:00 +07:00
51 changed files with 1296 additions and 119 deletions
+31
View File
@@ -0,0 +1,31 @@
import { useEffect, useRef } from 'react';
interface UseSuggestionFeedLoaderOptions {
currentFeedLength: number;
feedLength: number;
hasMore: boolean;
loadMore: () => Promise<void>;
requestKey: string;
shouldLoad: boolean;
}
const EMPTY_REQUEST_KEY = '';
export const useSuggestionFeedLoader = ({ currentFeedLength, feedLength, hasMore, loadMore, requestKey, shouldLoad }: UseSuggestionFeedLoaderOptions) => {
const lastRequestKeyRef = useRef(EMPTY_REQUEST_KEY);
useEffect(() => {
if (!shouldLoad || !requestKey || !hasMore || feedLength > currentFeedLength) {
lastRequestKeyRef.current = EMPTY_REQUEST_KEY;
return;
}
const nextRequestKey = `${requestKey}:${currentFeedLength}:${feedLength}`;
if (lastRequestKeyRef.current === nextRequestKey) {
return;
}
lastRequestKeyRef.current = nextRequestKey;
void loadMore();
}, [currentFeedLength, feedLength, hasMore, loadMore, requestKey, shouldLoad]);
};
+56
View File
@@ -0,0 +1,56 @@
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import {
getEffectiveTimeFilterName,
getEffectiveTimeFilterSeconds,
getStableLastVisitTimeFilterName,
getSelectedTimeFilterValue,
getTimeFilterOptionValues,
touchLastVisitTimestamp,
} from '../lib/utils/time-filter-utils';
declare global {
interface Window {
_5chanLastVisitTimestampIntervalId?: number;
_5chanLastVisitTimestampSubscribers?: number;
}
}
const ensureLastVisitTimestampTracking = (): (() => void) => {
if (typeof window === 'undefined') {
return () => {};
}
touchLastVisitTimestamp();
window._5chanLastVisitTimestampSubscribers = (window._5chanLastVisitTimestampSubscribers || 0) + 1;
if (!window._5chanLastVisitTimestampIntervalId) {
window._5chanLastVisitTimestampIntervalId = window.setInterval(() => {
touchLastVisitTimestamp();
}, 60 * 1000);
}
return () => {
window._5chanLastVisitTimestampSubscribers = Math.max((window._5chanLastVisitTimestampSubscribers || 1) - 1, 0);
if (window._5chanLastVisitTimestampSubscribers === 0 && window._5chanLastVisitTimestampIntervalId) {
window.clearInterval(window._5chanLastVisitTimestampIntervalId);
delete window._5chanLastVisitTimestampIntervalId;
}
};
};
const useTimeFilter = (timeFilterNameOverride?: string) => {
const location = useLocation();
useEffect(() => ensureLastVisitTimestampTracking(), []);
const lastVisitTimeFilterName = getStableLastVisitTimeFilterName();
const timeFilterValue = timeFilterNameOverride || getSelectedTimeFilterValue(location.search);
const timeFilterName = timeFilterNameOverride || getEffectiveTimeFilterName(location.search, lastVisitTimeFilterName);
const timeFilterSeconds = getEffectiveTimeFilterSeconds('', timeFilterName);
const timeFilterValues = getTimeFilterOptionValues(lastVisitTimeFilterName);
return { timeFilterSeconds, timeFilterName, timeFilterValue, timeFilterValues, lastVisitTimeFilterName };
};
export default useTimeFilter;