UNPKG

@oxyhq/services

Version:

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

212 lines (200 loc) • 7.25 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.FollowButton = void 0; var _react = 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 _useFollow = require("../hooks/useFollow"); var _theme = require("../styles/theme"); 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); } // Create animated TouchableOpacity const AnimatedTouchableOpacity = _reactNativeReanimated.default.createAnimatedComponent(_reactNative.TouchableOpacity); const AnimatedText = _reactNativeReanimated.default.createAnimatedComponent(_reactNative.Text); const FollowButton = ({ userId, initiallyFollowing = false, size = 'medium', onFollowChange, style, textStyle, disabled = false, showLoadingState = true, preventParentActions = true, theme = 'light' }) => { const { oxyServices, isAuthenticated } = (0, _OxyContext.useOxy)(); const colors = (0, _theme.useThemeColors)(theme); const { isFollowing, isLoading, error, toggleFollow, setFollowStatus, fetchStatus, clearError } = (0, _useFollow.useFollow)(userId); // Animation values const animationProgress = (0, _reactNativeReanimated.useSharedValue)(isFollowing ? 1 : 0); const scale = (0, _reactNativeReanimated.useSharedValue)(1); // Button press handler with animation const handlePress = (0, _react.useCallback)(async event => { if (preventParentActions && event && event.preventDefault) { event.preventDefault(); event.stopPropagation?.(); } if (disabled || isLoading) return; // Press animation scale.value = (0, _reactNativeReanimated.withTiming)(0.95, { duration: 100 }, finished => { if (finished) { scale.value = (0, _reactNativeReanimated.withSpring)(1, { damping: 15, stiffness: 200 }); } }); try { await toggleFollow?.(); if (onFollowChange) onFollowChange(!isFollowing); } catch (err) { const error = err instanceof Error ? err : new Error(String(err)); _sonner.toast.error(error.message || 'Failed to update follow status'); } }, [disabled, isLoading, toggleFollow, onFollowChange, isFollowing, preventParentActions, scale]); // Initialize Zustand state with initial value if not already set (0, _react.useEffect)(() => { if (userId && !isFollowing && initiallyFollowing) { setFollowStatus?.(initiallyFollowing); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [userId, initiallyFollowing]); // Fetch latest follow status from backend on mount if authenticated (0, _react.useEffect)(() => { if (userId && isAuthenticated) { fetchStatus?.(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [userId, oxyServices, isAuthenticated]); // Animate button on follow/unfollow (0, _react.useEffect)(() => { animationProgress.value = (0, _reactNativeReanimated.withTiming)(isFollowing ? 1 : 0, { duration: 300, easing: _reactNativeReanimated.Easing.inOut(_reactNativeReanimated.Easing.ease) }); }, [isFollowing]); // Animated styles for better performance const animatedButtonStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => { return { transform: [{ scale: scale.value }], backgroundColor: (0, _reactNativeReanimated.interpolateColor)(animationProgress.value, [0, 1], [colors.background, colors.primary]), borderColor: (0, _reactNativeReanimated.interpolateColor)(animationProgress.value, [0, 1], [colors.border, colors.primary]) }; }, [colors]); const animatedTextStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => { return { color: (0, _reactNativeReanimated.interpolateColor)(animationProgress.value, [0, 1], [colors.text, '#FFFFFF']) }; }, [colors]); // Get base button style (without state-specific colors since they're animated) const getBaseButtonStyle = () => { const baseStyle = { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', borderWidth: 1, ..._reactNative.Platform.select({ web: { boxShadow: '0 2px 4px rgba(0,0,0,0.1)' }, default: { shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 4, elevation: 2 } }) }; // Size-specific styles let sizeStyle = {}; if (size === 'small') { sizeStyle = { paddingVertical: 6, paddingHorizontal: 12, minWidth: 70, borderRadius: 35 }; } else if (size === 'large') { sizeStyle = { paddingVertical: 12, paddingHorizontal: 24, minWidth: 120, borderRadius: 35 }; } else { // medium sizeStyle = { paddingVertical: 8, paddingHorizontal: 16, minWidth: 90, borderRadius: 35 }; } return [baseStyle, sizeStyle, style]; }; // Get base text style (without state-specific colors since they're animated) const getBaseTextStyle = () => { const baseTextStyle = { fontFamily: _fonts.fontFamilies.phuduSemiBold, fontWeight: '600' }; // Size-specific text styles let sizeTextStyle = {}; if (size === 'small') { sizeTextStyle = { fontSize: 13 }; } else if (size === 'large') { sizeTextStyle = { fontSize: 16 }; } else { // medium sizeTextStyle = { fontSize: 15 }; } return [baseTextStyle, sizeTextStyle, textStyle]; }; return /*#__PURE__*/(0, _jsxRuntime.jsx)(AnimatedTouchableOpacity, { style: [getBaseButtonStyle(), animatedButtonStyle], onPress: handlePress, disabled: disabled || isLoading, activeOpacity: 0.8, children: showLoadingState && isLoading ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, { size: size === 'small' ? 'small' : 'small', color: isFollowing ? '#FFFFFF' : colors.primary }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(AnimatedText, { style: [getBaseTextStyle(), animatedTextStyle], children: isFollowing ? 'Following' : 'Follow' }) }); }; exports.FollowButton = FollowButton; var _default = exports.default = FollowButton; //# sourceMappingURL=FollowButton.js.map