UNPKG

@oxyhq/services

Version:

Reusable OxyHQ module to handle authentication, user management, karma system, device-based session management and more 🚀

246 lines (231 loc) • 8.31 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = _interopRequireWildcard(require("react")); var _reactNative = require("react-native"); var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated")); var _OxyContext = require("../context/OxyContext"); var _fonts = require("../styles/fonts"); var _sonner = require("../../lib/sonner"); var _jsxRuntime = require("react/jsx-runtime"); function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } /** * An animated follow button with interactive state changes and preventDefault support * * @example * ```tsx * // Basic usage * <FollowButton userId="123" /> * * // With custom styling * <FollowButton * userId="123" * initiallyFollowing={true} * size="large" * style={{ borderRadius: 12 }} * onFollowChange={(isFollowing) => console.log(`User is now ${isFollowing ? 'followed' : 'unfollowed'}`)} * /> * * // Inside a pressable container (prevents parent actions) * <TouchableOpacity onPress={() => navigateToProfile()}> * <View> * <Text>User Profile</Text> * <FollowButton * userId="123" * preventParentActions={true} // Default: true * /> * </View> * </TouchableOpacity> * * // Custom onPress handler * <FollowButton * userId="123" * onPress={(event) => { * event.preventDefault(); // Custom preventDefault * // Custom logic here * }} * /> * ``` */ const FollowButton = ({ userId, initiallyFollowing = false, size = 'medium', onFollowChange, style, textStyle, disabled = false, showLoadingState = true, preventParentActions = true, onPress }) => { const { oxyServices, isAuthenticated } = (0, _OxyContext.useOxy)(); const [isFollowing, setIsFollowing] = (0, _react.useState)(initiallyFollowing); const [isLoading, setIsLoading] = (0, _react.useState)(false); // Animation values const animationProgress = (0, _reactNativeReanimated.useSharedValue)(initiallyFollowing ? 1 : 0); const scale = (0, _reactNativeReanimated.useSharedValue)(1); // Update the animation value when isFollowing changes (0, _react.useEffect)(() => { animationProgress.value = (0, _reactNativeReanimated.withTiming)(isFollowing ? 1 : 0, { duration: 300, easing: _reactNativeReanimated.Easing.bezier(0.25, 0.1, 0.25, 1) }); }, [isFollowing, animationProgress]); // The button press handler with preventDefault support const handlePress = async event => { // Prevent parent actions if enabled (e.g., if inside a link or pressable container) if (preventParentActions && event) { // For React Native Web compatibility if (_reactNative.Platform.OS === 'web' && event.preventDefault) { event.preventDefault(); } // Stop event propagation to prevent parent TouchableOpacity/Pressable actions if (event.stopPropagation) { event.stopPropagation(); } // For React Native, prevent gesture bubbling if (event.nativeEvent && event.nativeEvent.stopPropagation) { event.nativeEvent.stopPropagation(); } } // If custom onPress is provided, use it instead of default behavior if (onPress) { onPress(event); return; } if (disabled || isLoading || !isAuthenticated) return; // Touch feedback animation scale.value = (0, _reactNativeReanimated.withSpring)(0.95, { damping: 10 }, () => { scale.value = (0, _reactNativeReanimated.withSpring)(1); }); setIsLoading(true); try { // This should be replaced with actual API call to your services if (isFollowing) { // Unfollow API call would go here // await oxyServices.user.unfollowUser(userId); console.log(`Unfollowing user: ${userId}`); await new Promise(resolve => setTimeout(resolve, 500)); // Simulating API call } else { // Follow API call would go here // await oxyServices.user.followUser(userId); console.log(`Following user: ${userId}`); await new Promise(resolve => setTimeout(resolve, 500)); // Simulating API call } // Toggle following state with animation const newFollowingState = !isFollowing; setIsFollowing(newFollowingState); // Call the callback if provided if (onFollowChange) { onFollowChange(newFollowingState); } // Show success toast _sonner.toast.success(newFollowingState ? 'Following user!' : 'Unfollowed user'); } catch (error) { console.error('Follow action failed:', error); _sonner.toast.error('Failed to update follow status. Please try again.'); } finally { setIsLoading(false); } }; // Animated styles for the button const animatedButtonStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => { const backgroundColor = (0, _reactNativeReanimated.interpolateColor)(animationProgress.value, [0, 1], ['#d169e5', '#FFFFFF']); const borderColor = (0, _reactNativeReanimated.interpolateColor)(animationProgress.value, [0, 1], ['#d169e5', '#d169e5']); // Add a slight scaling effect during the transition const transitionScale = 1 + 0.05 * Math.sin(animationProgress.value * Math.PI); return { backgroundColor, borderColor, borderWidth: 1, transform: [{ scale: scale.value * transitionScale }] }; }); // Animated styles for the text const animatedTextStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => { const color = (0, _reactNativeReanimated.interpolateColor)(animationProgress.value, [0, 1], ['#FFFFFF', '#d169e5']); return { color }; }); // Get size-specific styles const getSizeStyles = () => { switch (size) { case 'small': return { button: { paddingVertical: 6, paddingHorizontal: 12 }, text: { fontSize: 12 } }; case 'large': return { button: { paddingVertical: 12, paddingHorizontal: 24 }, text: { fontSize: 18 } }; default: // medium return { button: { paddingVertical: 8, paddingHorizontal: 16 }, text: { fontSize: 14 } }; } }; const sizeStyles = getSizeStyles(); return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, { activeOpacity: 0.8, onPress: handlePress, disabled: disabled || isLoading || !isAuthenticated, children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, { style: [styles.button, sizeStyles.button, animatedButtonStyle, style], children: isLoading && showLoadingState ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, { size: "small", color: isFollowing ? '#d169e5' : '#FFFFFF' }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.Text, { style: [styles.text, sizeStyles.text, animatedTextStyle, textStyle], children: isFollowing ? 'Following' : 'Follow' }) }) }); }; const styles = _reactNative.StyleSheet.create({ button: { justifyContent: 'center', alignItems: 'center', flexDirection: 'row', borderRadius: 100 }, text: { fontFamily: _reactNative.Platform.select({ web: 'Phudu', default: _fonts.fontFamilies.phuduSemiBold }), fontWeight: _reactNative.Platform.OS === 'web' ? '600' : undefined, textAlign: 'center' } }); var _default = exports.default = FollowButton; //# sourceMappingURL=FollowButton.js.map