mattermost-mobile/app/hooks/gallery.ts
2025-07-22 14:00:57 +08:00

125 lines
3.1 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {useCallback, useEffect} from 'react';
import {
Easing, runOnJS, useAnimatedRef, useAnimatedStyle,
useSharedValue,
withTiming, type WithTimingConfig,
} from 'react-native-reanimated';
import {useGallery} from '@context/gallery';
export function diff(context: any, name: string, value: any) {
'worklet';
if (!context.___diffs) {
context.___diffs = {};
}
if (!context.___diffs[name]) {
context.___diffs[name] = {
stash: 0,
prev: null,
};
}
const d = context.___diffs[name];
d.stash = d.prev === null ? 0 : value - d.prev;
d.prev = value;
return d.stash;
}
export const translateYConfig: WithTimingConfig = {
duration: 400,
easing: Easing.bezier(0.33, 0.01, 0, 1),
};
export function useGalleryControls() {
const headerAndFooterHidden = useSharedValue(false);
const headerStyles = useAnimatedStyle(() => ({
opacity: headerAndFooterHidden.value ? withTiming(0) : withTiming(1),
transform: [
{
translateY: headerAndFooterHidden.value ? withTiming(-100, translateYConfig) : withTiming(0, translateYConfig),
},
],
position: 'absolute',
top: 0,
width: '100%',
zIndex: 1,
}));
const footerStyles = useAnimatedStyle(() => ({
opacity: headerAndFooterHidden.value ? withTiming(0) : withTiming(1),
transform: [
{
translateY: headerAndFooterHidden.value ? withTiming(100, translateYConfig) : withTiming(0, translateYConfig),
},
],
position: 'absolute',
bottom: 0,
width: '100%',
zIndex: 1,
}));
const hideHeaderAndFooter = useCallback((hidden?: boolean) => {
'worklet';
if (hidden == null) {
// if we don't pass hidden, then we toggle the current value
headerAndFooterHidden.value = !headerAndFooterHidden.value;
return;
}
if (headerAndFooterHidden.value === hidden) {
return;
}
headerAndFooterHidden.value = hidden;
}, []);
return {
headerAndFooterHidden,
headerStyles,
footerStyles,
hideHeaderAndFooter,
};
}
export function useGalleryItem(
identifier: string,
index: number,
onPress: (identifier: string, itemIndex: number) => void,
) {
const gallery = useGallery(identifier);
const ref = useAnimatedRef<any>();
const {opacity, activeIndex} = gallery.sharedValues;
const styles = useAnimatedStyle(() => {
return {
opacity: activeIndex.value === index ? opacity.value : 1,
};
}, []);
useEffect(() => {
gallery.registerItem(index, ref);
}, []);
const onGestureEvent = () => {
'worklet';
activeIndex.value = index;
runOnJS(onPress)(identifier, index);
};
return {
ref,
styles,
onGestureEvent,
};
}