@oxyhq/services 5.11.8 → 5.11.10
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/lib/commonjs/core/OxyServices.js +104 -10
- package/lib/commonjs/core/OxyServices.js.map +1 -1
- package/lib/commonjs/ui/components/AnimationExample.js +213 -0
- package/lib/commonjs/ui/components/AnimationExample.js.map +1 -0
- package/lib/commonjs/ui/components/FollowButton.js +58 -47
- package/lib/commonjs/ui/components/FollowButton.js.map +1 -1
- package/lib/commonjs/ui/components/GroupedItem.js +2 -1
- package/lib/commonjs/ui/components/GroupedItem.js.map +1 -1
- package/lib/commonjs/ui/components/GroupedSection.js +3 -0
- package/lib/commonjs/ui/components/GroupedSection.js.map +1 -1
- package/lib/commonjs/ui/components/Header.js +25 -11
- package/lib/commonjs/ui/components/Header.js.map +1 -1
- package/lib/commonjs/ui/components/OxyProvider.js +69 -33
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/components/ProfileCard.js +5 -1
- package/lib/commonjs/ui/components/ProfileCard.js.map +1 -1
- package/lib/commonjs/ui/components/index.js +0 -7
- package/lib/commonjs/ui/components/index.js.map +1 -1
- package/lib/commonjs/ui/components/internal/TextField.js +8 -4
- package/lib/commonjs/ui/components/internal/TextField.js.map +1 -1
- package/lib/commonjs/ui/components/photogrid/JustifiedPhotoGrid.js +161 -0
- package/lib/commonjs/ui/components/photogrid/JustifiedPhotoGrid.js.map +1 -0
- package/lib/commonjs/ui/context/OxyContext.js +97 -38
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/hooks/useFollow.types.js +2 -0
- package/lib/commonjs/ui/hooks/useFollow.types.js.map +1 -0
- package/lib/commonjs/ui/navigation/OxyRouter.js +10 -0
- package/lib/commonjs/ui/navigation/OxyRouter.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountCenterScreen.js +26 -14
- package/lib/commonjs/ui/screens/AccountCenterScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountOverviewScreen.js +3 -3
- package/lib/commonjs/ui/screens/AccountOverviewScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js +64 -15
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +4 -4
- package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/FeedbackScreen.js +72 -75
- package/lib/commonjs/ui/screens/FeedbackScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/FileManagementScreen.js +286 -126
- package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/LanguageSelectorScreen.js +322 -0
- package/lib/commonjs/ui/screens/LanguageSelectorScreen.js.map +1 -0
- package/lib/commonjs/ui/screens/ProfileScreen.js +1 -1
- package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SessionManagementScreen.js +176 -174
- package/lib/commonjs/ui/screens/SessionManagementScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SignInScreen.js +43 -52
- package/lib/commonjs/ui/screens/SignInScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SignUpScreen.js +6 -4
- package/lib/commonjs/ui/screens/SignUpScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js +386 -0
- package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js.map +1 -0
- package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js +25 -15
- package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js.map +1 -1
- package/lib/commonjs/ui/screens/internal/SignInUsernameStep.js +16 -9
- package/lib/commonjs/ui/screens/internal/SignInUsernameStep.js.map +1 -1
- package/lib/commonjs/ui/screens/karma/KarmaCenterScreen.js +1 -1
- package/lib/commonjs/ui/screens/karma/KarmaCenterScreen.js.map +1 -1
- package/lib/commonjs/ui/styles/authStyles.js +1 -1
- package/lib/commonjs/ui/styles/authStyles.js.map +1 -1
- package/lib/module/core/OxyServices.js +103 -9
- package/lib/module/core/OxyServices.js.map +1 -1
- package/lib/module/ui/components/AnimationExample.js +209 -0
- package/lib/module/ui/components/AnimationExample.js.map +1 -0
- package/lib/module/ui/components/FollowButton.js +58 -47
- package/lib/module/ui/components/FollowButton.js.map +1 -1
- package/lib/module/ui/components/GroupedItem.js +2 -1
- package/lib/module/ui/components/GroupedItem.js.map +1 -1
- package/lib/module/ui/components/GroupedSection.js +3 -0
- package/lib/module/ui/components/GroupedSection.js.map +1 -1
- package/lib/module/ui/components/Header.js +25 -11
- package/lib/module/ui/components/Header.js.map +1 -1
- package/lib/module/ui/components/OxyProvider.js +70 -34
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/components/ProfileCard.js +5 -1
- package/lib/module/ui/components/ProfileCard.js.map +1 -1
- package/lib/module/ui/components/index.js +0 -1
- package/lib/module/ui/components/index.js.map +1 -1
- package/lib/module/ui/components/internal/TextField.js +8 -4
- package/lib/module/ui/components/internal/TextField.js.map +1 -1
- package/lib/module/ui/components/photogrid/JustifiedPhotoGrid.js +156 -0
- package/lib/module/ui/components/photogrid/JustifiedPhotoGrid.js.map +1 -0
- package/lib/module/ui/context/OxyContext.js +97 -39
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/hooks/useFollow.types.js +2 -0
- package/lib/module/ui/hooks/useFollow.types.js.map +1 -0
- package/lib/module/ui/navigation/OxyRouter.js +10 -0
- package/lib/module/ui/navigation/OxyRouter.js.map +1 -1
- package/lib/module/ui/screens/AccountCenterScreen.js +12 -1
- package/lib/module/ui/screens/AccountCenterScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountOverviewScreen.js +3 -3
- package/lib/module/ui/screens/AccountOverviewScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSettingsScreen.js +64 -15
- package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSwitcherScreen.js +4 -4
- package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
- package/lib/module/ui/screens/FeedbackScreen.js +72 -75
- package/lib/module/ui/screens/FeedbackScreen.js.map +1 -1
- package/lib/module/ui/screens/FileManagementScreen.js +285 -125
- package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/LanguageSelectorScreen.js +319 -0
- package/lib/module/ui/screens/LanguageSelectorScreen.js.map +1 -0
- package/lib/module/ui/screens/ProfileScreen.js +1 -1
- package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/module/ui/screens/SessionManagementScreen.js +177 -175
- package/lib/module/ui/screens/SessionManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/SignInScreen.js +44 -53
- package/lib/module/ui/screens/SignInScreen.js.map +1 -1
- package/lib/module/ui/screens/SignUpScreen.js +6 -4
- package/lib/module/ui/screens/SignUpScreen.js.map +1 -1
- package/lib/module/ui/screens/WelcomeNewUserScreen.js +382 -0
- package/lib/module/ui/screens/WelcomeNewUserScreen.js.map +1 -0
- package/lib/module/ui/screens/internal/SignInPasswordStep.js +23 -14
- package/lib/module/ui/screens/internal/SignInPasswordStep.js.map +1 -1
- package/lib/module/ui/screens/internal/SignInUsernameStep.js +15 -9
- package/lib/module/ui/screens/internal/SignInUsernameStep.js.map +1 -1
- package/lib/module/ui/screens/karma/KarmaCenterScreen.js +1 -1
- package/lib/module/ui/screens/karma/KarmaCenterScreen.js.map +1 -1
- package/lib/module/ui/styles/authStyles.js +1 -1
- package/lib/module/ui/styles/authStyles.js.map +1 -1
- package/lib/typescript/core/OxyServices.d.ts +95 -4
- package/lib/typescript/core/OxyServices.d.ts.map +1 -1
- package/lib/typescript/models/interfaces.d.ts +1 -5
- package/lib/typescript/models/interfaces.d.ts.map +1 -1
- package/lib/typescript/models/session.d.ts +1 -4
- package/lib/typescript/models/session.d.ts.map +1 -1
- package/lib/typescript/ui/components/AnimationExample.d.ts +4 -0
- package/lib/typescript/ui/components/AnimationExample.d.ts.map +1 -0
- package/lib/typescript/ui/components/FollowButton.d.ts.map +1 -1
- package/lib/typescript/ui/components/GroupedItem.d.ts.map +1 -1
- package/lib/typescript/ui/components/Header.d.ts +9 -0
- package/lib/typescript/ui/components/Header.d.ts.map +1 -1
- package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/ui/components/ProfileCard.d.ts +1 -3
- package/lib/typescript/ui/components/ProfileCard.d.ts.map +1 -1
- package/lib/typescript/ui/components/index.d.ts +0 -1
- package/lib/typescript/ui/components/index.d.ts.map +1 -1
- package/lib/typescript/ui/components/internal/TextField.d.ts.map +1 -1
- package/lib/typescript/ui/components/photogrid/JustifiedPhotoGrid.d.ts +27 -0
- package/lib/typescript/ui/components/photogrid/JustifiedPhotoGrid.d.ts.map +1 -0
- package/lib/typescript/ui/context/OxyContext.d.ts +6 -2
- package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useFollow.types.d.ts +33 -0
- package/lib/typescript/ui/hooks/useFollow.types.d.ts.map +1 -0
- package/lib/typescript/ui/navigation/OxyRouter.d.ts.map +1 -1
- package/lib/typescript/ui/navigation/types.d.ts +5 -0
- package/lib/typescript/ui/navigation/types.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountCenterScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/FeedbackScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/FileManagementScreen.d.ts +18 -1
- package/lib/typescript/ui/screens/FileManagementScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/LanguageSelectorScreen.d.ts +7 -0
- package/lib/typescript/ui/screens/LanguageSelectorScreen.d.ts.map +1 -0
- package/lib/typescript/ui/screens/ProfileScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/SessionManagementScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/SignInScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/SignUpScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/WelcomeNewUserScreen.d.ts +13 -0
- package/lib/typescript/ui/screens/WelcomeNewUserScreen.d.ts.map +1 -0
- package/lib/typescript/ui/screens/internal/SignInPasswordStep.d.ts +5 -5
- package/lib/typescript/ui/screens/internal/SignInPasswordStep.d.ts.map +1 -1
- package/lib/typescript/ui/screens/internal/SignInUsernameStep.d.ts +4 -4
- package/lib/typescript/ui/screens/internal/SignInUsernameStep.d.ts.map +1 -1
- package/lib/typescript/ui/styles/authStyles.d.ts +1 -1
- package/package.json +10 -2
- package/src/core/OxyServices.ts +107 -13
- package/src/models/interfaces.ts +2 -5
- package/src/models/session.ts +1 -4
- package/src/ui/components/AnimationExample.tsx +194 -0
- package/src/ui/components/FollowButton.tsx +65 -45
- package/src/ui/components/GroupedItem.tsx +1 -0
- package/src/ui/components/GroupedSection.tsx +1 -1
- package/src/ui/components/Header.tsx +36 -12
- package/src/ui/components/OxyProvider.tsx +66 -32
- package/src/ui/components/ProfileCard.tsx +6 -8
- package/src/ui/components/index.ts +0 -1
- package/src/ui/components/internal/TextField.tsx +12 -6
- package/src/ui/components/photogrid/JustifiedPhotoGrid.tsx +158 -0
- package/src/ui/context/OxyContext.tsx +84 -54
- package/src/ui/hooks/useFollow.types.ts +33 -0
- package/src/ui/navigation/OxyRouter.tsx +10 -0
- package/src/ui/navigation/types.ts +6 -0
- package/src/ui/screens/AccountCenterScreen.tsx +13 -7
- package/src/ui/screens/AccountOverviewScreen.tsx +3 -3
- package/src/ui/screens/AccountSettingsScreen.tsx +65 -13
- package/src/ui/screens/AccountSwitcherScreen.tsx +4 -4
- package/src/ui/screens/FeedbackScreen.tsx +57 -80
- package/src/ui/screens/FileManagementScreen.tsx +278 -175
- package/src/ui/screens/LanguageSelectorScreen.tsx +322 -0
- package/src/ui/screens/ProfileScreen.tsx +6 -1
- package/src/ui/screens/SessionManagementScreen.tsx +148 -151
- package/src/ui/screens/SignInScreen.tsx +43 -62
- package/src/ui/screens/SignUpScreen.tsx +3 -5
- package/src/ui/screens/WelcomeNewUserScreen.tsx +272 -0
- package/src/ui/screens/internal/SignInPasswordStep.tsx +28 -13
- package/src/ui/screens/internal/SignInUsernameStep.tsx +21 -11
- package/src/ui/screens/karma/KarmaCenterScreen.tsx +1 -1
- package/src/ui/styles/authStyles.ts +1 -1
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
|
|
3
|
+
import Animated, {
|
|
4
|
+
useSharedValue,
|
|
5
|
+
useAnimatedStyle,
|
|
6
|
+
withTiming,
|
|
7
|
+
withSpring,
|
|
8
|
+
withSequence,
|
|
9
|
+
withDelay,
|
|
10
|
+
interpolateColor,
|
|
11
|
+
runOnJS,
|
|
12
|
+
Easing,
|
|
13
|
+
} from 'react-native-reanimated';
|
|
14
|
+
|
|
15
|
+
// Example component showcasing improved Reanimated usage
|
|
16
|
+
const AnimationExample: React.FC = () => {
|
|
17
|
+
const [currentStep, setCurrentStep] = useState(0);
|
|
18
|
+
|
|
19
|
+
// Shared values for better performance
|
|
20
|
+
const opacity = useSharedValue(1);
|
|
21
|
+
const scale = useSharedValue(1);
|
|
22
|
+
const translateX = useSharedValue(0);
|
|
23
|
+
const rotation = useSharedValue(0);
|
|
24
|
+
const progress = useSharedValue(0);
|
|
25
|
+
const colorProgress = useSharedValue(0);
|
|
26
|
+
|
|
27
|
+
// Animated styles with proper interpolation
|
|
28
|
+
const animatedStyle = useAnimatedStyle(() => {
|
|
29
|
+
return {
|
|
30
|
+
opacity: opacity.value,
|
|
31
|
+
transform: [
|
|
32
|
+
{ scale: scale.value },
|
|
33
|
+
{ translateX: translateX.value },
|
|
34
|
+
{ rotate: `${rotation.value}deg` },
|
|
35
|
+
],
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const progressStyle = useAnimatedStyle(() => {
|
|
40
|
+
return {
|
|
41
|
+
width: `${progress.value * 100}%`,
|
|
42
|
+
backgroundColor: interpolateColor(
|
|
43
|
+
colorProgress.value,
|
|
44
|
+
[0, 1],
|
|
45
|
+
['#3498db', '#e74c3c']
|
|
46
|
+
),
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const backgroundStyle = useAnimatedStyle(() => {
|
|
51
|
+
return {
|
|
52
|
+
backgroundColor: interpolateColor(
|
|
53
|
+
colorProgress.value,
|
|
54
|
+
[0, 1],
|
|
55
|
+
['#ecf0f1', '#f39c12']
|
|
56
|
+
),
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Complex animation sequence
|
|
61
|
+
const animateSequence = () => {
|
|
62
|
+
'worklet';
|
|
63
|
+
|
|
64
|
+
// Staggered animations for smooth transitions
|
|
65
|
+
opacity.value = withTiming(0.5, { duration: 200 });
|
|
66
|
+
scale.value = withSpring(0.8, { damping: 15, stiffness: 150 });
|
|
67
|
+
|
|
68
|
+
// Delayed follow-up animations
|
|
69
|
+
translateX.value = withDelay(
|
|
70
|
+
100,
|
|
71
|
+
withSpring(50, { damping: 20, stiffness: 100 }, (finished) => {
|
|
72
|
+
if (finished) {
|
|
73
|
+
translateX.value = withSpring(0, { damping: 15, stiffness: 150 });
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// Sequential animations
|
|
79
|
+
rotation.value = withSequence(
|
|
80
|
+
withTiming(10, { duration: 150 }),
|
|
81
|
+
withTiming(-10, { duration: 150 }),
|
|
82
|
+
withTiming(0, { duration: 150 })
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// Progress animation with easing
|
|
86
|
+
progress.value = withTiming(1, {
|
|
87
|
+
duration: 1000,
|
|
88
|
+
easing: Easing.out(Easing.exp)
|
|
89
|
+
}, (finished) => {
|
|
90
|
+
if (finished) {
|
|
91
|
+
runOnJS(setCurrentStep)(currentStep + 1);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Color transition
|
|
96
|
+
colorProgress.value = withTiming(1, { duration: 800 });
|
|
97
|
+
|
|
98
|
+
// Reset animations
|
|
99
|
+
setTimeout(() => {
|
|
100
|
+
opacity.value = withSpring(1);
|
|
101
|
+
scale.value = withSpring(1);
|
|
102
|
+
progress.value = withTiming(0, { duration: 500 });
|
|
103
|
+
colorProgress.value = withTiming(0, { duration: 500 });
|
|
104
|
+
}, 1500);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<Animated.View style={[styles.container, backgroundStyle]}>
|
|
109
|
+
<Text style={styles.title}>Advanced Reanimated Example</Text>
|
|
110
|
+
<Text style={styles.subtitle}>Step: {currentStep}</Text>
|
|
111
|
+
|
|
112
|
+
<Animated.View style={[styles.box, animatedStyle]}>
|
|
113
|
+
<Text style={styles.boxText}>Animated Box</Text>
|
|
114
|
+
</Animated.View>
|
|
115
|
+
|
|
116
|
+
<View style={styles.progressContainer}>
|
|
117
|
+
<Animated.View style={[styles.progressBar, progressStyle]} />
|
|
118
|
+
</View>
|
|
119
|
+
|
|
120
|
+
<TouchableOpacity style={styles.button} onPress={animateSequence}>
|
|
121
|
+
<Text style={styles.buttonText}>Animate Sequence</Text>
|
|
122
|
+
</TouchableOpacity>
|
|
123
|
+
</Animated.View>
|
|
124
|
+
);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const styles = StyleSheet.create({
|
|
128
|
+
container: {
|
|
129
|
+
flex: 1,
|
|
130
|
+
justifyContent: 'center',
|
|
131
|
+
alignItems: 'center',
|
|
132
|
+
padding: 20,
|
|
133
|
+
},
|
|
134
|
+
title: {
|
|
135
|
+
fontSize: 24,
|
|
136
|
+
fontWeight: 'bold',
|
|
137
|
+
marginBottom: 10,
|
|
138
|
+
color: '#2c3e50',
|
|
139
|
+
},
|
|
140
|
+
subtitle: {
|
|
141
|
+
fontSize: 16,
|
|
142
|
+
marginBottom: 30,
|
|
143
|
+
color: '#7f8c8d',
|
|
144
|
+
},
|
|
145
|
+
box: {
|
|
146
|
+
width: 150,
|
|
147
|
+
height: 150,
|
|
148
|
+
backgroundColor: '#3498db',
|
|
149
|
+
borderRadius: 20,
|
|
150
|
+
justifyContent: 'center',
|
|
151
|
+
alignItems: 'center',
|
|
152
|
+
marginBottom: 30,
|
|
153
|
+
shadowColor: '#000',
|
|
154
|
+
shadowOffset: { width: 0, height: 4 },
|
|
155
|
+
shadowOpacity: 0.3,
|
|
156
|
+
shadowRadius: 8,
|
|
157
|
+
elevation: 8,
|
|
158
|
+
},
|
|
159
|
+
boxText: {
|
|
160
|
+
color: 'white',
|
|
161
|
+
fontSize: 16,
|
|
162
|
+
fontWeight: 'bold',
|
|
163
|
+
},
|
|
164
|
+
progressContainer: {
|
|
165
|
+
width: '100%',
|
|
166
|
+
height: 10,
|
|
167
|
+
backgroundColor: '#ecf0f1',
|
|
168
|
+
borderRadius: 5,
|
|
169
|
+
marginBottom: 30,
|
|
170
|
+
overflow: 'hidden',
|
|
171
|
+
},
|
|
172
|
+
progressBar: {
|
|
173
|
+
height: '100%',
|
|
174
|
+
borderRadius: 5,
|
|
175
|
+
},
|
|
176
|
+
button: {
|
|
177
|
+
backgroundColor: '#e74c3c',
|
|
178
|
+
paddingHorizontal: 30,
|
|
179
|
+
paddingVertical: 15,
|
|
180
|
+
borderRadius: 25,
|
|
181
|
+
shadowColor: '#000',
|
|
182
|
+
shadowOffset: { width: 0, height: 2 },
|
|
183
|
+
shadowOpacity: 0.2,
|
|
184
|
+
shadowRadius: 4,
|
|
185
|
+
elevation: 4,
|
|
186
|
+
},
|
|
187
|
+
buttonText: {
|
|
188
|
+
color: 'white',
|
|
189
|
+
fontSize: 16,
|
|
190
|
+
fontWeight: 'bold',
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
export default AnimationExample;
|
|
@@ -24,6 +24,10 @@ import { toast } from '../../lib/sonner';
|
|
|
24
24
|
import { useFollow } from '../hooks/useFollow';
|
|
25
25
|
import { useThemeColors } from '../styles/theme';
|
|
26
26
|
|
|
27
|
+
// Create animated TouchableOpacity
|
|
28
|
+
const AnimatedTouchableOpacity = Animated.createAnimatedComponent(TouchableOpacity);
|
|
29
|
+
const AnimatedText = Animated.createAnimatedComponent(Text);
|
|
30
|
+
|
|
27
31
|
export interface FollowButtonProps {
|
|
28
32
|
userId: string;
|
|
29
33
|
initiallyFollowing?: boolean;
|
|
@@ -65,6 +69,30 @@ const FollowButton: React.FC<FollowButtonProps> = ({
|
|
|
65
69
|
const animationProgress = useSharedValue(isFollowing ? 1 : 0);
|
|
66
70
|
const scale = useSharedValue(1);
|
|
67
71
|
|
|
72
|
+
// Button press handler with animation
|
|
73
|
+
const handlePress = useCallback(async (event?: { preventDefault?: () => void; stopPropagation?: () => void }) => {
|
|
74
|
+
if (preventParentActions && event && event.preventDefault) {
|
|
75
|
+
event.preventDefault();
|
|
76
|
+
event.stopPropagation?.();
|
|
77
|
+
}
|
|
78
|
+
if (disabled || isLoading) return;
|
|
79
|
+
|
|
80
|
+
// Press animation
|
|
81
|
+
scale.value = withTiming(0.95, { duration: 100 }, (finished) => {
|
|
82
|
+
if (finished) {
|
|
83
|
+
scale.value = withSpring(1, { damping: 15, stiffness: 200 });
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
await toggleFollow?.();
|
|
89
|
+
if (onFollowChange) onFollowChange(!isFollowing);
|
|
90
|
+
} catch (err: unknown) {
|
|
91
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
92
|
+
toast.error(error.message || 'Failed to update follow status');
|
|
93
|
+
}
|
|
94
|
+
}, [disabled, isLoading, toggleFollow, onFollowChange, isFollowing, preventParentActions, scale]);
|
|
95
|
+
|
|
68
96
|
// Initialize Zustand state with initial value if not already set
|
|
69
97
|
useEffect(() => {
|
|
70
98
|
if (userId && !isFollowing && initiallyFollowing) {
|
|
@@ -86,24 +114,35 @@ const FollowButton: React.FC<FollowButtonProps> = ({
|
|
|
86
114
|
animationProgress.value = withTiming(isFollowing ? 1 : 0, { duration: 300, easing: Easing.inOut(Easing.ease) });
|
|
87
115
|
}, [isFollowing, animationProgress]);
|
|
88
116
|
|
|
89
|
-
//
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
117
|
+
// Animated styles for better performance
|
|
118
|
+
const animatedButtonStyle = useAnimatedStyle(() => {
|
|
119
|
+
return {
|
|
120
|
+
transform: [{ scale: scale.value }],
|
|
121
|
+
backgroundColor: interpolateColor(
|
|
122
|
+
animationProgress.value,
|
|
123
|
+
[0, 1],
|
|
124
|
+
[colors.background, colors.primary]
|
|
125
|
+
),
|
|
126
|
+
borderColor: interpolateColor(
|
|
127
|
+
animationProgress.value,
|
|
128
|
+
[0, 1],
|
|
129
|
+
[colors.border, colors.primary]
|
|
130
|
+
),
|
|
131
|
+
};
|
|
132
|
+
}, [colors]);
|
|
133
|
+
|
|
134
|
+
const animatedTextStyle = useAnimatedStyle(() => {
|
|
135
|
+
return {
|
|
136
|
+
color: interpolateColor(
|
|
137
|
+
animationProgress.value,
|
|
138
|
+
[0, 1],
|
|
139
|
+
[colors.text, '#FFFFFF']
|
|
140
|
+
),
|
|
141
|
+
};
|
|
142
|
+
}, [colors]);
|
|
104
143
|
|
|
105
|
-
// Get button style
|
|
106
|
-
const
|
|
144
|
+
// Get base button style (without state-specific colors since they're animated)
|
|
145
|
+
const getBaseButtonStyle = (): StyleProp<ViewStyle> => {
|
|
107
146
|
const baseStyle = {
|
|
108
147
|
flexDirection: 'row' as const,
|
|
109
148
|
alignItems: 'center' as const,
|
|
@@ -148,27 +187,11 @@ const FollowButton: React.FC<FollowButtonProps> = ({
|
|
|
148
187
|
};
|
|
149
188
|
}
|
|
150
189
|
|
|
151
|
-
|
|
152
|
-
let stateStyle = {};
|
|
153
|
-
if (isFollowing) {
|
|
154
|
-
stateStyle = {
|
|
155
|
-
backgroundColor: colors.primary,
|
|
156
|
-
borderColor: colors.primary,
|
|
157
|
-
shadowColor: colors.primary,
|
|
158
|
-
};
|
|
159
|
-
} else {
|
|
160
|
-
stateStyle = {
|
|
161
|
-
backgroundColor: colors.background,
|
|
162
|
-
borderColor: colors.border,
|
|
163
|
-
shadowColor: colors.border,
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return [baseStyle, sizeStyle, stateStyle, style];
|
|
190
|
+
return [baseStyle, sizeStyle, style];
|
|
168
191
|
};
|
|
169
192
|
|
|
170
|
-
// Get text style
|
|
171
|
-
const
|
|
193
|
+
// Get base text style (without state-specific colors since they're animated)
|
|
194
|
+
const getBaseTextStyle = (): StyleProp<TextStyle> => {
|
|
172
195
|
const baseTextStyle = {
|
|
173
196
|
fontFamily: fontFamilies.phuduSemiBold,
|
|
174
197
|
fontWeight: '600' as const,
|
|
@@ -185,15 +208,12 @@ const FollowButton: React.FC<FollowButtonProps> = ({
|
|
|
185
208
|
sizeTextStyle = { fontSize: 15 };
|
|
186
209
|
}
|
|
187
210
|
|
|
188
|
-
|
|
189
|
-
const textColor = isFollowing ? '#FFFFFF' : colors.text;
|
|
190
|
-
|
|
191
|
-
return [baseTextStyle, sizeTextStyle, { color: textColor }, textStyle];
|
|
211
|
+
return [baseTextStyle, sizeTextStyle, textStyle];
|
|
192
212
|
};
|
|
193
213
|
|
|
194
214
|
return (
|
|
195
|
-
<
|
|
196
|
-
style={
|
|
215
|
+
<AnimatedTouchableOpacity
|
|
216
|
+
style={[getBaseButtonStyle(), animatedButtonStyle]}
|
|
197
217
|
onPress={handlePress}
|
|
198
218
|
disabled={disabled || isLoading}
|
|
199
219
|
activeOpacity={0.8}
|
|
@@ -204,11 +224,11 @@ const FollowButton: React.FC<FollowButtonProps> = ({
|
|
|
204
224
|
color={isFollowing ? '#FFFFFF' : colors.primary}
|
|
205
225
|
/>
|
|
206
226
|
) : (
|
|
207
|
-
<
|
|
227
|
+
<AnimatedText style={[getBaseTextStyle(), animatedTextStyle]}>
|
|
208
228
|
{isFollowing ? 'Following' : 'Follow'}
|
|
209
|
-
</
|
|
229
|
+
</AnimatedText>
|
|
210
230
|
)}
|
|
211
|
-
</
|
|
231
|
+
</AnimatedTouchableOpacity>
|
|
212
232
|
);
|
|
213
233
|
};
|
|
214
234
|
|
|
@@ -22,7 +22,16 @@ export interface HeaderProps {
|
|
|
22
22
|
loading?: boolean;
|
|
23
23
|
disabled?: boolean;
|
|
24
24
|
text?: string;
|
|
25
|
+
key?: string;
|
|
25
26
|
};
|
|
27
|
+
rightActions?: Array<{
|
|
28
|
+
icon?: string;
|
|
29
|
+
onPress: () => void;
|
|
30
|
+
loading?: boolean;
|
|
31
|
+
disabled?: boolean;
|
|
32
|
+
text?: string;
|
|
33
|
+
key?: string; // optional identifier
|
|
34
|
+
}>;
|
|
26
35
|
theme: 'light' | 'dark';
|
|
27
36
|
showBackButton?: boolean;
|
|
28
37
|
showCloseButton?: boolean;
|
|
@@ -38,6 +47,7 @@ const Header: React.FC<HeaderProps> = ({
|
|
|
38
47
|
onBack,
|
|
39
48
|
onClose,
|
|
40
49
|
rightAction,
|
|
50
|
+
rightActions,
|
|
41
51
|
theme,
|
|
42
52
|
showBackButton = true,
|
|
43
53
|
showCloseButton = false,
|
|
@@ -100,26 +110,24 @@ const Header: React.FC<HeaderProps> = ({
|
|
|
100
110
|
);
|
|
101
111
|
};
|
|
102
112
|
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const isTextAction = rightAction.text;
|
|
107
|
-
|
|
113
|
+
const renderRightActionButton = (action: NonNullable<HeaderProps['rightAction']>, idx: number) => {
|
|
114
|
+
const isTextAction = action.text;
|
|
108
115
|
return (
|
|
109
116
|
<TouchableOpacity
|
|
117
|
+
key={action.key || idx}
|
|
110
118
|
style={[
|
|
111
119
|
styles.rightActionButton,
|
|
112
120
|
isTextAction ? styles.textActionButton : styles.iconActionButton,
|
|
113
121
|
{
|
|
114
122
|
backgroundColor: isTextAction ? colors.primary : colors.surface,
|
|
115
|
-
opacity:
|
|
123
|
+
opacity: action.disabled ? 0.5 : 1
|
|
116
124
|
}
|
|
117
125
|
]}
|
|
118
|
-
onPress={
|
|
119
|
-
disabled={
|
|
126
|
+
onPress={action.onPress}
|
|
127
|
+
disabled={action.disabled || action.loading}
|
|
120
128
|
activeOpacity={0.7}
|
|
121
129
|
>
|
|
122
|
-
{
|
|
130
|
+
{action.loading ? (
|
|
123
131
|
<View style={styles.loadingContainer}>
|
|
124
132
|
<View style={[styles.loadingDot, { backgroundColor: isTextAction ? '#FFFFFF' : colors.primary }]} />
|
|
125
133
|
<View style={[styles.loadingDot, { backgroundColor: isTextAction ? '#FFFFFF' : colors.primary }]} />
|
|
@@ -127,15 +135,27 @@ const Header: React.FC<HeaderProps> = ({
|
|
|
127
135
|
</View>
|
|
128
136
|
) : isTextAction ? (
|
|
129
137
|
<Text style={[styles.actionText, { color: '#FFFFFF' }]}>
|
|
130
|
-
{
|
|
138
|
+
{action.text}
|
|
131
139
|
</Text>
|
|
132
140
|
) : (
|
|
133
|
-
<Ionicons name={
|
|
141
|
+
<Ionicons name={action.icon as any} size={18} color={colors.primary} />
|
|
134
142
|
)}
|
|
135
143
|
</TouchableOpacity>
|
|
136
144
|
);
|
|
137
145
|
};
|
|
138
146
|
|
|
147
|
+
const renderRightActions = () => {
|
|
148
|
+
if (rightActions && rightActions.length) {
|
|
149
|
+
return (
|
|
150
|
+
<View style={styles.rightActionsRow}>
|
|
151
|
+
{rightActions.map((a, i) => renderRightActionButton(a, i))}
|
|
152
|
+
</View>
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
if (rightAction) return renderRightActionButton(rightAction, 0);
|
|
156
|
+
return null;
|
|
157
|
+
};
|
|
158
|
+
|
|
139
159
|
const renderTitle = () => {
|
|
140
160
|
const titleStyle = variant === 'large' ? styles.titleLarge :
|
|
141
161
|
variant === 'minimal' ? styles.titleMinimal :
|
|
@@ -245,7 +265,7 @@ const Header: React.FC<HeaderProps> = ({
|
|
|
245
265
|
]}>
|
|
246
266
|
{renderBackButton()}
|
|
247
267
|
{renderTitle()}
|
|
248
|
-
{
|
|
268
|
+
{renderRightActions()}
|
|
249
269
|
{renderCloseButton()}
|
|
250
270
|
</View>
|
|
251
271
|
</View>
|
|
@@ -400,6 +420,10 @@ const styles = StyleSheet.create({
|
|
|
400
420
|
borderRadius: 2,
|
|
401
421
|
opacity: 0.6,
|
|
402
422
|
},
|
|
423
|
+
rightActionsRow: {
|
|
424
|
+
flexDirection: 'row',
|
|
425
|
+
alignItems: 'center',
|
|
426
|
+
},
|
|
403
427
|
});
|
|
404
428
|
|
|
405
429
|
export default Header;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
2
|
import { useCallback, useRef, useState, useEffect, useMemo } from 'react';
|
|
3
|
-
import { View, Text, StyleSheet, Dimensions, Platform, Animated, StatusBar, Keyboard, KeyboardEvent } from 'react-native';
|
|
3
|
+
import { View, Text, StyleSheet, Dimensions, Platform, Animated, StatusBar, Keyboard, KeyboardEvent, AppState } from 'react-native';
|
|
4
4
|
import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
5
5
|
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
6
6
|
import type { OxyProviderProps } from '../navigation/types';
|
|
@@ -8,6 +8,7 @@ import { OxyContextProvider, useOxy } from '../context/OxyContext';
|
|
|
8
8
|
import OxyRouter from '../navigation/OxyRouter';
|
|
9
9
|
import { FontLoader, setupFonts } from './FontLoader';
|
|
10
10
|
import { Toaster } from '../../lib/sonner';
|
|
11
|
+
import { QueryClient, QueryClientProvider, focusManager } from '@tanstack/react-query';
|
|
11
12
|
|
|
12
13
|
// Import bottom sheet components directly - no longer a peer dependency
|
|
13
14
|
import { BottomSheetModal, BottomSheetBackdrop, type BottomSheetBackdropProps, BottomSheetModalProvider, BottomSheetView, BottomSheetScrollView } from '@gorhom/bottom-sheet';
|
|
@@ -38,47 +39,80 @@ const OxyProvider: React.FC<OxyProviderProps> = (props) => {
|
|
|
38
39
|
// Create internal bottom sheet ref
|
|
39
40
|
const internalBottomSheetRef = useRef<BottomSheetModalRef>(null);
|
|
40
41
|
|
|
42
|
+
// Initialize React Query Client (use provided client or create a default one once)
|
|
43
|
+
const queryClientRef = useRef<QueryClient | null>(null);
|
|
44
|
+
if (!queryClientRef.current) {
|
|
45
|
+
queryClientRef.current = props.queryClient ?? new QueryClient({
|
|
46
|
+
defaultOptions: {
|
|
47
|
+
queries: {
|
|
48
|
+
staleTime: 30_000,
|
|
49
|
+
gcTime: 5 * 60_000,
|
|
50
|
+
retry: 2,
|
|
51
|
+
refetchOnReconnect: true,
|
|
52
|
+
refetchOnWindowFocus: false,
|
|
53
|
+
},
|
|
54
|
+
mutations: {
|
|
55
|
+
retry: 1,
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Hook React Query focus manager into React Native AppState
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
const subscription = AppState.addEventListener('change', (state) => {
|
|
64
|
+
focusManager.setFocused(state === 'active');
|
|
65
|
+
});
|
|
66
|
+
return () => {
|
|
67
|
+
subscription.remove();
|
|
68
|
+
};
|
|
69
|
+
}, []);
|
|
70
|
+
|
|
41
71
|
// If contextOnly is true, we just provide the context without the bottom sheet UI
|
|
42
72
|
if (contextOnly) {
|
|
43
73
|
return (
|
|
74
|
+
<QueryClientProvider client={queryClientRef.current}>
|
|
75
|
+
<OxyContextProvider
|
|
76
|
+
oxyServices={oxyServices}
|
|
77
|
+
baseURL={baseURL}
|
|
78
|
+
storageKeyPrefix={storageKeyPrefix}
|
|
79
|
+
onAuthStateChange={onAuthStateChange}
|
|
80
|
+
>
|
|
81
|
+
{children}
|
|
82
|
+
</OxyContextProvider>
|
|
83
|
+
</QueryClientProvider>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Otherwise, provide both the context and the bottom sheet UI
|
|
88
|
+
return (
|
|
89
|
+
<QueryClientProvider client={queryClientRef.current}>
|
|
44
90
|
<OxyContextProvider
|
|
45
91
|
oxyServices={oxyServices}
|
|
46
92
|
baseURL={baseURL}
|
|
47
93
|
storageKeyPrefix={storageKeyPrefix}
|
|
48
94
|
onAuthStateChange={onAuthStateChange}
|
|
95
|
+
bottomSheetRef={internalBottomSheetRef}
|
|
49
96
|
>
|
|
50
|
-
|
|
97
|
+
<FontLoader>
|
|
98
|
+
<GestureHandlerRootView style={styles.gestureHandlerRoot}>
|
|
99
|
+
<BottomSheetModalProvider>
|
|
100
|
+
<StatusBar translucent backgroundColor="transparent" />
|
|
101
|
+
<SafeAreaProvider>
|
|
102
|
+
<OxyBottomSheet {...bottomSheetProps} bottomSheetRef={internalBottomSheetRef} oxyServices={oxyServices} />
|
|
103
|
+
{children}
|
|
104
|
+
</SafeAreaProvider>
|
|
105
|
+
</BottomSheetModalProvider>
|
|
106
|
+
{/* Global Toaster for app-wide notifications outside of Modal contexts - only show if internal toaster is disabled */}
|
|
107
|
+
{!showInternalToaster && (
|
|
108
|
+
<View style={styles.toasterContainer}>
|
|
109
|
+
<Toaster position="top-center" swipeToDismissDirection="left" offset={15} />
|
|
110
|
+
</View>
|
|
111
|
+
)}
|
|
112
|
+
</GestureHandlerRootView>
|
|
113
|
+
</FontLoader>
|
|
51
114
|
</OxyContextProvider>
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Otherwise, provide both the context and the bottom sheet UI
|
|
56
|
-
return (
|
|
57
|
-
<OxyContextProvider
|
|
58
|
-
oxyServices={oxyServices}
|
|
59
|
-
baseURL={baseURL}
|
|
60
|
-
storageKeyPrefix={storageKeyPrefix}
|
|
61
|
-
onAuthStateChange={onAuthStateChange}
|
|
62
|
-
bottomSheetRef={internalBottomSheetRef}
|
|
63
|
-
>
|
|
64
|
-
<FontLoader>
|
|
65
|
-
<GestureHandlerRootView style={styles.gestureHandlerRoot}>
|
|
66
|
-
<BottomSheetModalProvider>
|
|
67
|
-
<StatusBar translucent backgroundColor="transparent" />
|
|
68
|
-
<SafeAreaProvider>
|
|
69
|
-
<OxyBottomSheet {...bottomSheetProps} bottomSheetRef={internalBottomSheetRef} oxyServices={oxyServices} />
|
|
70
|
-
{children}
|
|
71
|
-
</SafeAreaProvider>
|
|
72
|
-
</BottomSheetModalProvider>
|
|
73
|
-
{/* Global Toaster for app-wide notifications outside of Modal contexts - only show if internal toaster is disabled */}
|
|
74
|
-
{!showInternalToaster && (
|
|
75
|
-
<View style={styles.toasterContainer}>
|
|
76
|
-
<Toaster position="top-center" swipeToDismissDirection="left" offset={15} />
|
|
77
|
-
</View>
|
|
78
|
-
)}
|
|
79
|
-
</GestureHandlerRootView>
|
|
80
|
-
</FontLoader>
|
|
81
|
-
</OxyContextProvider>
|
|
115
|
+
</QueryClientProvider>
|
|
82
116
|
);
|
|
83
117
|
};
|
|
84
118
|
|
|
@@ -2,18 +2,15 @@ import type React from 'react';
|
|
|
2
2
|
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
|
|
3
3
|
import { Ionicons } from '@expo/vector-icons';
|
|
4
4
|
import Avatar from './Avatar';
|
|
5
|
+
import { useOxy } from '../context/OxyContext';
|
|
5
6
|
import { fontFamilies } from '../styles/fonts';
|
|
6
7
|
|
|
7
8
|
interface ProfileCardProps {
|
|
8
9
|
user: {
|
|
9
10
|
username: string;
|
|
10
11
|
email?: string;
|
|
11
|
-
name?: {
|
|
12
|
-
|
|
13
|
-
};
|
|
14
|
-
avatar?: {
|
|
15
|
-
url?: string;
|
|
16
|
-
};
|
|
12
|
+
name?: { full?: string };
|
|
13
|
+
avatar?: string; // file id
|
|
17
14
|
};
|
|
18
15
|
theme: 'light' | 'dark';
|
|
19
16
|
onEditPress?: () => void;
|
|
@@ -29,6 +26,7 @@ const ProfileCard: React.FC<ProfileCardProps> = ({
|
|
|
29
26
|
showCloseButton = false,
|
|
30
27
|
}) => {
|
|
31
28
|
const isDarkTheme = theme === 'dark';
|
|
29
|
+
const { oxyServices } = useOxy();
|
|
32
30
|
const textColor = isDarkTheme ? '#FFFFFF' : '#000000';
|
|
33
31
|
const secondaryBackgroundColor = isDarkTheme ? '#222222' : '#FFFFFF';
|
|
34
32
|
const primaryColor = '#0066CC';
|
|
@@ -43,7 +41,7 @@ const ProfileCard: React.FC<ProfileCardProps> = ({
|
|
|
43
41
|
]}>
|
|
44
42
|
<View style={styles.userProfile}>
|
|
45
43
|
<Avatar
|
|
46
|
-
uri={user?.avatar
|
|
44
|
+
uri={user?.avatar ? oxyServices.getFileDownloadUrl(user.avatar as string, 'thumb') : undefined}
|
|
47
45
|
name={user?.name?.full || user?.username}
|
|
48
46
|
size={60}
|
|
49
47
|
theme={theme}
|
|
@@ -56,7 +54,7 @@ const ProfileCard: React.FC<ProfileCardProps> = ({
|
|
|
56
54
|
</Text>
|
|
57
55
|
)}
|
|
58
56
|
{onEditPress && (
|
|
59
|
-
<TouchableOpacity
|
|
57
|
+
<TouchableOpacity
|
|
60
58
|
style={styles.editProfileButton}
|
|
61
59
|
onPress={onEditPress}
|
|
62
60
|
>
|
|
@@ -12,6 +12,5 @@ export { default as Avatar } from './Avatar';
|
|
|
12
12
|
export { default as FollowButton } from './FollowButton';
|
|
13
13
|
export { FontLoader, setupFonts } from './FontLoader';
|
|
14
14
|
export { default as OxyLogo } from './OxyLogo';
|
|
15
|
-
export { default as OxyProvider } from './OxyProvider';
|
|
16
15
|
export { default as OxySignInButton } from './OxySignInButton';
|
|
17
16
|
export { default as OxyPayButton } from './OxyPayButton';
|