fix: Improve autoscroll to bottom
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
import { resolveAutoScrollBehavior } from './auto-scroll.rules';
|
||||
|
||||
describe('resolveAutoScrollBehavior', () => {
|
||||
const base = {
|
||||
newMessages: true,
|
||||
forceLocalSend: false,
|
||||
distanceFromBottom: 0,
|
||||
withinInitialGrace: false
|
||||
};
|
||||
|
||||
it('does nothing when no new messages arrived', () => {
|
||||
expect(resolveAutoScrollBehavior({ ...base, newMessages: false })).toBe('none');
|
||||
});
|
||||
|
||||
it('jumps instantly for the local user own send regardless of grace', () => {
|
||||
expect(resolveAutoScrollBehavior({ ...base, forceLocalSend: true })).toBe('instant');
|
||||
expect(
|
||||
resolveAutoScrollBehavior({ ...base, forceLocalSend: true, withinInitialGrace: true })
|
||||
).toBe('instant');
|
||||
});
|
||||
|
||||
it('jumps instantly when near bottom while settling after a channel switch', () => {
|
||||
expect(
|
||||
resolveAutoScrollBehavior({ ...base, distanceFromBottom: 40, withinInitialGrace: true })
|
||||
).toBe('instant');
|
||||
});
|
||||
|
||||
it('animates smoothly for live messages once settled and near bottom', () => {
|
||||
expect(
|
||||
resolveAutoScrollBehavior({ ...base, distanceFromBottom: 40, withinInitialGrace: false })
|
||||
).toBe('smooth');
|
||||
});
|
||||
|
||||
it('shows the indicator (no scroll) when far from the bottom', () => {
|
||||
expect(resolveAutoScrollBehavior({ ...base, distanceFromBottom: 800 })).toBe('none');
|
||||
expect(
|
||||
resolveAutoScrollBehavior({ ...base, distanceFromBottom: 800, withinInitialGrace: true })
|
||||
).toBe('none');
|
||||
});
|
||||
|
||||
it('honours a custom sticky threshold', () => {
|
||||
expect(
|
||||
resolveAutoScrollBehavior({ ...base, distanceFromBottom: 150, stickyThreshold: 100 })
|
||||
).toBe('none');
|
||||
|
||||
expect(
|
||||
resolveAutoScrollBehavior({ ...base, distanceFromBottom: 80, stickyThreshold: 100 })
|
||||
).toBe('smooth');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
/** Behaviour the message list should apply when new messages arrive. */
|
||||
export type AutoScrollBehavior = 'smooth' | 'instant' | 'none';
|
||||
|
||||
export interface AutoScrollDecisionInput {
|
||||
/** Whether the message count grew since the last render. */
|
||||
readonly newMessages: boolean;
|
||||
/** Forced jump for the local user's own just-sent message. */
|
||||
readonly forceLocalSend: boolean;
|
||||
/** Pixels from the bottom of the scroll container. */
|
||||
readonly distanceFromBottom: number;
|
||||
/**
|
||||
* True while the conversation is still settling after a channel/server
|
||||
* switch (initial scroll + async embed/image height changes). During this
|
||||
* window we jump instantly so we never animate to the wrong position.
|
||||
*/
|
||||
readonly withinInitialGrace: boolean;
|
||||
/** Distance threshold under which the list is considered stuck to bottom. */
|
||||
readonly stickyThreshold?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides how the message list should react when the message count changes.
|
||||
*
|
||||
* - No new messages -> `none`.
|
||||
* - Local user's own send -> `instant` (never animate your own message).
|
||||
* - Near bottom during the post-switch settle window -> `instant` (so loading
|
||||
* a channel always lands at the bottom without a mid-chat smooth animation).
|
||||
* - Near bottom afterwards -> `smooth` (live messages animate into view).
|
||||
* - Far from bottom -> `none` (show the new-messages indicator instead).
|
||||
*/
|
||||
export function resolveAutoScrollBehavior(input: AutoScrollDecisionInput): AutoScrollBehavior {
|
||||
if (!input.newMessages)
|
||||
return 'none';
|
||||
|
||||
if (input.forceLocalSend)
|
||||
return 'instant';
|
||||
|
||||
const threshold = input.stickyThreshold ?? 300;
|
||||
|
||||
if (input.distanceFromBottom > threshold)
|
||||
return 'none';
|
||||
|
||||
return input.withinInitialGrace ? 'instant' : 'smooth';
|
||||
}
|
||||
Reference in New Issue
Block a user