archive old guild-subscriptions stuff

This commit is contained in:
Michael Peters 2022-02-06 19:24:25 -06:00
parent fb0981e92b
commit ddd15972aa
9 changed files with 164 additions and 158 deletions

View File

@ -42,13 +42,13 @@ interface EffectParams<T> {
type Arguments<T> = T extends (...args: infer A) => unknown ? A : never;
// The result of a general subscription. Includes the value and associated guild
export interface SubscriptionResult<T> {
interface SubscriptionResult<T> {
value: T;
guild: CombinedGuild;
}
// The result of a scrolling subscription. Includes the state of each end of the list, the list, and the associated guild
export interface ScrollingSubscriptionResult<T> {
interface ScrollingSubscriptionResult<T> {
value: {
ends: { hasMoreAbove: boolean, hasMoreBelow: boolean };
elements: T[];
@ -57,7 +57,7 @@ export interface ScrollingSubscriptionResult<T> {
}
// Ensures that a nullable subscriptionResult has a value and has been fetched
export function isNonNullAndHasValue<T>(subscriptionResult: SubscriptionResult<T | null> | null): subscriptionResult is SubscriptionResult<T> {
function isNonNullAndHasValue<T>(subscriptionResult: SubscriptionResult<T | null> | null): subscriptionResult is SubscriptionResult<T> {
return !!(subscriptionResult !== null && subscriptionResult.value !== null);
}
@ -902,7 +902,7 @@ function useTokensSubscription(guild: CombinedGuild) {
* fetchBelowError: The error from fetching below
* ]
*/
export function useMessagesScrollingSubscription(guild: CombinedGuild, channel: Channel, channelGuild: CombinedGuild, scrollToBottomFunc: () => void) {
function useMessagesScrollingSubscription(guild: CombinedGuild, channel: Channel, channelGuild: CombinedGuild, scrollToBottomFunc: () => void) {
const maxFetchElements = Globals.MESSAGES_PER_REQUEST;
const maxElements = Globals.MAX_CURRENT_MESSAGES;
const fetchMessagesFunc = useCallback(async () => {

View File

@ -4,7 +4,7 @@ import CombinedGuild from '../../guild-combined';
import GuildsManager from "../../guilds-manager";
// Subscribes to changes in the list of guilds in the manager
export function useGuildListSubscription(guildsManager: GuildsManager): [ guilds: CombinedGuild[] ] {
function useGuildListSubscription(guildsManager: GuildsManager): [ guilds: CombinedGuild[] ] {
const [ refreshId, setRefreshId ] = useState<string>(uuid.v4());
const refresh = useCallback(() => {

View File

@ -0,0 +1,64 @@
import React, { Dispatch, FC, ReactNode, RefObject, SetStateAction } from 'react';
import { useColumnReverseInfiniteScroll } from '../require/react-helper';
import Retry from './retry';
export interface InfiniteScrollProps {
infiniteScrollElementRef: RefObject<HTMLDivElement>;
fetchRetryCallable: () => Promise<void>;
fetchAboveCallable: () => Promise<void>;
fetchBelowCallable: () => Promise<void>;
setScrollRatio: Dispatch<SetStateAction<number>>;
ends: { hasMoreAbove: boolean, hasMoreBelow: boolean } | null;
fetchError: unknown | null;
fetchAboveError: unknown | null;
fetchBelowError: unknown | null;
fetchErrorMessage: string;
fetchAboveErrorMessage: string;
fetchBelowErrorMessage: string;
children: ReactNode;
}
// Implements convenient features such as 'try again' components
const InfiniteScroll: FC<InfiniteScrollProps> = (props: InfiniteScrollProps) => {
const {
infiniteScrollElementRef,
fetchRetryCallable,
fetchAboveCallable,
fetchBelowCallable,
setScrollRatio,
ends,
fetchError,
fetchAboveError,
fetchBelowError,
fetchErrorMessage,
fetchAboveErrorMessage,
fetchBelowErrorMessage,
children
} = props;
const [
updateScrollCallable,
fetchAboveRetry,
fetchBelowRetry
] = useColumnReverseInfiniteScroll(
600,
ends,
fetchAboveCallable,
fetchBelowCallable,
setScrollRatio
);
return (
<div className="infinite-scroll-scroll-base" ref={infiniteScrollElementRef} onScroll={updateScrollCallable}>
<div className="infinite-scroll-elements">
<Retry error={fetchAboveError} text={fetchAboveErrorMessage} retryFunc={fetchAboveRetry} />
{children}
<Retry error={fetchError} text={fetchErrorMessage} retryFunc={fetchRetryCallable} />
<Retry error={fetchBelowError} text={fetchBelowErrorMessage} retryFunc={fetchBelowRetry} />
</div>
</div>
)
};
export default InfiniteScroll;

View File

@ -0,0 +1,66 @@
/** Creates useful state for an infinite scroll subscription */
export function useColumnReverseInfiniteScroll(
threshold: number,
ends: { hasMoreBelow: boolean, hasMoreAbove: boolean } | null,
loadMoreAbove: () => Promise<void>,
loadMoreBelow: () => Promise<void>,
setScrollRatio: Dispatch<SetStateAction<number>>
): [
updateCallable: (event: UIEvent<HTMLElement>) => void,
loadAboveRetry: () => Promise<void>,
loadBelowRetry: () => Promise<void>
] {
const isMounted = useIsMountedRef();
const [ loadingAbove, setLoadingAbove ] = useState<boolean>(false);
const [ loadingBelow, setLoadingBelow ] = useState<boolean>(false);
const loadAbove = useCallback(async () => {
if (loadingAbove) return;
if (!ends || !ends.hasMoreAbove) return;
setLoadingAbove(true);
await loadMoreAbove();
if (!isMounted.current) return;
setLoadingAbove(false);
}, [ loadingAbove, loadMoreAbove, ends ]);
const loadBelow = useCallback(async () => {
if (loadingBelow) return;
if (!ends || !ends.hasMoreBelow) return;
setLoadingBelow(true);
await loadMoreBelow();
if (!isMounted.current) return;
setLoadingBelow(false);
}, [ loadingBelow, loadMoreBelow, ends ]);
const onScrollCallable = useCallback(async (event: UIEvent<HTMLElement>) => {
const scrollTop = event.currentTarget.scrollTop;
const scrollHeight = event.currentTarget.scrollHeight;
const clientHeight = event.currentTarget.clientHeight;
// WARNING
// There's likely an inconsistency between browsers on this so have fun when you're working
// on the cross-platform implementation of this
// scrollTop apparantly is negative for column-reverse divs (this actually kindof makes sense if you flip your head upside down)
// have to reverse this
// I expect this was a change with some version of chromium.
// MDN documentation issue: https://github.com/mdn/content/issues/10968
setScrollRatio(Math.abs(scrollTop / scrollHeight));
const distToTop = -(clientHeight - scrollHeight - scrollTop); // keep in mind scrollTop is negative >:]
const distToBottom = -scrollTop;
//LOG.debug(`scroll callable update. to top: ${distToTop}, to bottom: ${distToBottom}`)
if (distToTop < threshold) {
await loadAbove();
}
if (distToBottom < threshold) {
await loadBelow();
}
}, [ setScrollRatio, loadAbove, loadBelow, threshold ]);
return [ onScrollCallable, loadAbove, loadBelow ];
}

View File

@ -1,36 +0,0 @@
import React, { ReactNode } from 'react';
import { LoadableValueScrolling } from "../require/loadables";
import { useScrollableCallables } from "../require/react-helper";
import Retry from './retry';
export interface InfiniteScrollRecoilProps<T, E> {
scrollable: LoadableValueScrolling<T, E>;
initialErrorMessage: string; // For if there was a problem loading the initial messages
aboveErrorMessage: string; // For if there was a problem loading messages above the list
belowErrorMessage: string; // For if there was a problem loading messages below the list
children: ReactNode;
}
function InfiniteScrollRecoil<T>(props: InfiniteScrollRecoilProps<T[], T>) {
const { scrollable, initialErrorMessage, aboveErrorMessage, belowErrorMessage, children } = props;
const {
fetchAboveCallable,
fetchBelowCallable,
onScrollCallable
} = useScrollableCallables(scrollable, 600); // Activate fetch above/below when 600 client px from the top/bottom
return (
<div className="infinite-scroll-scroll-base" onScroll={onScrollCallable}>
<div className="infinite-scroll-elements">
<Retry error={scrollable?.above?.error} text={aboveErrorMessage} retryFunc={fetchAboveCallable} />
{children}
<Retry error={scrollable.error} text={initialErrorMessage} retryFunc={scrollable.retry ?? (async () => {/* do nothing if we are unloaded */})} />
<Retry error={scrollable?.below?.error} text={belowErrorMessage} retryFunc={fetchBelowCallable} />
</div>
</div>
);
}
export default InfiniteScrollRecoil;

View File

@ -1,64 +1,36 @@
import React, { Dispatch, FC, ReactNode, RefObject, SetStateAction } from 'react';
import { useColumnReverseInfiniteScroll } from '../require/react-helper';
import React, { ReactNode } from 'react';
import { LoadableValueScrolling } from "../require/loadables";
import { useScrollableCallables } from "../require/react-helper";
import Retry from './retry';
export interface InfiniteScrollProps {
infiniteScrollElementRef: RefObject<HTMLDivElement>;
fetchRetryCallable: () => Promise<void>;
fetchAboveCallable: () => Promise<void>;
fetchBelowCallable: () => Promise<void>;
setScrollRatio: Dispatch<SetStateAction<number>>;
ends: { hasMoreAbove: boolean, hasMoreBelow: boolean } | null;
fetchError: unknown | null;
fetchAboveError: unknown | null;
fetchBelowError: unknown | null;
fetchErrorMessage: string;
fetchAboveErrorMessage: string;
fetchBelowErrorMessage: string;
export interface InfiniteScrollRecoilProps<T, E> {
scrollable: LoadableValueScrolling<T, E>;
initialErrorMessage: string; // For if there was a problem loading the initial messages
aboveErrorMessage: string; // For if there was a problem loading messages above the list
belowErrorMessage: string; // For if there was a problem loading messages below the list
children: ReactNode;
}
// Implements convenient features such as 'try again' components
const InfiniteScroll: FC<InfiniteScrollProps> = (props: InfiniteScrollProps) => {
function InfiniteScrollRecoil<T>(props: InfiniteScrollRecoilProps<T[], T>) {
const { scrollable, initialErrorMessage, aboveErrorMessage, belowErrorMessage, children } = props;
const {
infiniteScrollElementRef,
fetchRetryCallable,
fetchAboveCallable,
fetchBelowCallable,
setScrollRatio,
ends,
fetchError,
fetchAboveError,
fetchBelowError,
fetchErrorMessage,
fetchAboveErrorMessage,
fetchBelowErrorMessage,
children
} = props;
const [
updateScrollCallable,
fetchAboveRetry,
fetchBelowRetry
] = useColumnReverseInfiniteScroll(
600,
ends,
fetchAboveCallable,
fetchBelowCallable,
setScrollRatio
);
onScrollCallable
} = useScrollableCallables(scrollable, 600); // Activate fetch above/below when 600 client px from the top/bottom
return (
<div className="infinite-scroll-scroll-base" ref={infiniteScrollElementRef} onScroll={updateScrollCallable}>
<div className="infinite-scroll-scroll-base" onScroll={onScrollCallable}>
<div className="infinite-scroll-elements">
<Retry error={fetchAboveError} text={fetchAboveErrorMessage} retryFunc={fetchAboveRetry} />
<Retry error={scrollable?.above?.error} text={aboveErrorMessage} retryFunc={fetchAboveCallable} />
{children}
<Retry error={fetchError} text={fetchErrorMessage} retryFunc={fetchRetryCallable} />
<Retry error={fetchBelowError} text={fetchBelowErrorMessage} retryFunc={fetchBelowRetry} />
<Retry error={scrollable.error} text={initialErrorMessage} retryFunc={scrollable.retry ?? (async () => {/* do nothing if we are unloaded */})} />
<Retry error={scrollable?.below?.error} text={belowErrorMessage} retryFunc={fetchBelowCallable} />
</div>
</div>
)
};
);
}
export default InfiniteScroll;
export default InfiniteScrollRecoil;

View File

@ -9,7 +9,7 @@ import MessageElement from './components/message-element';
import { currGuildActiveChannelMessagesState, currGuildState } from '../require/atoms';
import { useRecoilValue } from 'recoil';
import { isFailed, isLoaded, isPended, isUnload } from '../require/loadables';
import InfiniteScrollRecoil from '../components/infinite-scroll-recoil';
import InfiniteScrollRecoil from '../components/infinite-scroll';
const MessageList: FC = () => {
// const scrollToBottomFunc = useCallback(() => {
@ -19,6 +19,11 @@ const MessageList: FC = () => {
const guild = useRecoilValue(currGuildState);
const messages = useRecoilValue(currGuildActiveChannelMessagesState);
// TODO: Show loading indicators if above/below are pending
// TODO: Show nicer retry buttons (and test the retry buttons)
// Leaving this in for debugging purposes
useEffect(() => {
if (isUnload(messages)) {
LOG.debug('recoilMessages unloaded');

View File

@ -255,73 +255,8 @@ export function useAsyncSubmitButton<ResultType>(
return [ callable, text, shaking, result, errorMessage ];
}
/** Creates useful state for an infinite scroll subscription */
export function useColumnReverseInfiniteScroll(
threshold: number,
ends: { hasMoreBelow: boolean, hasMoreAbove: boolean } | null,
loadMoreAbove: () => Promise<void>,
loadMoreBelow: () => Promise<void>,
setScrollRatio: Dispatch<SetStateAction<number>>
): [
updateCallable: (event: UIEvent<HTMLElement>) => void,
loadAboveRetry: () => Promise<void>,
loadBelowRetry: () => Promise<void>
] {
const isMounted = useIsMountedRef();
const [ loadingAbove, setLoadingAbove ] = useState<boolean>(false);
const [ loadingBelow, setLoadingBelow ] = useState<boolean>(false);
const loadAbove = useCallback(async () => {
if (loadingAbove) return;
if (!ends || !ends.hasMoreAbove) return;
setLoadingAbove(true);
await loadMoreAbove();
if (!isMounted.current) return;
setLoadingAbove(false);
}, [ loadingAbove, loadMoreAbove, ends ]);
const loadBelow = useCallback(async () => {
if (loadingBelow) return;
if (!ends || !ends.hasMoreBelow) return;
setLoadingBelow(true);
await loadMoreBelow();
if (!isMounted.current) return;
setLoadingBelow(false);
}, [ loadingBelow, loadMoreBelow, ends ]);
const onScrollCallable = useCallback(async (event: UIEvent<HTMLElement>) => {
const scrollTop = event.currentTarget.scrollTop;
const scrollHeight = event.currentTarget.scrollHeight;
const clientHeight = event.currentTarget.clientHeight;
// WARNING
// There's likely an inconsistency between browsers on this so have fun when you're working
// on the cross-platform implementation of this
// scrollTop apparantly is negative for column-reverse divs (this actually kindof makes sense if you flip your head upside down)
// have to reverse this
// I expect this was a change with some version of chromium.
// MDN documentation issue: https://github.com/mdn/content/issues/10968
setScrollRatio(Math.abs(scrollTop / scrollHeight));
const distToTop = -(clientHeight - scrollHeight - scrollTop); // keep in mind scrollTop is negative >:]
const distToBottom = -scrollTop;
//LOG.debug(`scroll callable update. to top: ${distToTop}, to bottom: ${distToBottom}`)
if (distToTop < threshold) {
await loadAbove();
}
if (distToBottom < threshold) {
await loadBelow();
}
}, [ setScrollRatio, loadAbove, loadBelow, threshold ]);
return [ onScrollCallable, loadAbove, loadBelow ];
}
// Returns functions that can be used to fetch the elements above the top element / below the bottom element
// NOTE: This is for a column-reverse element
export function useScrollableCallables<T>(scrollable: LoadableValueScrolling<T[], T>, threshold: number): {
fetchAboveCallable: () => Promise<void>, // these fetch functions are returned for use in retry buttons
fetchBelowCallable: () => Promise<void>,
@ -378,7 +313,7 @@ export function useScrollableCallables<T>(scrollable: LoadableValueScrolling<T[]
const distToTop = -(clientHeight - scrollHeight - scrollTop); // keep in mind scrollTop is negative >:]
const distToBottom = -scrollTop;
LOG.debug(`scroll callable update. to top: ${distToTop}, to bottom: ${distToBottom}`)
//LOG.debug(`scroll callable update. to top: ${distToTop}, to bottom: ${distToBottom}`)
if (distToTop < threshold) {
await fetchAboveCallable();