Jump messages to bottom when current user sent the message
This commit is contained in:
parent
ce9b17535b
commit
b97db81fa2
@ -1,8 +1,9 @@
|
||||
import React, { Dispatch, FC, ReactNode, SetStateAction } from 'react';
|
||||
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>;
|
||||
@ -20,6 +21,7 @@ export interface InfiniteScrollProps {
|
||||
// Implements convenient features such as 'try again' components
|
||||
const InfiniteScroll: FC<InfiniteScrollProps> = (props: InfiniteScrollProps) => {
|
||||
const {
|
||||
infiniteScrollElementRef,
|
||||
fetchRetryCallable,
|
||||
fetchAboveCallable,
|
||||
fetchBelowCallable,
|
||||
@ -47,7 +49,7 @@ const InfiniteScroll: FC<InfiniteScrollProps> = (props: InfiniteScrollProps) =>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="infinite-scroll-scroll-base" onScroll={updateScrollCallable}>
|
||||
<div className="infinite-scroll-scroll-base" ref={infiniteScrollElementRef} onScroll={updateScrollCallable}>
|
||||
<div className="infinite-scroll-elements">
|
||||
<Retry error={fetchAboveError} text={fetchAboveErrorMessage} retryFunc={fetchAboveRetry} />
|
||||
{children}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { Dispatch, FC, ReactNode, SetStateAction, useMemo } from 'react';
|
||||
import React, { Dispatch, FC, ReactNode, SetStateAction, useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import { Channel, Message } from '../../data-types';
|
||||
import CombinedGuild from '../../guild-combined';
|
||||
import MessageElement from './components/message-element';
|
||||
@ -9,12 +9,22 @@ interface MessageListProps {
|
||||
guild: CombinedGuild;
|
||||
channel: Channel;
|
||||
channelGuild: CombinedGuild;
|
||||
setFetchRetryCallable: Dispatch<SetStateAction<(() => Promise<void>) | null>>;
|
||||
setOverlay: Dispatch<SetStateAction<ReactNode>>;
|
||||
}
|
||||
|
||||
const MessageList: FC<MessageListProps> = (props: MessageListProps) => {
|
||||
const { guild, channel, channelGuild, setOverlay } = props;
|
||||
const { guild, channel, channelGuild, setFetchRetryCallable, setOverlay } = props;
|
||||
|
||||
const infiniteScrollElementRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const scrollToBottomFunc = useCallback(() => {
|
||||
if (!infiniteScrollElementRef.current) return;
|
||||
console.log('scrolling to bottom');
|
||||
infiniteScrollElementRef.current.scrollTop = 0; // Keep in mind that this is reversed for flex-flow: column-reverse
|
||||
}, []);
|
||||
|
||||
// TODO: Store state higher up
|
||||
const [
|
||||
fetchRetryCallable,
|
||||
fetchAboveCallable,
|
||||
@ -24,7 +34,12 @@ const MessageList: FC<MessageListProps> = (props: MessageListProps) => {
|
||||
messagesFetchError,
|
||||
messagesFetchAboveError,
|
||||
messagesFetchBelowError
|
||||
] = useMessagesScrollingSubscription(guild, channel, channelGuild);
|
||||
] = useMessagesScrollingSubscription(guild, channel, channelGuild, scrollToBottomFunc);
|
||||
|
||||
useEffect(() => {
|
||||
// Note: have to use a function here otherwise React trys to "help out" by running the promise
|
||||
setFetchRetryCallable(() => { return fetchRetryCallable });
|
||||
}, [ fetchRetryCallable ]);
|
||||
|
||||
const messageElements = useMemo(() => {
|
||||
const result = [];
|
||||
@ -41,6 +56,7 @@ const MessageList: FC<MessageListProps> = (props: MessageListProps) => {
|
||||
return (
|
||||
<div className="message-list">
|
||||
<InfiniteScroll
|
||||
infiniteScrollElementRef={infiniteScrollElementRef}
|
||||
fetchRetryCallable={fetchRetryCallable}
|
||||
fetchAboveCallable={fetchAboveCallable}
|
||||
fetchBelowCallable={fetchBelowCallable}
|
||||
|
@ -350,6 +350,7 @@ function useMultipleGuildSubscriptionScrolling<
|
||||
fetchFunc: () => Promise<T[] | null>,
|
||||
fetchAboveFunc: ((reference: T) => Promise<T[] | null>),
|
||||
fetchBelowFunc: ((reference: T) => Promise<T[] | null>),
|
||||
scrollToBottomFunc: () => void,
|
||||
): [
|
||||
fetchRetryCallable: () => Promise<void>,
|
||||
fetchAboveCallable: () => Promise<void>,
|
||||
@ -369,6 +370,8 @@ function useMultipleGuildSubscriptionScrolling<
|
||||
} = eventMappingParams;
|
||||
|
||||
const isMounted = useIsMountedRef();
|
||||
const guildRef = useRef<CombinedGuild>(guild);
|
||||
guildRef.current = guild;
|
||||
|
||||
// TODO: lastResult.value should really be only T[] instead of | null since we set it to [] anyway in the onUpdate, etc functions
|
||||
const [ lastResult, setLastResult ] = useState<ScrollingSubscriptionResult<T> | null>(null);
|
||||
@ -535,6 +538,7 @@ function useMultipleGuildSubscriptionScrolling<
|
||||
guild: fetchValueGuild
|
||||
});
|
||||
setFetchError(null);
|
||||
scrollToBottomFunc(); // Make sure that we scroll back to the bottom
|
||||
}, [ sortFunc, maxFetchElements, maxElements ]);
|
||||
|
||||
const onFetchError = useCallback((e: unknown) => {
|
||||
@ -799,7 +803,7 @@ export function useTokensSubscription(guild: CombinedGuild) {
|
||||
}, fetchTokensFunc);
|
||||
}
|
||||
|
||||
export function useMessagesScrollingSubscription(guild: CombinedGuild, channel: Channel, channelGuild: CombinedGuild) {
|
||||
export 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 () => {
|
||||
@ -829,7 +833,8 @@ export function useMessagesScrollingSubscription(guild: CombinedGuild, channel:
|
||||
sortFunc: Message.sortOrder
|
||||
},
|
||||
maxElements, maxFetchElements,
|
||||
fetchMessagesFunc, fetchAboveFunc, fetchBelowFunc
|
||||
)
|
||||
fetchMessagesFunc, fetchAboveFunc, fetchBelowFunc,
|
||||
scrollToBottomFunc
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3,8 +3,8 @@ const electronConsole = electronRemote.getGlobal('console') as Console;
|
||||
import Logger from '../../../../logger/logger';
|
||||
const LOG = Logger.create(__filename, electronConsole);
|
||||
|
||||
import React, { Dispatch, FC, ReactNode, SetStateAction, useEffect, useState } from 'react';
|
||||
import { Channel } from '../../data-types';
|
||||
import React, { Dispatch, FC, ReactNode, SetStateAction, useCallback, useEffect, useState } from 'react';
|
||||
import { Channel, Message } from '../../data-types';
|
||||
import CombinedGuild from '../../guild-combined';
|
||||
import ChannelList from '../lists/channel-list';
|
||||
import MemberList from '../lists/member-list';
|
||||
@ -36,6 +36,8 @@ const GuildElement: FC<GuildElementProps> = (props: GuildElementProps) => {
|
||||
const [ activeChannel, setActiveChannel ] = useState<Channel | null>(null);
|
||||
const [ activeChannelGuild, setActiveChannelGuild ] = useState<CombinedGuild | null>(null);
|
||||
|
||||
const [ fetchMessagesRetryCallable, setFetchMessagesRetryCallable ] = useState<(() => Promise<void>) | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setActiveChannel(null);
|
||||
}, [ guild ]);
|
||||
@ -57,6 +59,28 @@ const GuildElement: FC<GuildElementProps> = (props: GuildElementProps) => {
|
||||
}
|
||||
}, [ channelsResult, activeChannel ]);
|
||||
|
||||
// When the self member sends a message to the active channel and we get it as a new message,
|
||||
// move the active channel's messages back to the bottom
|
||||
const onNewMessage = useCallback((messages: Message[]) => {
|
||||
if (messages.find(message =>
|
||||
message.member.id === selfMemberResult?.value?.id &&
|
||||
message.channel.id === activeChannel?.id)
|
||||
) {
|
||||
if (fetchMessagesRetryCallable) {
|
||||
// TODO: Only do the fetch if we're not at the bottom. If we're at the bottom, just
|
||||
// set the scroll height
|
||||
fetchMessagesRetryCallable(); // Re-load the messages to move them to the bottom.
|
||||
}
|
||||
}
|
||||
}, [ fetchMessagesRetryCallable/*, Note: Removing these to prevent re-sending fetch. This technique should probably be changed in the future with react/redux selfMemberResult, activeChannel */ ]);
|
||||
|
||||
useEffect(() => {
|
||||
guild.on('new-messages', onNewMessage);
|
||||
return () => {
|
||||
guild.off('new-messages', onNewMessage);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="guild-react">
|
||||
<div className="guild-sidebar">
|
||||
@ -73,7 +97,7 @@ const GuildElement: FC<GuildElementProps> = (props: GuildElementProps) => {
|
||||
<ChannelTitle channel={activeChannel} />
|
||||
<div className="guild-channel-content">
|
||||
<div className="guild-channel-feed-wrapper">
|
||||
{activeChannel && activeChannelGuild && <MessageList guild={guild} channel={activeChannel} channelGuild={activeChannelGuild} setOverlay={setOverlay} />}
|
||||
{activeChannel && activeChannelGuild && <MessageList guild={guild} channel={activeChannel} channelGuild={activeChannelGuild} setFetchRetryCallable={setFetchMessagesRetryCallable} setOverlay={setOverlay} />}
|
||||
{activeChannel && activeChannelGuild && <SendMessage guild={guild} channel={activeChannel} />}
|
||||
</div>
|
||||
<div className="member-list-wrapper">
|
||||
|
@ -1,8 +1,3 @@
|
||||
import * as electronRemote from '@electron/remote';
|
||||
const electronConsole = electronRemote.getGlobal('console') as Console;
|
||||
import Logger from '../../../../logger/logger';
|
||||
const LOG = Logger.create(__filename, electronConsole);
|
||||
|
||||
import React, { ClipboardEvent, FC, FormEvent, KeyboardEvent, RefObject, useCallback, useMemo, useRef, useState } from 'react';
|
||||
import { Channel } from '../../data-types';
|
||||
import CombinedGuild from '../../guild-combined';
|
||||
@ -79,6 +74,7 @@ const SendMessage: FC<SendMessageProps> = (props: SendMessageProps) => {
|
||||
if (!isMounted.current) return;
|
||||
}
|
||||
|
||||
|
||||
setText('');
|
||||
if (contentEditableRef.current) contentEditableRef.current.innerText = '';
|
||||
setEnabled(true);
|
||||
|
@ -56,7 +56,7 @@ window.addEventListener('DOMContentLoaded', () => {
|
||||
const guildsManager = new GuildsManager(messageRAMCache, resourceRAMCache, personalDB);
|
||||
await guildsManager.init();
|
||||
|
||||
LOG.silly('events bound');
|
||||
LOG.silly('guildsManager initialized');
|
||||
|
||||
ReactDOM.render(<RootElement guildsManager={guildsManager} />, document.getElementById('react-root'));
|
||||
})();
|
||||
|
Loading…
Reference in New Issue
Block a user