61 lines
1.8 KiB
TypeScript
61 lines
1.8 KiB
TypeScript
export type ThreadScrollAction = "jump" | "smooth" | null;
|
|
|
|
export type ThreadScrollState = {
|
|
detached: boolean;
|
|
unread: boolean;
|
|
};
|
|
|
|
export class ThreadScrollBehavior {
|
|
private threadKey: string | null = null;
|
|
private laidOut = false;
|
|
private contentReady = false;
|
|
private latestMessageId: string | null = null;
|
|
private currentState: ThreadScrollState = { detached: false, unread: false };
|
|
|
|
openThread(threadKey: string): void {
|
|
if (this.threadKey === threadKey) return;
|
|
this.threadKey = threadKey;
|
|
this.laidOut = false;
|
|
this.contentReady = false;
|
|
this.latestMessageId = null;
|
|
this.currentState = { detached: false, unread: false };
|
|
}
|
|
|
|
onLayout(): ThreadScrollAction {
|
|
this.laidOut = true;
|
|
return this.contentReady && !this.currentState.detached ? "jump" : null;
|
|
}
|
|
|
|
onContentChanged(blocked: boolean, latestMessageId: string | null): ThreadScrollAction {
|
|
if (blocked) return null;
|
|
this.contentReady = true;
|
|
if (latestMessageId === null || latestMessageId === this.latestMessageId) return null;
|
|
const initial = this.latestMessageId === null;
|
|
this.latestMessageId = latestMessageId;
|
|
if (initial) return this.laidOut ? "jump" : null;
|
|
if (this.currentState.detached) {
|
|
this.currentState = { detached: true, unread: true };
|
|
return null;
|
|
}
|
|
return "smooth";
|
|
}
|
|
|
|
onUserScroll(distanceFromEnd: number): ThreadScrollState {
|
|
const detached = distanceFromEnd > 80;
|
|
this.currentState = {
|
|
detached,
|
|
unread: detached ? this.currentState.unread : false,
|
|
};
|
|
return this.state();
|
|
}
|
|
|
|
jumpToLatest(): ThreadScrollAction {
|
|
this.currentState = { detached: false, unread: false };
|
|
return "smooth";
|
|
}
|
|
|
|
state(): ThreadScrollState {
|
|
return { ...this.currentState };
|
|
}
|
|
}
|