add infinite scroll "jump to bottom" ui components
This commit is contained in:
parent
f60831454f
commit
29b7c47a7e
@ -0,0 +1,31 @@
|
||||
@use '../../styles/theme.scss';
|
||||
|
||||
.jump-to-bottom-wrapper {
|
||||
position: relative;
|
||||
|
||||
.jump-to-bottom {
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
|
||||
font-size: 0.85em;
|
||||
|
||||
left: 0;
|
||||
bottom: 52px;
|
||||
|
||||
padding: 4px 8px 16px 8px;
|
||||
margin-left: 16px;
|
||||
width: calc(100% - 32px);
|
||||
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
border-radius: 8px;
|
||||
color: theme.$header-primary;
|
||||
background-color: theme.$background-input-alt;
|
||||
|
||||
.jump {
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import React, { MutableRefObject, ReactNode } from 'react';
|
||||
import React, { useMemo, MutableRefObject, ReactNode } from 'react';
|
||||
|
||||
import { LoadableValueScrolling } from '../require/loadables';
|
||||
import { useScrollableCallables } from '../require/react-helper';
|
||||
@ -18,15 +18,33 @@ function InfiniteScrollRecoil<T>(props: InfiniteScrollRecoilProps<T[], T>) {
|
||||
|
||||
const { fetchAboveCallable, fetchBelowCallable, onScrollCallable } = useScrollableCallables(scrollable, 600); // activate fetch above/below when 600 client px from the top/bottom
|
||||
|
||||
const jumpToBottom = useMemo(() => {
|
||||
if (!scrollable.below?.hasMore) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className="jump-to-bottom-wrapper">
|
||||
<div className="jump-to-bottom">
|
||||
<div className="text">You are viewing older messages</div>
|
||||
<div className="jump" onClick={async () => { console.log('jump to bottom'); }}>
|
||||
Jump to Bottom
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}, [scrollable]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div ref={infiniteScrollRef} className="infinite-scroll-scroll-base" onScroll={onScrollCallable}>
|
||||
<div className="infinite-scroll-elements">
|
||||
<Retry error={scrollable?.above?.error} text={aboveErrorMessage} retryFunc={fetchAboveCallable} />
|
||||
<Retry error={scrollable.above?.error} text={aboveErrorMessage} retryFunc={fetchAboveCallable} />
|
||||
{children}
|
||||
<Retry error={scrollable?.below?.error} text={belowErrorMessage} retryFunc={fetchBelowCallable} />
|
||||
<Retry error={scrollable.below?.error} text={belowErrorMessage} retryFunc={fetchBelowCallable} />
|
||||
<Retry
|
||||
error={scrollable.error}
|
||||
text={initialErrorMessage}
|
||||
// TODO: Allow null instead of noop func to prevent re-renders
|
||||
retryFunc={
|
||||
scrollable.retry ??
|
||||
(async () => {
|
||||
@ -36,6 +54,8 @@ function InfiniteScrollRecoil<T>(props: InfiniteScrollRecoilProps<T[], T>) {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{jumpToBottom}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -343,7 +343,7 @@ export function useScrollableCallables<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 && isLoaded(scrollable) && !isEndFailed(scrollable.above)) {
|
||||
await fetchAboveCallable();
|
||||
|
@ -392,7 +392,7 @@ export default class CombinedGuild
|
||||
return messages;
|
||||
}
|
||||
async fetchMessagesAfter(channelId: string, messageOrderId: string, number: number): Promise<Message[]> {
|
||||
Util.failSometimes(0.75); // for testing
|
||||
// xUtil.failSometimes(0.05); // for testing
|
||||
LOG.debug(`g#${this.id}: fetch messages after ch#${channelId.slice(0, 4)}, mo#${messageOrderId}, ${number}`);
|
||||
const members = await this.grabRAMMembersMap();
|
||||
const channels = await this.grabRAMChannelsMap();
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
@use 'elements-styles/components/buttons.scss';
|
||||
@use 'elements-styles/components/file-drop-target.scss';
|
||||
@use 'elements-styles/components/infinite-scroll.scss';
|
||||
@use 'elements-styles/components/input-dropdown.scss';
|
||||
@use 'elements-styles/components/input-image-edit.scss';
|
||||
@use 'elements-styles/components/input-text.scss';
|
||||
|
@ -29,6 +29,7 @@ $background-popup-message: rgba(30, 31, 34, 0.75);
|
||||
$background-primary-divider: #3f4149;
|
||||
|
||||
$background-input: #2f3136;
|
||||
$background-input-alt: #646872;
|
||||
$border-input: #1d1e22;
|
||||
$border-input-hover: #0b0c0e;
|
||||
$border-input-focus: #0099ff;
|
||||
|
@ -16,8 +16,12 @@ process.on('unhandledRejection', async (reason, _promise) => {
|
||||
const targetGuild = guilds.find(guild => guild.name === 'no chicoms');
|
||||
const members = await DB.getMembers(targetGuild.id);
|
||||
const targetMember = members.find(member => member.display_name === 'Elipzer');
|
||||
if (targetMember === undefined) {
|
||||
LOG.error('unable to find target member');
|
||||
}
|
||||
const channels = await DB.getChannels(targetGuild.id);
|
||||
const targetChannel = channels.find(channel => channel.name === 'memes');
|
||||
LOG.debug('inserting testing messages...');
|
||||
for (let i = 0; i < 2000; ++i) {
|
||||
await DB.insertMessage(targetGuild.id, targetChannel.id, targetMember.id, 'Test Message #' + i);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user