@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
JavaScript
;
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