Fix timeline scrolling automatically while emoji picker is open
This commit is contained in:
parent
01231eb1c6
commit
f115ba5873
5 changed files with 150 additions and 11 deletions
87
ts/hooks/useScrollLock.tsx
Normal file
87
ts/hooks/useScrollLock.tsx
Normal file
|
@ -0,0 +1,87 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { useContext, createContext, useEffect, useRef } from 'react';
|
||||
import * as log from '../logging/log';
|
||||
|
||||
type ScrollerLock = Readonly<{
|
||||
isLocked(): boolean;
|
||||
lock(reason: string, onUserInterrupt: () => void): () => void;
|
||||
onUserInterrupt(reason: string): void;
|
||||
}>;
|
||||
|
||||
export function createScrollerLock(
|
||||
title: string,
|
||||
onUpdate: () => void
|
||||
): ScrollerLock {
|
||||
const locks = new Set<() => void>();
|
||||
|
||||
let lastUpdate: boolean | null = null;
|
||||
function update() {
|
||||
const isLocked = locks.size > 0;
|
||||
if (isLocked !== lastUpdate) {
|
||||
lastUpdate = isLocked;
|
||||
onUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isLocked() {
|
||||
return locks.size > 0;
|
||||
},
|
||||
lock(reason, onUserInterrupt) {
|
||||
log.info('ScrollerLock: Locking', title, reason);
|
||||
locks.add(onUserInterrupt);
|
||||
update();
|
||||
function release() {
|
||||
log.info('ScrollerLock: Releasing', title, reason);
|
||||
locks.delete(onUserInterrupt);
|
||||
update();
|
||||
}
|
||||
return release;
|
||||
},
|
||||
onUserInterrupt(reason) {
|
||||
// Ignore interuptions if we're not locked
|
||||
if (locks.size > 0) {
|
||||
log.info('ScrollerLock: User Interrupt', title, reason);
|
||||
locks.forEach(listener => listener());
|
||||
locks.clear();
|
||||
update();
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const ScrollerLockContext = createContext<ScrollerLock | null>(null);
|
||||
|
||||
export type ScrollLockProps = Readonly<{
|
||||
reason: string;
|
||||
lockScrollWhen: boolean;
|
||||
onUserInterrupt(): void;
|
||||
}>;
|
||||
|
||||
export function useScrollerLock({
|
||||
reason,
|
||||
lockScrollWhen,
|
||||
onUserInterrupt,
|
||||
}: ScrollLockProps): void {
|
||||
const scrollerLock = useContext(ScrollerLockContext);
|
||||
|
||||
if (scrollerLock == null) {
|
||||
throw new Error('Missing <ScrollLockProvider/>');
|
||||
}
|
||||
|
||||
const onUserInterruptRef = useRef(onUserInterrupt);
|
||||
useEffect(() => {
|
||||
onUserInterruptRef.current = onUserInterrupt;
|
||||
}, [onUserInterrupt]);
|
||||
|
||||
useEffect(() => {
|
||||
if (lockScrollWhen) {
|
||||
return scrollerLock.lock(reason, () => {
|
||||
onUserInterruptRef.current();
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
}, [reason, scrollerLock, lockScrollWhen, onUserInterrupt]);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue