@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 +54 -0
- package/lib/commonjs/ui/components/FollowButton.js +198 -0
- package/lib/commonjs/ui/components/FollowButton.js.map +1 -0
- package/lib/commonjs/ui/index.js +8 -0
- package/lib/commonjs/ui/index.js.map +1 -1
- package/lib/module/ui/components/FollowButton.js +193 -0
- package/lib/module/ui/components/FollowButton.js.map +1 -0
- package/lib/module/ui/index.js +1 -0
- package/lib/module/ui/index.js.map +1 -1
- package/lib/typescript/ui/components/FollowButton.d.ts +61 -0
- package/lib/typescript/ui/components/FollowButton.d.ts.map +1 -0
- package/lib/typescript/ui/index.d.ts +1 -0
- package/lib/typescript/ui/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ui/components/FollowButton.tsx +286 -0
- package/src/ui/index.ts +1 -0
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":[]}
|
package/lib/commonjs/ui/index.js
CHANGED
|
@@ -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":"
|
|
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":[]}
|
package/lib/module/ui/index.js
CHANGED
|
@@ -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,
|
|
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
|
@@ -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,
|