@oxyhq/services 5.1.15 → 5.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/UI_COMPONENTS.md CHANGED
@@ -8,6 +8,7 @@ This document provides details about the UI components available in the `@oxyhq/
8
8
  - [OxySignInButton](#oxysigninbutton)
9
9
  - [OxyLogo](#oxylogo)
10
10
  - [Avatar](#avatar)
11
+ - [FollowButton](#followbutton)
11
12
 
12
13
  ## OxyProvider
13
14
 
@@ -141,3 +142,56 @@ npm install react-native-svg
141
142
  # or
142
143
  yarn add react-native-svg
143
144
  ```
145
+
146
+ ## FollowButton
147
+
148
+ An animated button component for social interactions that toggles between "Follow" and "Following" states with smooth transitions.
149
+
150
+ ```tsx
151
+ import { FollowButton } from '@oxyhq/services';
152
+
153
+ // Basic usage
154
+ <FollowButton userId="123" />
155
+
156
+ // With custom styling
157
+ <FollowButton
158
+ userId="123"
159
+ initiallyFollowing={true}
160
+ size="large"
161
+ style={{ borderRadius: 12 }}
162
+ onFollowChange={(isFollowing) => console.log(`User is now ${isFollowing ? 'followed' : 'unfollowed'}`)}
163
+ />
164
+
165
+ // Different sizes
166
+ <FollowButton userId="123" size="small" />
167
+ <FollowButton userId="123" size="medium" /> // default
168
+ <FollowButton userId="123" size="large" />
169
+
170
+ // Disabled state
171
+ <FollowButton userId="123" disabled={true} />
172
+ ```
173
+
174
+ ### Props
175
+
176
+ | Prop | Type | Default | Description |
177
+ |------|------|---------|-------------|
178
+ | userId | `string` | *Required* | The ID of the user to follow/unfollow |
179
+ | initiallyFollowing | `boolean` | `false` | Initial follow state, if already known |
180
+ | size | `'small' \| 'medium' \| 'large'` | `'medium'` | Size variant of the button |
181
+ | onFollowChange | `(isFollowing: boolean) => void` | `undefined` | Callback function invoked when follow state changes |
182
+ | style | `StyleProp<ViewStyle>` | `undefined` | Additional styles for the button container |
183
+ | textStyle | `StyleProp<TextStyle>` | `undefined` | Additional styles for the button text |
184
+ | disabled | `boolean` | `false` | Whether the button is disabled |
185
+ | showLoadingState | `boolean` | `true` | Whether to show loading indicator during API calls |
186
+
187
+ ### Requirements
188
+
189
+ This component requires `react-native-reanimated` to be installed in your project:
190
+
191
+ ```bash
192
+ npm install react-native-reanimated
193
+ # or
194
+ yarn add react-native-reanimated
195
+ ```
196
+
197
+ > **Note:** After installing react-native-reanimated, you may need to set up the Babel plugin. Add `'react-native-reanimated/plugin'` to your Babel plugins in `babel.config.js`.
@@ -0,0 +1,198 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _reactNative = require("react-native");
9
+ var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
10
+ var _OxyContext = require("../context/OxyContext");
11
+ var _fonts = require("../styles/fonts");
12
+ var _jsxRuntime = require("react/jsx-runtime");
13
+ 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); }
14
+ /**
15
+ * An animated follow button with interactive state changes
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * // Basic usage
20
+ * <FollowButton userId="123" />
21
+ *
22
+ * // With custom styling
23
+ * <FollowButton
24
+ * userId="123"
25
+ * initiallyFollowing={true}
26
+ * size="large"
27
+ * style={{ borderRadius: 12 }}
28
+ * onFollowChange={(isFollowing) => console.log(`User is now ${isFollowing ? 'followed' : 'unfollowed'}`)}
29
+ * />
30
+ * ```
31
+ */
32
+ const FollowButton = ({
33
+ userId,
34
+ initiallyFollowing = false,
35
+ size = 'medium',
36
+ onFollowChange,
37
+ style,
38
+ textStyle,
39
+ disabled = false,
40
+ showLoadingState = true
41
+ }) => {
42
+ const {
43
+ oxyServices,
44
+ isAuthenticated
45
+ } = (0, _OxyContext.useOxy)();
46
+ const [isFollowing, setIsFollowing] = (0, _react.useState)(initiallyFollowing);
47
+ const [isLoading, setIsLoading] = (0, _react.useState)(false);
48
+
49
+ // Animation values
50
+ const animationProgress = (0, _reactNativeReanimated.useSharedValue)(initiallyFollowing ? 1 : 0);
51
+ const scale = (0, _reactNativeReanimated.useSharedValue)(1);
52
+
53
+ // Update the animation value when isFollowing changes
54
+ (0, _react.useEffect)(() => {
55
+ animationProgress.value = (0, _reactNativeReanimated.withTiming)(isFollowing ? 1 : 0, {
56
+ duration: 300,
57
+ easing: _reactNativeReanimated.Easing.bezier(0.25, 0.1, 0.25, 1)
58
+ });
59
+ }, [isFollowing, animationProgress]);
60
+
61
+ // The button press handler
62
+ const handlePress = async () => {
63
+ if (disabled || isLoading || !isAuthenticated) return;
64
+
65
+ // Touch feedback animation
66
+ scale.value = (0, _reactNativeReanimated.withSpring)(0.95, {
67
+ damping: 10
68
+ }, () => {
69
+ scale.value = (0, _reactNativeReanimated.withSpring)(1);
70
+ });
71
+ setIsLoading(true);
72
+ try {
73
+ // This should be replaced with actual API call to your services
74
+ if (isFollowing) {
75
+ // Unfollow API call would go here
76
+ // await oxyServices.user.unfollowUser(userId);
77
+ console.log(`Unfollowing user: ${userId}`);
78
+ await new Promise(resolve => setTimeout(resolve, 500)); // Simulating API call
79
+ } else {
80
+ // Follow API call would go here
81
+ // await oxyServices.user.followUser(userId);
82
+ console.log(`Following user: ${userId}`);
83
+ await new Promise(resolve => setTimeout(resolve, 500)); // Simulating API call
84
+ }
85
+
86
+ // Toggle following state with animation
87
+ const newFollowingState = !isFollowing;
88
+ setIsFollowing(newFollowingState);
89
+
90
+ // Call the callback if provided
91
+ if (onFollowChange) {
92
+ onFollowChange(newFollowingState);
93
+ }
94
+ } catch (error) {
95
+ console.error('Follow action failed:', error);
96
+ } finally {
97
+ setIsLoading(false);
98
+ }
99
+ };
100
+
101
+ // Animated styles for the button
102
+ const animatedButtonStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
103
+ const backgroundColor = (0, _reactNativeReanimated.interpolateColor)(animationProgress.value, [0, 1], ['#d169e5', '#FFFFFF']);
104
+ const borderColor = (0, _reactNativeReanimated.interpolateColor)(animationProgress.value, [0, 1], ['#d169e5', '#d169e5']);
105
+
106
+ // Add a slight scaling effect during the transition
107
+ const transitionScale = 1 + 0.05 * Math.sin(animationProgress.value * Math.PI);
108
+ return {
109
+ backgroundColor,
110
+ borderColor,
111
+ borderWidth: 1,
112
+ transform: [{
113
+ scale: scale.value * transitionScale
114
+ }]
115
+ };
116
+ });
117
+
118
+ // Animated styles for the text
119
+ const animatedTextStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
120
+ const color = (0, _reactNativeReanimated.interpolateColor)(animationProgress.value, [0, 1], ['#FFFFFF', '#d169e5']);
121
+ return {
122
+ color
123
+ };
124
+ });
125
+
126
+ // Get size-specific styles
127
+ const getSizeStyles = () => {
128
+ switch (size) {
129
+ case 'small':
130
+ return {
131
+ button: {
132
+ paddingVertical: 6,
133
+ paddingHorizontal: 12,
134
+ borderRadius: 12
135
+ },
136
+ text: {
137
+ fontSize: 12
138
+ }
139
+ };
140
+ case 'large':
141
+ return {
142
+ button: {
143
+ paddingVertical: 12,
144
+ paddingHorizontal: 24,
145
+ borderRadius: 16
146
+ },
147
+ text: {
148
+ fontSize: 18
149
+ }
150
+ };
151
+ default:
152
+ // medium
153
+ return {
154
+ button: {
155
+ paddingVertical: 8,
156
+ paddingHorizontal: 16,
157
+ borderRadius: 14
158
+ },
159
+ text: {
160
+ fontSize: 14
161
+ }
162
+ };
163
+ }
164
+ };
165
+ const sizeStyles = getSizeStyles();
166
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
167
+ activeOpacity: 0.8,
168
+ onPress: handlePress,
169
+ disabled: disabled || isLoading || !isAuthenticated,
170
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.View, {
171
+ style: [styles.button, sizeStyles.button, animatedButtonStyle, style],
172
+ children: isLoading && showLoadingState ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
173
+ size: "small",
174
+ color: isFollowing ? '#d169e5' : '#FFFFFF'
175
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeReanimated.default.Text, {
176
+ style: [styles.text, sizeStyles.text, animatedTextStyle, textStyle],
177
+ children: isFollowing ? 'Following' : 'Follow'
178
+ })
179
+ })
180
+ });
181
+ };
182
+ const styles = _reactNative.StyleSheet.create({
183
+ button: {
184
+ justifyContent: 'center',
185
+ alignItems: 'center',
186
+ flexDirection: 'row'
187
+ },
188
+ text: {
189
+ fontFamily: _reactNative.Platform.select({
190
+ web: 'Phudu',
191
+ default: _fonts.fontFamilies.phuduSemiBold
192
+ }),
193
+ fontWeight: _reactNative.Platform.OS === 'web' ? '600' : undefined,
194
+ textAlign: 'center'
195
+ }
196
+ });
197
+ var _default = exports.default = FollowButton;
198
+ //# sourceMappingURL=FollowButton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_reactNativeReanimated","_OxyContext","_fonts","_jsxRuntime","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","FollowButton","userId","initiallyFollowing","size","onFollowChange","style","textStyle","disabled","showLoadingState","oxyServices","isAuthenticated","useOxy","isFollowing","setIsFollowing","useState","isLoading","setIsLoading","animationProgress","useSharedValue","scale","useEffect","value","withTiming","duration","easing","Easing","bezier","handlePress","withSpring","damping","console","log","Promise","resolve","setTimeout","newFollowingState","error","animatedButtonStyle","useAnimatedStyle","backgroundColor","interpolateColor","borderColor","transitionScale","Math","sin","PI","borderWidth","transform","animatedTextStyle","color","getSizeStyles","button","paddingVertical","paddingHorizontal","borderRadius","text","fontSize","sizeStyles","jsx","TouchableOpacity","activeOpacity","onPress","children","View","styles","ActivityIndicator","Text","StyleSheet","create","justifyContent","alignItems","flexDirection","fontFamily","Platform","select","web","fontFamilies","phuduSemiBold","fontWeight","OS","undefined","textAlign","_default","exports"],"sourceRoot":"../../../../src","sources":["ui/components/FollowButton.tsx"],"mappings":"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","ignoreList":[]}
@@ -8,6 +8,7 @@ var _exportNames = {
8
8
  OxySignInButton: true,
9
9
  OxyLogo: true,
10
10
  Avatar: true,
11
+ FollowButton: true,
11
12
  FontLoader: true,
12
13
  setupFonts: true,
13
14
  OxyContextProvider: true,
@@ -39,6 +40,12 @@ Object.defineProperty(exports, "Avatar", {
39
40
  return _Avatar.default;
40
41
  }
41
42
  });
43
+ Object.defineProperty(exports, "FollowButton", {
44
+ enumerable: true,
45
+ get: function () {
46
+ return _FollowButton.default;
47
+ }
48
+ });
42
49
  Object.defineProperty(exports, "FontLoader", {
43
50
  enumerable: true,
44
51
  get: function () {
@@ -121,6 +128,7 @@ var _OxyProvider = _interopRequireDefault(require("./components/OxyProvider"));
121
128
  var _OxySignInButton = _interopRequireDefault(require("./components/OxySignInButton"));
122
129
  var _OxyLogo = _interopRequireDefault(require("./components/OxyLogo"));
123
130
  var _Avatar = _interopRequireDefault(require("./components/Avatar"));
131
+ var _FollowButton = _interopRequireDefault(require("./components/FollowButton"));
124
132
  var _FontLoader = require("./components/FontLoader");
125
133
  var _OxyContext = require("./context/OxyContext");
126
134
  var _fonts = require("./styles/fonts");
@@ -1 +1 @@
1
- {"version":3,"names":["_OxyProvider","_interopRequireDefault","require","_OxySignInButton","_OxyLogo","_Avatar","_FontLoader","_OxyContext","_fonts","_SignInScreen","_SignUpScreen","_AccountCenterScreen","_AccountOverviewScreen","_types","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get","e","__esModule","default"],"sourceRoot":"../../../src","sources":["ui/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAKA,IAAAA,YAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,gBAAA,GAAAF,sBAAA,CAAAC,OAAA;AACA,IAAAE,QAAA,GAAAH,sBAAA,CAAAC,OAAA;AACA,IAAAG,OAAA,GAAAJ,sBAAA,CAAAC,OAAA;AACA,IAAAI,WAAA,GAAAJ,OAAA;AACA,IAAAK,WAAA,GAAAL,OAAA;AAQA,IAAAM,MAAA,GAAAN,OAAA;AAGA,IAAAO,aAAA,GAAAR,sBAAA,CAAAC,OAAA;AACA,IAAAQ,aAAA,GAAAT,sBAAA,CAAAC,OAAA;AACA,IAAAS,oBAAA,GAAAV,sBAAA,CAAAC,OAAA;AACA,IAAAU,sBAAA,GAAAX,sBAAA,CAAAC,OAAA;AAGA,IAAAW,MAAA,GAAAX,OAAA;AAAAY,MAAA,CAAAC,IAAA,CAAAF,MAAA,EAAAG,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAH,MAAA,CAAAI,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAC,YAAA,EAAAJ,GAAA;EAAA,IAAAA,GAAA,IAAAK,OAAA,IAAAA,OAAA,CAAAL,GAAA,MAAAJ,MAAA,CAAAI,GAAA;EAAAH,MAAA,CAAAS,cAAA,CAAAD,OAAA,EAAAL,GAAA;IAAAO,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAZ,MAAA,CAAAI,GAAA;IAAA;EAAA;AAAA;AAAmC,SAAAhB,uBAAAyB,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA","ignoreList":[]}
1
+ {"version":3,"names":["_OxyProvider","_interopRequireDefault","require","_OxySignInButton","_OxyLogo","_Avatar","_FollowButton","_FontLoader","_OxyContext","_fonts","_SignInScreen","_SignUpScreen","_AccountCenterScreen","_AccountOverviewScreen","_types","Object","keys","forEach","key","prototype","hasOwnProperty","call","_exportNames","exports","defineProperty","enumerable","get","e","__esModule","default"],"sourceRoot":"../../../src","sources":["ui/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAKA,IAAAA,YAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,gBAAA,GAAAF,sBAAA,CAAAC,OAAA;AACA,IAAAE,QAAA,GAAAH,sBAAA,CAAAC,OAAA;AACA,IAAAG,OAAA,GAAAJ,sBAAA,CAAAC,OAAA;AACA,IAAAI,aAAA,GAAAL,sBAAA,CAAAC,OAAA;AACA,IAAAK,WAAA,GAAAL,OAAA;AACA,IAAAM,WAAA,GAAAN,OAAA;AAQA,IAAAO,MAAA,GAAAP,OAAA;AAGA,IAAAQ,aAAA,GAAAT,sBAAA,CAAAC,OAAA;AACA,IAAAS,aAAA,GAAAV,sBAAA,CAAAC,OAAA;AACA,IAAAU,oBAAA,GAAAX,sBAAA,CAAAC,OAAA;AACA,IAAAW,sBAAA,GAAAZ,sBAAA,CAAAC,OAAA;AAGA,IAAAY,MAAA,GAAAZ,OAAA;AAAAa,MAAA,CAAAC,IAAA,CAAAF,MAAA,EAAAG,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAH,MAAA,CAAAI,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAC,YAAA,EAAAJ,GAAA;EAAA,IAAAA,GAAA,IAAAK,OAAA,IAAAA,OAAA,CAAAL,GAAA,MAAAJ,MAAA,CAAAI,GAAA;EAAAH,MAAA,CAAAS,cAAA,CAAAD,OAAA,EAAAL,GAAA;IAAAO,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAZ,MAAA,CAAAI,GAAA;IAAA;EAAA;AAAA;AAAmC,SAAAjB,uBAAA0B,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA","ignoreList":[]}
@@ -0,0 +1,193 @@
1
+ "use strict";
2
+
3
+ import React, { useState, useEffect } from 'react';
4
+ import { TouchableOpacity, StyleSheet, Platform, ActivityIndicator } from 'react-native';
5
+ import Animated, { useSharedValue, useAnimatedStyle, withSpring, interpolateColor, Easing, withTiming } from 'react-native-reanimated';
6
+ import { useOxy } from '../context/OxyContext';
7
+ import { fontFamilies } from '../styles/fonts';
8
+ import { jsx as _jsx } from "react/jsx-runtime";
9
+ /**
10
+ * An animated follow button with interactive state changes
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * // Basic usage
15
+ * <FollowButton userId="123" />
16
+ *
17
+ * // With custom styling
18
+ * <FollowButton
19
+ * userId="123"
20
+ * initiallyFollowing={true}
21
+ * size="large"
22
+ * style={{ borderRadius: 12 }}
23
+ * onFollowChange={(isFollowing) => console.log(`User is now ${isFollowing ? 'followed' : 'unfollowed'}`)}
24
+ * />
25
+ * ```
26
+ */
27
+ const FollowButton = ({
28
+ userId,
29
+ initiallyFollowing = false,
30
+ size = 'medium',
31
+ onFollowChange,
32
+ style,
33
+ textStyle,
34
+ disabled = false,
35
+ showLoadingState = true
36
+ }) => {
37
+ const {
38
+ oxyServices,
39
+ isAuthenticated
40
+ } = useOxy();
41
+ const [isFollowing, setIsFollowing] = useState(initiallyFollowing);
42
+ const [isLoading, setIsLoading] = useState(false);
43
+
44
+ // Animation values
45
+ const animationProgress = useSharedValue(initiallyFollowing ? 1 : 0);
46
+ const scale = useSharedValue(1);
47
+
48
+ // Update the animation value when isFollowing changes
49
+ useEffect(() => {
50
+ animationProgress.value = withTiming(isFollowing ? 1 : 0, {
51
+ duration: 300,
52
+ easing: Easing.bezier(0.25, 0.1, 0.25, 1)
53
+ });
54
+ }, [isFollowing, animationProgress]);
55
+
56
+ // The button press handler
57
+ const handlePress = async () => {
58
+ if (disabled || isLoading || !isAuthenticated) return;
59
+
60
+ // Touch feedback animation
61
+ scale.value = withSpring(0.95, {
62
+ damping: 10
63
+ }, () => {
64
+ scale.value = withSpring(1);
65
+ });
66
+ setIsLoading(true);
67
+ try {
68
+ // This should be replaced with actual API call to your services
69
+ if (isFollowing) {
70
+ // Unfollow API call would go here
71
+ // await oxyServices.user.unfollowUser(userId);
72
+ console.log(`Unfollowing user: ${userId}`);
73
+ await new Promise(resolve => setTimeout(resolve, 500)); // Simulating API call
74
+ } else {
75
+ // Follow API call would go here
76
+ // await oxyServices.user.followUser(userId);
77
+ console.log(`Following user: ${userId}`);
78
+ await new Promise(resolve => setTimeout(resolve, 500)); // Simulating API call
79
+ }
80
+
81
+ // Toggle following state with animation
82
+ const newFollowingState = !isFollowing;
83
+ setIsFollowing(newFollowingState);
84
+
85
+ // Call the callback if provided
86
+ if (onFollowChange) {
87
+ onFollowChange(newFollowingState);
88
+ }
89
+ } catch (error) {
90
+ console.error('Follow action failed:', error);
91
+ } finally {
92
+ setIsLoading(false);
93
+ }
94
+ };
95
+
96
+ // Animated styles for the button
97
+ const animatedButtonStyle = useAnimatedStyle(() => {
98
+ const backgroundColor = interpolateColor(animationProgress.value, [0, 1], ['#d169e5', '#FFFFFF']);
99
+ const borderColor = interpolateColor(animationProgress.value, [0, 1], ['#d169e5', '#d169e5']);
100
+
101
+ // Add a slight scaling effect during the transition
102
+ const transitionScale = 1 + 0.05 * Math.sin(animationProgress.value * Math.PI);
103
+ return {
104
+ backgroundColor,
105
+ borderColor,
106
+ borderWidth: 1,
107
+ transform: [{
108
+ scale: scale.value * transitionScale
109
+ }]
110
+ };
111
+ });
112
+
113
+ // Animated styles for the text
114
+ const animatedTextStyle = useAnimatedStyle(() => {
115
+ const color = interpolateColor(animationProgress.value, [0, 1], ['#FFFFFF', '#d169e5']);
116
+ return {
117
+ color
118
+ };
119
+ });
120
+
121
+ // Get size-specific styles
122
+ const getSizeStyles = () => {
123
+ switch (size) {
124
+ case 'small':
125
+ return {
126
+ button: {
127
+ paddingVertical: 6,
128
+ paddingHorizontal: 12,
129
+ borderRadius: 12
130
+ },
131
+ text: {
132
+ fontSize: 12
133
+ }
134
+ };
135
+ case 'large':
136
+ return {
137
+ button: {
138
+ paddingVertical: 12,
139
+ paddingHorizontal: 24,
140
+ borderRadius: 16
141
+ },
142
+ text: {
143
+ fontSize: 18
144
+ }
145
+ };
146
+ default:
147
+ // medium
148
+ return {
149
+ button: {
150
+ paddingVertical: 8,
151
+ paddingHorizontal: 16,
152
+ borderRadius: 14
153
+ },
154
+ text: {
155
+ fontSize: 14
156
+ }
157
+ };
158
+ }
159
+ };
160
+ const sizeStyles = getSizeStyles();
161
+ return /*#__PURE__*/_jsx(TouchableOpacity, {
162
+ activeOpacity: 0.8,
163
+ onPress: handlePress,
164
+ disabled: disabled || isLoading || !isAuthenticated,
165
+ children: /*#__PURE__*/_jsx(Animated.View, {
166
+ style: [styles.button, sizeStyles.button, animatedButtonStyle, style],
167
+ children: isLoading && showLoadingState ? /*#__PURE__*/_jsx(ActivityIndicator, {
168
+ size: "small",
169
+ color: isFollowing ? '#d169e5' : '#FFFFFF'
170
+ }) : /*#__PURE__*/_jsx(Animated.Text, {
171
+ style: [styles.text, sizeStyles.text, animatedTextStyle, textStyle],
172
+ children: isFollowing ? 'Following' : 'Follow'
173
+ })
174
+ })
175
+ });
176
+ };
177
+ const styles = StyleSheet.create({
178
+ button: {
179
+ justifyContent: 'center',
180
+ alignItems: 'center',
181
+ flexDirection: 'row'
182
+ },
183
+ text: {
184
+ fontFamily: Platform.select({
185
+ web: 'Phudu',
186
+ default: fontFamilies.phuduSemiBold
187
+ }),
188
+ fontWeight: Platform.OS === 'web' ? '600' : undefined,
189
+ textAlign: 'center'
190
+ }
191
+ });
192
+ export default FollowButton;
193
+ //# sourceMappingURL=FollowButton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["React","useState","useEffect","TouchableOpacity","StyleSheet","Platform","ActivityIndicator","Animated","useSharedValue","useAnimatedStyle","withSpring","interpolateColor","Easing","withTiming","useOxy","fontFamilies","jsx","_jsx","FollowButton","userId","initiallyFollowing","size","onFollowChange","style","textStyle","disabled","showLoadingState","oxyServices","isAuthenticated","isFollowing","setIsFollowing","isLoading","setIsLoading","animationProgress","scale","value","duration","easing","bezier","handlePress","damping","console","log","Promise","resolve","setTimeout","newFollowingState","error","animatedButtonStyle","backgroundColor","borderColor","transitionScale","Math","sin","PI","borderWidth","transform","animatedTextStyle","color","getSizeStyles","button","paddingVertical","paddingHorizontal","borderRadius","text","fontSize","sizeStyles","activeOpacity","onPress","children","View","styles","Text","create","justifyContent","alignItems","flexDirection","fontFamily","select","web","default","phuduSemiBold","fontWeight","OS","undefined","textAlign"],"sourceRoot":"../../../../src","sources":["ui/components/FollowButton.tsx"],"mappings":"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","ignoreList":[]}
@@ -9,6 +9,7 @@ export { default as OxyProvider } from './components/OxyProvider';
9
9
  export { default as OxySignInButton } from './components/OxySignInButton';
10
10
  export { default as OxyLogo } from './components/OxyLogo';
11
11
  export { default as Avatar } from './components/Avatar';
12
+ export { default as FollowButton } from './components/FollowButton';
12
13
  export { FontLoader, setupFonts } from './components/FontLoader';
13
14
  export { OxyContextProvider, useOxy, OxyContextState, OxyContextProviderProps } from './context/OxyContext';
14
15
 
@@ -1 +1 @@
1
- {"version":3,"names":["default","OxyProvider","OxySignInButton","OxyLogo","Avatar","FontLoader","setupFonts","OxyContextProvider","useOxy","OxyContextState","OxyContextProviderProps","fontFamilies","fontStyles","SignInScreen","SignUpScreen","AccountCenterScreen","AccountOverviewScreen"],"sourceRoot":"../../../src","sources":["ui/index.ts"],"mappings":";;AAAA;AACA;AACA;;AAEA;AACA,SAASA,OAAO,IAAIC,WAAW,QAAQ,0BAA0B;AACjE,SAASD,OAAO,IAAIE,eAAe,QAAQ,8BAA8B;AACzE,SAASF,OAAO,IAAIG,OAAO,QAAQ,sBAAsB;AACzD,SAASH,OAAO,IAAII,MAAM,QAAQ,qBAAqB;AACvD,SAASC,UAAU,EAAEC,UAAU,QAAQ,yBAAyB;AAChE,SACEC,kBAAkB,EAClBC,MAAM,EACNC,eAAe,EACfC,uBAAuB,QAClB,sBAAsB;;AAE7B;AACA,SAASC,YAAY,EAAEC,UAAU,QAAQ,gBAAgB;;AAEzD;AACA,SAASZ,OAAO,IAAIa,YAAY,QAAQ,wBAAwB;AAChE,SAASb,OAAO,IAAIc,YAAY,QAAQ,wBAAwB;AAChE,SAASd,OAAO,IAAIe,mBAAmB,QAAQ,+BAA+B;AAC9E,SAASf,OAAO,IAAIgB,qBAAqB,QAAQ,iCAAiC;;AAElF;AACA,cAAc,oBAAoB","ignoreList":[]}
1
+ {"version":3,"names":["default","OxyProvider","OxySignInButton","OxyLogo","Avatar","FollowButton","FontLoader","setupFonts","OxyContextProvider","useOxy","OxyContextState","OxyContextProviderProps","fontFamilies","fontStyles","SignInScreen","SignUpScreen","AccountCenterScreen","AccountOverviewScreen"],"sourceRoot":"../../../src","sources":["ui/index.ts"],"mappings":";;AAAA;AACA;AACA;;AAEA;AACA,SAASA,OAAO,IAAIC,WAAW,QAAQ,0BAA0B;AACjE,SAASD,OAAO,IAAIE,eAAe,QAAQ,8BAA8B;AACzE,SAASF,OAAO,IAAIG,OAAO,QAAQ,sBAAsB;AACzD,SAASH,OAAO,IAAII,MAAM,QAAQ,qBAAqB;AACvD,SAASJ,OAAO,IAAIK,YAAY,QAAQ,2BAA2B;AACnE,SAASC,UAAU,EAAEC,UAAU,QAAQ,yBAAyB;AAChE,SACEC,kBAAkB,EAClBC,MAAM,EACNC,eAAe,EACfC,uBAAuB,QAClB,sBAAsB;;AAE7B;AACA,SAASC,YAAY,EAAEC,UAAU,QAAQ,gBAAgB;;AAEzD;AACA,SAASb,OAAO,IAAIc,YAAY,QAAQ,wBAAwB;AAChE,SAASd,OAAO,IAAIe,YAAY,QAAQ,wBAAwB;AAChE,SAASf,OAAO,IAAIgB,mBAAmB,QAAQ,+BAA+B;AAC9E,SAAShB,OAAO,IAAIiB,qBAAqB,QAAQ,iCAAiC;;AAElF;AACA,cAAc,oBAAoB","ignoreList":[]}
@@ -0,0 +1,61 @@
1
+ import React from 'react';
2
+ import { ViewStyle, TextStyle, StyleProp } from 'react-native';
3
+ export interface FollowButtonProps {
4
+ /**
5
+ * The user ID to follow/unfollow
6
+ */
7
+ userId: string;
8
+ /**
9
+ * Initial follow state, if known
10
+ * @default false
11
+ */
12
+ initiallyFollowing?: boolean;
13
+ /**
14
+ * Button size
15
+ * @default 'medium'
16
+ */
17
+ size?: 'small' | 'medium' | 'large';
18
+ /**
19
+ * Custom callback when follow/unfollow action happens
20
+ */
21
+ onFollowChange?: (isFollowing: boolean) => void;
22
+ /**
23
+ * Additional styles for the button container
24
+ */
25
+ style?: StyleProp<ViewStyle>;
26
+ /**
27
+ * Additional styles for the button text
28
+ */
29
+ textStyle?: StyleProp<TextStyle>;
30
+ /**
31
+ * Whether to disable the button
32
+ * @default false
33
+ */
34
+ disabled?: boolean;
35
+ /**
36
+ * Whether to show loading indicator during API calls
37
+ * @default true
38
+ */
39
+ showLoadingState?: boolean;
40
+ }
41
+ /**
42
+ * An animated follow button with interactive state changes
43
+ *
44
+ * @example
45
+ * ```tsx
46
+ * // Basic usage
47
+ * <FollowButton userId="123" />
48
+ *
49
+ * // With custom styling
50
+ * <FollowButton
51
+ * userId="123"
52
+ * initiallyFollowing={true}
53
+ * size="large"
54
+ * style={{ borderRadius: 12 }}
55
+ * onFollowChange={(isFollowing) => console.log(`User is now ${isFollowing ? 'followed' : 'unfollowed'}`)}
56
+ * />
57
+ * ```
58
+ */
59
+ declare const FollowButton: React.FC<FollowButtonProps>;
60
+ export default FollowButton;
61
+ //# sourceMappingURL=FollowButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FollowButton.d.ts","sourceRoot":"","sources":["../../../../src/ui/components/FollowButton.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AACnD,OAAO,EAIL,SAAS,EACT,SAAS,EACT,SAAS,EAGV,MAAM,cAAc,CAAC;AAYtB,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;IAEpC;;OAEG;IACH,cAAc,CAAC,EAAE,CAAC,WAAW,EAAE,OAAO,KAAK,IAAI,CAAC;IAEhD;;OAEG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAE7B;;OAEG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAEjC;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,QAAA,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAqL7C,CAAC;AAkBF,eAAe,YAAY,CAAC"}
@@ -5,6 +5,7 @@ export { default as OxyProvider } from './components/OxyProvider';
5
5
  export { default as OxySignInButton } from './components/OxySignInButton';
6
6
  export { default as OxyLogo } from './components/OxyLogo';
7
7
  export { default as Avatar } from './components/Avatar';
8
+ export { default as FollowButton } from './components/FollowButton';
8
9
  export { FontLoader, setupFonts } from './components/FontLoader';
9
10
  export { OxyContextProvider, useOxy, OxyContextState, OxyContextProviderProps } from './context/OxyContext';
10
11
  export { fontFamilies, fontStyles } from './styles/fonts';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EACL,kBAAkB,EAClB,MAAM,EACN,eAAe,EACf,uBAAuB,EACxB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAG1D,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAC/E,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAGnF,cAAc,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EACL,kBAAkB,EAClB,MAAM,EACN,eAAe,EACf,uBAAuB,EACxB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAG1D,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AAC/E,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAGnF,cAAc,oBAAoB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/services",
3
- "version": "5.1.15",
3
+ "version": "5.1.16",
4
4
  "description": "Reusable OxyHQ module to handle authentication, user management, karma system and more 🚀",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -0,0 +1,286 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import {
3
+ TouchableOpacity,
4
+ Text,
5
+ StyleSheet,
6
+ ViewStyle,
7
+ TextStyle,
8
+ StyleProp,
9
+ Platform,
10
+ ActivityIndicator
11
+ } from 'react-native';
12
+ import Animated, {
13
+ useSharedValue,
14
+ useAnimatedStyle,
15
+ withSpring,
16
+ interpolateColor,
17
+ Easing,
18
+ withTiming
19
+ } from 'react-native-reanimated';
20
+ import { useOxy } from '../context/OxyContext';
21
+ import { fontFamilies } from '../styles/fonts';
22
+
23
+ export interface FollowButtonProps {
24
+ /**
25
+ * The user ID to follow/unfollow
26
+ */
27
+ userId: string;
28
+
29
+ /**
30
+ * Initial follow state, if known
31
+ * @default false
32
+ */
33
+ initiallyFollowing?: boolean;
34
+
35
+ /**
36
+ * Button size
37
+ * @default 'medium'
38
+ */
39
+ size?: 'small' | 'medium' | 'large';
40
+
41
+ /**
42
+ * Custom callback when follow/unfollow action happens
43
+ */
44
+ onFollowChange?: (isFollowing: boolean) => void;
45
+
46
+ /**
47
+ * Additional styles for the button container
48
+ */
49
+ style?: StyleProp<ViewStyle>;
50
+
51
+ /**
52
+ * Additional styles for the button text
53
+ */
54
+ textStyle?: StyleProp<TextStyle>;
55
+
56
+ /**
57
+ * Whether to disable the button
58
+ * @default false
59
+ */
60
+ disabled?: boolean;
61
+
62
+ /**
63
+ * Whether to show loading indicator during API calls
64
+ * @default true
65
+ */
66
+ showLoadingState?: boolean;
67
+ }
68
+
69
+ /**
70
+ * An animated follow button with interactive state changes
71
+ *
72
+ * @example
73
+ * ```tsx
74
+ * // Basic usage
75
+ * <FollowButton userId="123" />
76
+ *
77
+ * // With custom styling
78
+ * <FollowButton
79
+ * userId="123"
80
+ * initiallyFollowing={true}
81
+ * size="large"
82
+ * style={{ borderRadius: 12 }}
83
+ * onFollowChange={(isFollowing) => console.log(`User is now ${isFollowing ? 'followed' : 'unfollowed'}`)}
84
+ * />
85
+ * ```
86
+ */
87
+ const FollowButton: React.FC<FollowButtonProps> = ({
88
+ userId,
89
+ initiallyFollowing = false,
90
+ size = 'medium',
91
+ onFollowChange,
92
+ style,
93
+ textStyle,
94
+ disabled = false,
95
+ showLoadingState = true,
96
+ }) => {
97
+ const { oxyServices, isAuthenticated } = useOxy();
98
+ const [isFollowing, setIsFollowing] = useState(initiallyFollowing);
99
+ const [isLoading, setIsLoading] = useState(false);
100
+
101
+ // Animation values
102
+ const animationProgress = useSharedValue(initiallyFollowing ? 1 : 0);
103
+ const scale = useSharedValue(1);
104
+
105
+ // Update the animation value when isFollowing changes
106
+ useEffect(() => {
107
+ animationProgress.value = withTiming(isFollowing ? 1 : 0, {
108
+ duration: 300,
109
+ easing: Easing.bezier(0.25, 0.1, 0.25, 1),
110
+ });
111
+ }, [isFollowing, animationProgress]);
112
+
113
+ // The button press handler
114
+ const handlePress = async () => {
115
+ if (disabled || isLoading || !isAuthenticated) return;
116
+
117
+ // Touch feedback animation
118
+ scale.value = withSpring(0.95, { damping: 10 }, () => {
119
+ scale.value = withSpring(1);
120
+ });
121
+
122
+ setIsLoading(true);
123
+
124
+ try {
125
+ // This should be replaced with actual API call to your services
126
+ if (isFollowing) {
127
+ // Unfollow API call would go here
128
+ // await oxyServices.user.unfollowUser(userId);
129
+ console.log(`Unfollowing user: ${userId}`);
130
+ await new Promise(resolve => setTimeout(resolve, 500)); // Simulating API call
131
+ } else {
132
+ // Follow API call would go here
133
+ // await oxyServices.user.followUser(userId);
134
+ console.log(`Following user: ${userId}`);
135
+ await new Promise(resolve => setTimeout(resolve, 500)); // Simulating API call
136
+ }
137
+
138
+ // Toggle following state with animation
139
+ const newFollowingState = !isFollowing;
140
+ setIsFollowing(newFollowingState);
141
+
142
+ // Call the callback if provided
143
+ if (onFollowChange) {
144
+ onFollowChange(newFollowingState);
145
+ }
146
+ } catch (error) {
147
+ console.error('Follow action failed:', error);
148
+ } finally {
149
+ setIsLoading(false);
150
+ }
151
+ };
152
+
153
+ // Animated styles for the button
154
+ const animatedButtonStyle = useAnimatedStyle(() => {
155
+ const backgroundColor = interpolateColor(
156
+ animationProgress.value,
157
+ [0, 1],
158
+ ['#d169e5', '#FFFFFF']
159
+ );
160
+
161
+ const borderColor = interpolateColor(
162
+ animationProgress.value,
163
+ [0, 1],
164
+ ['#d169e5', '#d169e5']
165
+ );
166
+
167
+ // Add a slight scaling effect during the transition
168
+ const transitionScale = 1 + 0.05 * Math.sin(animationProgress.value * Math.PI);
169
+
170
+ return {
171
+ backgroundColor,
172
+ borderColor,
173
+ borderWidth: 1,
174
+ transform: [
175
+ { scale: scale.value * transitionScale },
176
+ ],
177
+ };
178
+ });
179
+
180
+ // Animated styles for the text
181
+ const animatedTextStyle = useAnimatedStyle(() => {
182
+ const color = interpolateColor(
183
+ animationProgress.value,
184
+ [0, 1],
185
+ ['#FFFFFF', '#d169e5']
186
+ );
187
+
188
+ return {
189
+ color,
190
+ };
191
+ });
192
+
193
+ // Get size-specific styles
194
+ const getSizeStyles = () => {
195
+ switch (size) {
196
+ case 'small':
197
+ return {
198
+ button: {
199
+ paddingVertical: 6,
200
+ paddingHorizontal: 12,
201
+ borderRadius: 12,
202
+ } as ViewStyle,
203
+ text: {
204
+ fontSize: 12,
205
+ } as TextStyle,
206
+ };
207
+ case 'large':
208
+ return {
209
+ button: {
210
+ paddingVertical: 12,
211
+ paddingHorizontal: 24,
212
+ borderRadius: 16,
213
+ } as ViewStyle,
214
+ text: {
215
+ fontSize: 18,
216
+ } as TextStyle,
217
+ };
218
+ default: // medium
219
+ return {
220
+ button: {
221
+ paddingVertical: 8,
222
+ paddingHorizontal: 16,
223
+ borderRadius: 14,
224
+ } as ViewStyle,
225
+ text: {
226
+ fontSize: 14,
227
+ } as TextStyle,
228
+ };
229
+ }
230
+ };
231
+
232
+ const sizeStyles = getSizeStyles();
233
+
234
+ return (
235
+ <TouchableOpacity
236
+ activeOpacity={0.8}
237
+ onPress={handlePress}
238
+ disabled={disabled || isLoading || !isAuthenticated}
239
+ >
240
+ <Animated.View
241
+ style={[
242
+ styles.button,
243
+ sizeStyles.button,
244
+ animatedButtonStyle,
245
+ style,
246
+ ]}
247
+ >
248
+ {isLoading && showLoadingState ? (
249
+ <ActivityIndicator
250
+ size="small"
251
+ color={isFollowing ? '#d169e5' : '#FFFFFF'}
252
+ />
253
+ ) : (
254
+ <Animated.Text
255
+ style={[
256
+ styles.text,
257
+ sizeStyles.text,
258
+ animatedTextStyle,
259
+ textStyle,
260
+ ]}
261
+ >
262
+ {isFollowing ? 'Following' : 'Follow'}
263
+ </Animated.Text>
264
+ )}
265
+ </Animated.View>
266
+ </TouchableOpacity>
267
+ );
268
+ };
269
+
270
+ const styles = StyleSheet.create({
271
+ button: {
272
+ justifyContent: 'center',
273
+ alignItems: 'center',
274
+ flexDirection: 'row',
275
+ },
276
+ text: {
277
+ fontFamily: Platform.select({
278
+ web: 'Phudu',
279
+ default: fontFamilies.phuduSemiBold,
280
+ }),
281
+ fontWeight: Platform.OS === 'web' ? '600' : undefined,
282
+ textAlign: 'center',
283
+ },
284
+ });
285
+
286
+ export default FollowButton;
package/src/ui/index.ts CHANGED
@@ -7,6 +7,7 @@ export { default as OxyProvider } from './components/OxyProvider';
7
7
  export { default as OxySignInButton } from './components/OxySignInButton';
8
8
  export { default as OxyLogo } from './components/OxyLogo';
9
9
  export { default as Avatar } from './components/Avatar';
10
+ export { default as FollowButton } from './components/FollowButton';
10
11
  export { FontLoader, setupFonts } from './components/FontLoader';
11
12
  export {
12
13
  OxyContextProvider,