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