Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Jalson1982/a47e6e3f75cec042b25bbd625135ba6e to your computer and use it in GitHub Desktop.
Save Jalson1982/a47e6e3f75cec042b25bbd625135ba6e to your computer and use it in GitHub Desktop.
import React, {useCallback, useEffect} from 'react';
import {
Text,
View,
StyleSheet,
TouchableOpacity,
ActivityIndicator,
ImageBackground,
GestureResponderEvent,
useWindowDimensions,
} from 'react-native';
import {hp, wp} from '@theme/responsiveHelpers';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import {darkGray, gray2, gray3, gray4, primaryBlue, white} from '@theme/colors';
import {moderateScale} from '@utilities/helpers/sizeHelpers';
import useTranslations from '@translations/useTranslation';
import useConnect from './useConnect';
import TableIcon from '@assets/svg/table-icon.svg';
import Animated, {
useAnimatedStyle,
useSharedValue,
interpolateColor,
withTiming,
useAnimatedRef,
scrollTo,
interpolate,
useWorkletCallback,
useAnimatedScrollHandler,
runOnJS,
SharedValue,
Easing,
} from 'react-native-reanimated';
import {useResizeMode} from 'react-native-keyboard-controller';
import {useSmoothKeyboardHandler} from '@components/keyboard-aware-scrollview/useSmoothKeyboardHandler';
import TableUsers from './components/TableUsers';
import MyUserCard from './components/MyUserCard';
import ColorPicker from './components/ColorPicker';
import ImagePicker from './components/ImagePicker';
import TableDetails from './components/TabelDetails';
import AnimatedContainerWithShadow from '@components/containerWithShadow/animatedContainerWithShadow';
const backIcon = require('../../assets/backArrow.png');
let scrollAmount = 0;
const SCROLL_TRASHOLD = 100;
const BOTTOM_OFFSET = 50;
const ANIMATION_DURATION = 200;
const DURATION = 300;
const BOTTOM_SHADOW_HEIGHT = 20;
const TableSettings = () => {
const {top, bottom} = useSafeAreaInsets();
const {translate} = useTranslations();
const {
hasChanges,
imageSource,
isSaving,
onUpdateTable,
goBack,
onCancel,
currentTable,
isTableOwner,
setCurrentTable,
selectedColor,
} = useConnect();
useResizeMode();
const [hasScrollEnough, setHasScrollEnough] = React.useState(false);
const [headerHeight, setHeaderHeight] = React.useState(0);
const opacity = useSharedValue(0);
const shadowOpacity = useSharedValue(0);
const zIndex = useSharedValue(1);
const zIndexBlack = useSharedValue(0);
const scrollViewAnimatedRef = useAnimatedRef<Animated.ScrollView>();
const scrollPosition = useSharedValue(0);
const click = useSharedValue(0);
const position = useSharedValue(0);
const fakeViewHeight = useSharedValue(0);
const keyboardHeight = useSharedValue(0);
const {height} = useWindowDimensions();
const updateScrollAmount = (e: number) => {
scrollAmount = e;
if (e < SCROLL_TRASHOLD) {
if (hasScrollEnough) {
setHasScrollEnough(false);
}
}
if (e > SCROLL_TRASHOLD) {
if (!hasScrollEnough) {
setHasScrollEnough(true);
}
}
};
const onScroll = useAnimatedScrollHandler(
{
onScroll: e => {
position.value = e.contentOffset.y;
runOnJS(updateScrollAmount)(e.contentOffset.y);
if (!hasChanges) {
opacity.value = e.contentOffset.y / SCROLL_TRASHOLD;
zIndexBlack.value = e.contentOffset.y / SCROLL_TRASHOLD;
zIndex.value = 1 - e.contentOffset.y / SCROLL_TRASHOLD;
}
},
},
[hasChanges],
);
const onContentTouch = useCallback(
(e: GestureResponderEvent) => {
if (keyboardHeight.value === 0) {
click.value = e.nativeEvent.pageY;
scrollPosition.value = position.value;
}
},
[click, keyboardHeight, position, scrollPosition],
);
const maybeScroll = useWorkletCallback((e: number) => {
'worklet';
fakeViewHeight.value = e;
const visibleRect = height - keyboardHeight.value;
if (visibleRect - click.value <= BOTTOM_OFFSET) {
const interpolatedScrollTo = interpolate(
e,
[0, keyboardHeight.value],
[0, keyboardHeight.value - (height - click.value) + BOTTOM_OFFSET],
);
const targetScrollY =
Math.max(interpolatedScrollTo, 0) + scrollPosition.value;
scrollTo(scrollViewAnimatedRef, 0, targetScrollY, false);
}
}, []);
useSmoothKeyboardHandler(
{
onStart: e => {
'worklet';
if (e.height > 0) {
keyboardHeight.value = e.height;
}
},
onMove: e => {
'worklet';
maybeScroll(e.height);
},
onEnd: e => {
'worklet';
keyboardHeight.value = e.height;
},
},
[height],
);
const view = useAnimatedStyle(
() => ({
height: fakeViewHeight.value,
}),
[],
);
const stylez = useAnimatedStyle(() => {
const backgroundColor = interpolateColor(
opacity.value,
[0, 1],
['transparent', white],
);
return {
backgroundColor,
};
});
const textStylez = useAnimatedStyle(() => {
const color = interpolateColor(opacity.value, [0, 1], [white, darkGray]);
return {
color,
};
});
const backIconStylez = useAnimatedStyle(() => {
const color = interpolateColor(opacity.value, [0, 1], [white, darkGray]);
return {
tintColor: color,
};
});
const applyChanges = useCallback(() => {
setWithTiming(opacity, 1);
setWithTiming(zIndexBlack, 1);
setWithTiming(zIndex, 0);
}, [opacity, zIndex, zIndexBlack]);
const adjustVisibilityBasedOnScroll = useCallback(() => {
const normalizedScroll = scrollAmount / SCROLL_TRASHOLD;
setWithTiming(opacity, normalizedScroll);
setWithTiming(zIndexBlack, normalizedScroll);
setWithTiming(zIndex, 1 - normalizedScroll);
}, [opacity, zIndex, zIndexBlack]);
const setWithTiming = (value: SharedValue<number>, target: number): void => {
value.value = withTiming(target, {
duration: ANIMATION_DURATION,
easing: Easing.linear,
});
};
useEffect(() => {
if (hasChanges) {
applyChanges();
} else if (!hasChanges && hasScrollEnough) {
adjustVisibilityBasedOnScroll();
} else if (!hasChanges) {
adjustVisibilityBasedOnScroll();
}
}, [
hasChanges,
opacity,
zIndex,
zIndexBlack,
hasScrollEnough,
applyChanges,
adjustVisibilityBasedOnScroll,
]);
useEffect(() => {
return () => {
scrollAmount = 0;
};
}, []);
return (
<View style={styles.container}>
<Animated.View style={[styles.safeArea, {height: top}, stylez]} />
<Animated.View
style={[styles.header, {top}, stylez]}
onLayout={e => {
setHeaderHeight(e.nativeEvent.layout.height);
}}>
{hasChanges ? (
<TouchableOpacity
disabled={isSaving}
onPress={onCancel}
style={styles.backContainer}>
<Text style={styles.cancel}>
{translate('cancel_common_button_title')}
</Text>
</TouchableOpacity>
) : (
<TouchableOpacity onPress={goBack} style={styles.backContainer}>
<Animated.Image source={backIcon} style={backIconStylez} />
<Animated.Text style={[styles.backText, textStylez]}>
{translate('common_back')}
</Animated.Text>
</TouchableOpacity>
)}
<View style={styles.titleContainer}>
<Animated.Text style={[styles.headerTitle, textStylez]}>
{translate('table_settings_label')}
</Animated.Text>
</View>
<View style={styles.emptyContainer}>
{hasChanges && (
<>
{!isSaving ? (
<TouchableOpacity onPress={onUpdateTable}>
<Text style={styles.saveLabel}>
{translate('common_save')}
</Text>
</TouchableOpacity>
) : (
<ActivityIndicator />
)}
</>
)}
</View>
</Animated.View>
<AnimatedContainerWithShadow
opacity={shadowOpacity}
bottomStyle={{height: bottom + BOTTOM_SHADOW_HEIGHT}}
topStyle={{top: top + headerHeight}}>
<Animated.ScrollView
ref={scrollViewAnimatedRef}
onScroll={onScroll}
bounces={false}
onMomentumScrollBegin={() =>
(shadowOpacity.value = withTiming(1, {
duration: DURATION,
easing: Easing.linear,
}))
}
onMomentumScrollEnd={() =>
(shadowOpacity.value = withTiming(0, {
duration: DURATION,
easing: Easing.linear,
}))
}
onTouchStart={onContentTouch}
scrollEventThrottle={16}
contentContainerStyle={styles.contentContainer}>
<ImageBackground
source={imageSource}
style={styles.backgroundImage}
resizeMode="cover">
<TableIcon style={styles.tableIcon} />
</ImageBackground>
<TableDetails
currentTable={currentTable}
setCurrentTable={setCurrentTable}
isTableOwner={isTableOwner}
/>
<MyUserCard />
<View style={styles.spacer10} />
<TableUsers />
<ColorPicker
currentTable={currentTable}
selectedColor={selectedColor}
isTableOwner={isTableOwner}
setCurrentTable={setCurrentTable}
/>
<ImagePicker
currentTable={currentTable}
setCurrentTable={setCurrentTable}
isTableOwner={isTableOwner}
/>
<View style={styles.spacer10} />
<View style={styles.spacer10} />
<Animated.View style={view} />
</Animated.ScrollView>
</AnimatedContainerWithShadow>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: gray3,
},
contentContainer: {
paddingBottom: 100,
},
header: {
position: 'absolute',
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 20,
height: 44,
justifyContent: 'space-between',
width: wp(100),
zIndex: 100,
},
safeArea: {
backgroundColor: white,
width: wp(100),
position: 'absolute',
zIndex: 100,
},
backgroundImage: {
width: wp(100),
height: hp(35),
justifyContent: 'center',
alignItems: 'center',
},
backContainer: {
flexDirection: 'row',
alignItems: 'center',
flex: 0.3,
},
emptyContainer: {
flex: 0.3,
alignItems: 'flex-end',
},
titleContainer: {
flex: 1,
alignItems: 'center',
},
saveLabel: {
fontFamily: 'OpenSans-Bold',
fontSize: moderateScale(14),
fontWeight: '700',
color: primaryBlue,
},
backText: {
fontSize: moderateScale(14),
fontFamily: 'OpenSans-Regular',
marginLeft: 10,
},
headerTitle: {
fontSize: moderateScale(16),
fontFamily: 'OpenSans-Bold',
fontWeight: '700',
},
tableNameLabel: {
fontSize: moderateScale(11),
fontFamily: 'OpenSans-Regular',
color: darkGray,
},
content: {
flex: 1,
backgroundColor: white,
},
cancel: {
fontFamily: 'OpenSans-Regular',
fontSize: moderateScale(14),
fontWeight: '400',
color: primaryBlue,
},
tableTitle: {
fontFamily: 'OpenSans-SemiBold',
fontSize: moderateScale(21),
fontWeight: '600',
},
tableDescriptionLabel: {
fontSize: moderateScale(11),
fontFamily: 'OpenSans-Regular',
color: gray2,
},
descriptionContainer: {
marginTop: 10,
marginBottom: 10,
paddingHorizontal: 20,
backgroundColor: white,
},
descriptionInput: {
marginTop: 0,
},
descriptionInputNoValue: {
color: gray4,
fontSize: moderateScale(14),
fontFamily: 'OpenSans-Regular',
},
createdByLabel: {
color: gray4,
fontSize: moderateScale(11),
fontFamily: 'OpenSans-Regular',
},
detailsRow: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 20,
marginTop: 10,
backgroundColor: white,
},
detailsRowWithMargin: {
flexDirection: 'row',
alignItems: 'center',
marginTop: 8,
paddingHorizontal: 20,
backgroundColor: white,
},
detailsValue: {
fontSize: moderateScale(11),
fontFamily: 'OpenSans-Regular',
color: darkGray,
marginLeft: 10,
},
containerWithPadding: {
paddingHorizontal: 20,
paddingVertical: 15,
},
spacer10: {
height: 10,
},
divider: {
height: 1,
width: wp(100),
backgroundColor: gray3,
},
tableIcon: {position: 'absolute', bottom: 40},
backBlackIcon: {position: 'absolute'},
descriptionInputWithValue: {
color: darkGray,
fontSize: moderateScale(14),
fontFamily: 'OpenSans-Regular',
},
});
export default TableSettings;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment