app-expo-cli 1.0.0
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/bin/index.js +3 -0
- package/bun.lock +82 -0
- package/package.json +26 -0
- package/src/copy-template.js +156 -0
- package/src/create-expo.js +10 -0
- package/src/index.js +39 -0
- package/src/install-deps.js +22 -0
- package/src/utils/logger.js +29 -0
- package/tailwind.config.js +69 -0
- package/template/src/app/_layout.tsx +61 -0
- package/template/src/app/auth/_layout.tsx +41 -0
- package/template/src/app/auth/change_pass.tsx +138 -0
- package/template/src/app/auth/change_pass_modal.tsx +74 -0
- package/template/src/app/auth/forgot.tsx +124 -0
- package/template/src/app/auth/index.tsx +274 -0
- package/template/src/app/auth/opt_verify.tsx +145 -0
- package/template/src/app/auth/register.tsx +334 -0
- package/template/src/app/auth/reset_pass.tsx +152 -0
- package/template/src/app/common/image.tsx +202 -0
- package/template/src/app/common/openurl.tsx +42 -0
- package/template/src/app/home/_layout.tsx +29 -0
- package/template/src/app/home/drawer/_layout.tsx +27 -0
- package/template/src/app/home/tabs/_layout.tsx +75 -0
- package/template/src/app/home/tabs/index.tsx +11 -0
- package/template/src/app/index.tsx +11 -0
- package/template/src/app/modals/confirmation_logout_modal.tsx +78 -0
- package/template/src/app/modals/payment_modal.tsx +105 -0
- package/template/src/app/modals/success_modal.tsx +72 -0
- package/template/src/app/modals/toaster.tsx +31 -0
- package/template/src/app/settings/_layout.tsx +19 -0
- package/template/src/app/settings/about_us.tsx +61 -0
- package/template/src/app/settings/privacy_policy.tsx +61 -0
- package/template/src/app/settings/terms_and_conditions.tsx +60 -0
- package/template/src/hooks/useCheckLocation.ts +36 -0
- package/template/src/hooks/useDocPicker.ts +83 -0
- package/template/src/hooks/useImgePicker.ts +70 -0
- package/template/src/hooks/useSuggestionLocation.ts +36 -0
- package/template/src/hooks/useUploadProgress.ts +127 -0
- package/template/src/redux/api-config/baseApi.ts +89 -0
- package/template/src/redux/api-slices/authSlices.ts +175 -0
- package/template/src/redux/interface/common.ts +19 -0
- package/template/src/redux/interface/interface.ts +196 -0
- package/template/src/redux/interface/tag-types.ts +13 -0
- package/template/src/redux/service/demo.ts +27 -0
- package/template/src/redux/store.ts +17 -0
- package/template/src/utils/utils.ts +27 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { router, useGlobalSearchParams } from "expo-router";
|
|
2
|
+
import React, { useRef } from "react";
|
|
3
|
+
import {
|
|
4
|
+
Dimensions,
|
|
5
|
+
StyleSheet,
|
|
6
|
+
Text,
|
|
7
|
+
TouchableOpacity,
|
|
8
|
+
View,
|
|
9
|
+
} from "react-native";
|
|
10
|
+
import {
|
|
11
|
+
GestureHandlerRootView,
|
|
12
|
+
PanGestureHandler,
|
|
13
|
+
PinchGestureHandler,
|
|
14
|
+
TapGestureHandler,
|
|
15
|
+
} from "react-native-gesture-handler";
|
|
16
|
+
import Animated, {
|
|
17
|
+
useAnimatedGestureHandler,
|
|
18
|
+
useAnimatedStyle,
|
|
19
|
+
useSharedValue,
|
|
20
|
+
withSpring,
|
|
21
|
+
withTiming,
|
|
22
|
+
} from "react-native-reanimated";
|
|
23
|
+
|
|
24
|
+
import tw from "@/src/lib/tailwind";
|
|
25
|
+
import { SvgXml } from "react-native-svg";
|
|
26
|
+
|
|
27
|
+
const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get("window");
|
|
28
|
+
const AnimatedImage = Animated.createAnimatedComponent(Animated.Image);
|
|
29
|
+
|
|
30
|
+
const clamp = (value: number, min: number, max: number) => {
|
|
31
|
+
"worklet";
|
|
32
|
+
return Math.min(Math.max(value, min), max);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const ImageModal = () => {
|
|
36
|
+
const { url }: { url?: string } = useGlobalSearchParams();
|
|
37
|
+
|
|
38
|
+
console.log(url);
|
|
39
|
+
|
|
40
|
+
const scale = useSharedValue(1);
|
|
41
|
+
const translateX = useSharedValue(0);
|
|
42
|
+
const translateY = useSharedValue(0);
|
|
43
|
+
|
|
44
|
+
const pinchRef = useRef(null);
|
|
45
|
+
const panRef = useRef(null);
|
|
46
|
+
const doubleTapRef = useRef(null);
|
|
47
|
+
|
|
48
|
+
const pinchHandler = useAnimatedGestureHandler({
|
|
49
|
+
onStart: (_, ctx: any) => {
|
|
50
|
+
ctx.startScale = scale.value;
|
|
51
|
+
},
|
|
52
|
+
onActive: (event, ctx: any) => {
|
|
53
|
+
scale.value = clamp(ctx.startScale * event.scale, 0.8, 4);
|
|
54
|
+
},
|
|
55
|
+
onEnd: () => {
|
|
56
|
+
if (scale.value < 1) scale.value = withTiming(1);
|
|
57
|
+
if (scale.value > 4) scale.value = withTiming(4);
|
|
58
|
+
|
|
59
|
+
// Adjust translation after pinch to stay in bounds
|
|
60
|
+
const maxX = (SCREEN_WIDTH * scale.value - SCREEN_WIDTH) / 2;
|
|
61
|
+
const maxY = (SCREEN_HEIGHT * scale.value - SCREEN_HEIGHT) / 2;
|
|
62
|
+
|
|
63
|
+
translateX.value = withTiming(clamp(translateX.value, -maxX, maxX));
|
|
64
|
+
translateY.value = withTiming(clamp(translateY.value, -maxY, maxY));
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const panHandler = useAnimatedGestureHandler({
|
|
69
|
+
onStart: (_, ctx: any) => {
|
|
70
|
+
ctx.startX = translateX.value;
|
|
71
|
+
ctx.startY = translateY.value;
|
|
72
|
+
},
|
|
73
|
+
onActive: (event, ctx: any) => {
|
|
74
|
+
const maxX = (SCREEN_WIDTH * scale.value - SCREEN_WIDTH) / 2;
|
|
75
|
+
const maxY = (SCREEN_HEIGHT * scale.value - SCREEN_HEIGHT) / 2;
|
|
76
|
+
|
|
77
|
+
translateX.value = clamp(ctx.startX + event.translationX, -maxX, maxX);
|
|
78
|
+
translateY.value = clamp(ctx.startY + event.translationY, -maxY, maxY);
|
|
79
|
+
},
|
|
80
|
+
onEnd: () => {
|
|
81
|
+
const maxX = (SCREEN_WIDTH * scale.value - SCREEN_WIDTH) / 2;
|
|
82
|
+
const maxY = (SCREEN_HEIGHT * scale.value - SCREEN_HEIGHT) / 2;
|
|
83
|
+
|
|
84
|
+
translateX.value = withSpring(clamp(translateX.value, -maxX, maxX));
|
|
85
|
+
translateY.value = withSpring(clamp(translateY.value, -maxY, maxY));
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const doubleTapHandler = useAnimatedGestureHandler({
|
|
90
|
+
onActive: () => {
|
|
91
|
+
if (scale.value > 1.5) {
|
|
92
|
+
scale.value = withTiming(1);
|
|
93
|
+
translateX.value = withTiming(0);
|
|
94
|
+
translateY.value = withTiming(0);
|
|
95
|
+
} else {
|
|
96
|
+
scale.value = withTiming(2);
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
102
|
+
transform: [
|
|
103
|
+
{ translateX: translateX.value },
|
|
104
|
+
{ translateY: translateY.value },
|
|
105
|
+
{ scale: scale.value },
|
|
106
|
+
],
|
|
107
|
+
}));
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<GestureHandlerRootView style={styles.flex}>
|
|
111
|
+
{/* Close Button */}
|
|
112
|
+
<TouchableOpacity
|
|
113
|
+
onPress={() => router.dismiss()}
|
|
114
|
+
style={tw`flex-row items-center gap-3 absolute bottom-4 right-4 z-50`}
|
|
115
|
+
>
|
|
116
|
+
<View
|
|
117
|
+
style={[
|
|
118
|
+
tw`bg-[#3D3D3D] w-12 h-12 justify-center items-center rounded-full `,
|
|
119
|
+
]}
|
|
120
|
+
>
|
|
121
|
+
<SvgXml
|
|
122
|
+
xml={`<svg width="12" height="18" viewBox="0 0 12 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
123
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.477124 9.99984L9.69575 18L12 16.0003L3.9335 9L12 1.99969L9.69575 0L0.477124 8.00016C0.171621 8.26536 0 8.625 0 9C0 9.375 0.171621 9.73464 0.477124 9.99984Z" fill="white"/>
|
|
124
|
+
</svg>
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
`}
|
|
129
|
+
/>
|
|
130
|
+
</View>
|
|
131
|
+
</TouchableOpacity>
|
|
132
|
+
|
|
133
|
+
{/* Gesture Handlers */}
|
|
134
|
+
<PanGestureHandler
|
|
135
|
+
onGestureEvent={panHandler}
|
|
136
|
+
minPointers={1}
|
|
137
|
+
maxPointers={2}
|
|
138
|
+
ref={panRef}
|
|
139
|
+
>
|
|
140
|
+
<Animated.View style={styles.flex}>
|
|
141
|
+
<PinchGestureHandler
|
|
142
|
+
onGestureEvent={pinchHandler}
|
|
143
|
+
ref={pinchRef}
|
|
144
|
+
simultaneousHandlers={[panRef, doubleTapRef]}
|
|
145
|
+
>
|
|
146
|
+
<Animated.View style={styles.flex}>
|
|
147
|
+
<TapGestureHandler
|
|
148
|
+
onGestureEvent={doubleTapHandler}
|
|
149
|
+
numberOfTaps={2}
|
|
150
|
+
ref={doubleTapRef}
|
|
151
|
+
maxDelayMs={300}
|
|
152
|
+
>
|
|
153
|
+
<Animated.View style={styles.imageWrapper}>
|
|
154
|
+
{url ? (
|
|
155
|
+
<AnimatedImage
|
|
156
|
+
source={{ uri: url }}
|
|
157
|
+
style={[styles.image, animatedStyle]}
|
|
158
|
+
resizeMode="contain"
|
|
159
|
+
/>
|
|
160
|
+
) : (
|
|
161
|
+
<View style={styles.placeholder}>
|
|
162
|
+
<Text style={tw`text-white`}>No image URL provided</Text>
|
|
163
|
+
</View>
|
|
164
|
+
)}
|
|
165
|
+
</Animated.View>
|
|
166
|
+
</TapGestureHandler>
|
|
167
|
+
</Animated.View>
|
|
168
|
+
</PinchGestureHandler>
|
|
169
|
+
</Animated.View>
|
|
170
|
+
</PanGestureHandler>
|
|
171
|
+
</GestureHandlerRootView>
|
|
172
|
+
);
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const styles = StyleSheet.create({
|
|
176
|
+
flex: { flex: 1, backgroundColor: tw.color("base") },
|
|
177
|
+
closeRow: {
|
|
178
|
+
position: "absolute",
|
|
179
|
+
top: 50,
|
|
180
|
+
left: 0,
|
|
181
|
+
right: 0,
|
|
182
|
+
zIndex: 10,
|
|
183
|
+
flexDirection: "row",
|
|
184
|
+
justifyContent: "flex-start",
|
|
185
|
+
paddingHorizontal: 10,
|
|
186
|
+
},
|
|
187
|
+
imageWrapper: {
|
|
188
|
+
width: "100%",
|
|
189
|
+
height: "100%",
|
|
190
|
+
alignItems: "center",
|
|
191
|
+
justifyContent: "center",
|
|
192
|
+
},
|
|
193
|
+
image: { width: "100%", height: "100%", backgroundColor: "transparent" },
|
|
194
|
+
placeholder: {
|
|
195
|
+
width: "100%",
|
|
196
|
+
height: "100%",
|
|
197
|
+
justifyContent: "center",
|
|
198
|
+
alignItems: "center",
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
export default ImageModal;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Text, TouchableOpacity, View } from "react-native";
|
|
2
|
+
import { router, useGlobalSearchParams } from "expo-router";
|
|
3
|
+
|
|
4
|
+
import React from "react";
|
|
5
|
+
import { SvgXml } from "react-native-svg";
|
|
6
|
+
import WebView from "react-native-webview";
|
|
7
|
+
import tw from "@/src/lib/tailwind";
|
|
8
|
+
|
|
9
|
+
const OpenUrl = () => {
|
|
10
|
+
const params = useGlobalSearchParams();
|
|
11
|
+
return (
|
|
12
|
+
<View style={tw`flex-1 bg-base`}>
|
|
13
|
+
<TouchableOpacity
|
|
14
|
+
onPress={() => router.dismiss()}
|
|
15
|
+
style={tw`flex-row items-center gap-3 absolute bottom-4 right-4 z-50`}
|
|
16
|
+
>
|
|
17
|
+
<View
|
|
18
|
+
style={[
|
|
19
|
+
tw`bg-[#3D3D3D] w-12 h-12 justify-center items-center rounded-full `,
|
|
20
|
+
]}
|
|
21
|
+
>
|
|
22
|
+
<SvgXml
|
|
23
|
+
xml={`<svg width="12" height="18" viewBox="0 0 12 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
24
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.477124 9.99984L9.69575 18L12 16.0003L3.9335 9L12 1.99969L9.69575 0L0.477124 8.00016C0.171621 8.26536 0 8.625 0 9C0 9.375 0.171621 9.73464 0.477124 9.99984Z" fill="white"/>
|
|
25
|
+
</svg>
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
`}
|
|
30
|
+
/>
|
|
31
|
+
</View>
|
|
32
|
+
</TouchableOpacity>
|
|
33
|
+
{params?.url ? (
|
|
34
|
+
<WebView source={{ uri: params?.url } as any} style={{ flex: 1 }} />
|
|
35
|
+
) : (
|
|
36
|
+
<Text>no url</Text>
|
|
37
|
+
)}
|
|
38
|
+
</View>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export default OpenUrl;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Stack } from "expo-router";
|
|
2
|
+
|
|
3
|
+
export const unstable_settings = {
|
|
4
|
+
initialRouteName: "index",
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export default function RootLayout() {
|
|
8
|
+
// const { top, bottom } = useSafeAreaInsets();
|
|
9
|
+
return (
|
|
10
|
+
<Stack
|
|
11
|
+
screenOptions={{
|
|
12
|
+
headerShown: false,
|
|
13
|
+
// statusBarAnimation: "fade",
|
|
14
|
+
statusBarStyle: "light",
|
|
15
|
+
}}
|
|
16
|
+
>
|
|
17
|
+
<Stack.Screen name="tabs" options={{}} />
|
|
18
|
+
<Stack.Screen name="other_user" options={{}} />
|
|
19
|
+
<Stack.Screen name="post_vibe" options={{}} />
|
|
20
|
+
<Stack.Screen name="post_music" options={{}} />
|
|
21
|
+
<Stack.Screen name="post_podcast" options={{}} />
|
|
22
|
+
<Stack.Screen name="create_mood" options={{}} />
|
|
23
|
+
<Stack.Screen name="view_post" options={{}} />
|
|
24
|
+
<Stack.Screen name="report" options={{}} />
|
|
25
|
+
<Stack.Screen name="report_details" options={{}} />
|
|
26
|
+
<Stack.Screen name="appeal" options={{}} />
|
|
27
|
+
</Stack>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Drawer } from "expo-router/drawer";
|
|
2
|
+
|
|
3
|
+
function CustomDrawerContent(props) {
|
|
4
|
+
return (
|
|
5
|
+
<DrawerContentScrollView {...props}>
|
|
6
|
+
<DrawerItemList {...props} />
|
|
7
|
+
<DrawerItem
|
|
8
|
+
label="Help"
|
|
9
|
+
onPress={() => Linking.openURL("https://mywebsite.com/help")}
|
|
10
|
+
/>
|
|
11
|
+
</DrawerContentScrollView>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default function Layout() {
|
|
16
|
+
return (
|
|
17
|
+
<Drawer>
|
|
18
|
+
<Drawer.Screen
|
|
19
|
+
name="index" // This is the name of the page and must match the url from root
|
|
20
|
+
options={{
|
|
21
|
+
drawerLabel: "Home",
|
|
22
|
+
title: "overview",
|
|
23
|
+
}}
|
|
24
|
+
/>
|
|
25
|
+
</Drawer>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Text, View } from "react-native";
|
|
2
|
+
|
|
3
|
+
import { Tabs } from "expo-router";
|
|
4
|
+
|
|
5
|
+
// Define your route params
|
|
6
|
+
|
|
7
|
+
// Props type for MyTabBar
|
|
8
|
+
function MyTabBar({ state, descriptors, navigation }) {
|
|
9
|
+
return (
|
|
10
|
+
<View style={{ flexDirection: "row" }}>
|
|
11
|
+
{state.routes.map((route, index) => {
|
|
12
|
+
const { options } = descriptors[route.key];
|
|
13
|
+
const label =
|
|
14
|
+
options.tabBarLabel !== undefined
|
|
15
|
+
? options.tabBarLabel
|
|
16
|
+
: options.title !== undefined
|
|
17
|
+
? options.title
|
|
18
|
+
: route.name;
|
|
19
|
+
|
|
20
|
+
const isFocused = state.index === index;
|
|
21
|
+
|
|
22
|
+
const onPress = () => {
|
|
23
|
+
const event = navigation.emit({
|
|
24
|
+
type: "tabPress",
|
|
25
|
+
target: route.key,
|
|
26
|
+
canPreventDefault: true,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
if (!isFocused && !event.defaultPrevented) {
|
|
30
|
+
navigation.navigate(route.name, route.params);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const onLongPress = () => {
|
|
35
|
+
navigation.emit({
|
|
36
|
+
type: "tabLongPress",
|
|
37
|
+
target: route.key,
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<PlatformPressable
|
|
43
|
+
key={route.key}
|
|
44
|
+
href={buildHref(route.name, route.params)}
|
|
45
|
+
accessibilityState={isFocused ? { selected: true } : {}}
|
|
46
|
+
accessibilityLabel={options.tabBarAccessibilityLabel}
|
|
47
|
+
testID={options.tabBarButtonTestID}
|
|
48
|
+
onPress={onPress}
|
|
49
|
+
onLongPress={onLongPress}
|
|
50
|
+
style={{ flex: 1 }}
|
|
51
|
+
>
|
|
52
|
+
<Text style={{ color: isFocused ? colors.primary : colors.text }}>
|
|
53
|
+
{label}
|
|
54
|
+
</Text>
|
|
55
|
+
</PlatformPressable>
|
|
56
|
+
);
|
|
57
|
+
})}
|
|
58
|
+
</View>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export default function Layout() {
|
|
63
|
+
return (
|
|
64
|
+
<Tabs
|
|
65
|
+
screenOptions={{
|
|
66
|
+
headerShown: false,
|
|
67
|
+
tabBarHideOnKeyboard: true,
|
|
68
|
+
lazy: true,
|
|
69
|
+
}}
|
|
70
|
+
tabBar={(props: any) => <MyTabBar {...props} />}
|
|
71
|
+
>
|
|
72
|
+
<Tabs.Screen name="index" />
|
|
73
|
+
</Tabs>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { router, useGlobalSearchParams } from "expo-router";
|
|
2
|
+
import { Pressable, Text, View } from "react-native";
|
|
3
|
+
|
|
4
|
+
import TButton from "@/src/lib/buttons/TButton";
|
|
5
|
+
import tw from "@/src/lib/tailwind";
|
|
6
|
+
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
7
|
+
|
|
8
|
+
const ConfirmationModal = () => {
|
|
9
|
+
const params = useGlobalSearchParams();
|
|
10
|
+
|
|
11
|
+
const handleUnfollow = async () => {
|
|
12
|
+
try {
|
|
13
|
+
await AsyncStorage.removeItem("token");
|
|
14
|
+
router.canDismiss() && router.dismissAll();
|
|
15
|
+
router.replace("/auth");
|
|
16
|
+
} catch (error) {
|
|
17
|
+
console.log(error);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<Pressable
|
|
23
|
+
onPress={() => {
|
|
24
|
+
router.dismiss();
|
|
25
|
+
}}
|
|
26
|
+
style={tw`flex-1 bg-black/45 items-center justify-center`}
|
|
27
|
+
>
|
|
28
|
+
<View
|
|
29
|
+
style={tw`w-[90%] bg-black/85 border border-secondary rounded-xl overflow-hidden items-center justify-center p-4`}
|
|
30
|
+
>
|
|
31
|
+
<View style={tw`items-center gap-5 w-full py-4`}>
|
|
32
|
+
{/* <Text style={tw`text-white text-xl font-InterBold text-center`}>
|
|
33
|
+
{subject || "subject"}
|
|
34
|
+
</Text> */}
|
|
35
|
+
{/* <SvgXml xml={Icon.unfriend} width={80} height={80} /> */}
|
|
36
|
+
|
|
37
|
+
<View style={tw`gap-2 w-full items-center`}>
|
|
38
|
+
<Text style={tw`text-white text-2xl font-InterBold text-center`}>
|
|
39
|
+
Are you sure ?
|
|
40
|
+
</Text>
|
|
41
|
+
<Text
|
|
42
|
+
style={tw`text-gray-400 text-xs font-InterRegular text-center `}
|
|
43
|
+
>
|
|
44
|
+
If want you to logout from the app. {"\n"} then press sure!
|
|
45
|
+
otherwise press cancel
|
|
46
|
+
</Text>
|
|
47
|
+
</View>
|
|
48
|
+
|
|
49
|
+
<View style={tw`flex-row gap-3`}>
|
|
50
|
+
<TButton
|
|
51
|
+
offGradient
|
|
52
|
+
containerStyle={tw`w-[45%] self-center bg-black border border-secondary rounded-xl h-13`}
|
|
53
|
+
titleStyle={tw`text-sm font-InterSemiBold`}
|
|
54
|
+
onPress={() => {
|
|
55
|
+
router?.dismiss();
|
|
56
|
+
}}
|
|
57
|
+
title={"Cancel"}
|
|
58
|
+
/>
|
|
59
|
+
<TButton
|
|
60
|
+
containerStyle={tw`w-[45%] self-center h-13`}
|
|
61
|
+
titleStyle={tw`text-sm font-InterSemiBold`}
|
|
62
|
+
gradinLayoutStyle={tw`rounded-xl`}
|
|
63
|
+
onPress={() => {
|
|
64
|
+
router?.dismiss();
|
|
65
|
+
// router.push("/profile_setup");
|
|
66
|
+
// route && router.push(route);
|
|
67
|
+
handleUnfollow();
|
|
68
|
+
}}
|
|
69
|
+
title={"Sure"}
|
|
70
|
+
/>
|
|
71
|
+
</View>
|
|
72
|
+
</View>
|
|
73
|
+
</View>
|
|
74
|
+
</Pressable>
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export default ConfirmationModal;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { _HIGHT, _WIDTH } from "@/src/utils/utils";
|
|
2
|
+
import { router, useGlobalSearchParams } from "expo-router";
|
|
3
|
+
import { Alert, View } from "react-native";
|
|
4
|
+
|
|
5
|
+
import ModalHeader from "@/src/components/ModalHeader";
|
|
6
|
+
import tw from "@/src/lib/tailwind";
|
|
7
|
+
import React from "react";
|
|
8
|
+
import { WebView } from "react-native-webview";
|
|
9
|
+
|
|
10
|
+
const userAgent =
|
|
11
|
+
"Mozilla/5.0 (Linux; Android 10; SM-G975F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Mobile Safari/537.36";
|
|
12
|
+
|
|
13
|
+
// --- This JavaScript will try to force the loaded website's background to black ---
|
|
14
|
+
// This is useful if the website itself doesn't have a dark mode.
|
|
15
|
+
|
|
16
|
+
// --- Main Purchase Screen Component ---
|
|
17
|
+
const PurchaseScreen = () => {
|
|
18
|
+
const [data, setData] = React.useState<any>(null);
|
|
19
|
+
|
|
20
|
+
const handlePurchase = (values: any) => {
|
|
21
|
+
// console.log("Payment Details:", values);
|
|
22
|
+
Alert.alert("Payment Successful", "Your plan has been purchased.");
|
|
23
|
+
router.dismiss();
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const param = useGlobalSearchParams();
|
|
27
|
+
|
|
28
|
+
React.useLayoutEffect(() => {
|
|
29
|
+
try {
|
|
30
|
+
const parsedData = JSON?.parse(
|
|
31
|
+
Array.isArray(param.data) ? param.data[0] : param.data || "{}"
|
|
32
|
+
);
|
|
33
|
+
setData(parsedData);
|
|
34
|
+
} catch (e) {
|
|
35
|
+
console.error("Failed to parse data:", e);
|
|
36
|
+
setData({}); // Set to empty object on error
|
|
37
|
+
}
|
|
38
|
+
}, [param.data]);
|
|
39
|
+
|
|
40
|
+
// console.log(data);
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<View style={tw`flex-1 bg-black/60 justify-end items-center w-full`}>
|
|
44
|
+
<View
|
|
45
|
+
style={[
|
|
46
|
+
// Changed bg-base to bg-black to make the modal background dark
|
|
47
|
+
tw`bg-black rounded-t-2xl`,
|
|
48
|
+
{
|
|
49
|
+
height: _HIGHT * 0.9,
|
|
50
|
+
width: _WIDTH,
|
|
51
|
+
},
|
|
52
|
+
]}
|
|
53
|
+
>
|
|
54
|
+
{/* Header */}
|
|
55
|
+
<ModalHeader title="Payment" onPress={() => router.dismiss()} />
|
|
56
|
+
{data?.session_url || data?.url ? (
|
|
57
|
+
<WebView
|
|
58
|
+
// Changed bg-base to bg-black and added flex-1
|
|
59
|
+
// This makes the WebView's own background black and ensures it fills the space
|
|
60
|
+
style={tw`bg-black flex-1`}
|
|
61
|
+
source={
|
|
62
|
+
param.type === "subscription"
|
|
63
|
+
? {
|
|
64
|
+
uri: data?.session_url,
|
|
65
|
+
}
|
|
66
|
+
: {
|
|
67
|
+
uri: data?.url,
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
startInLoadingState={true}
|
|
71
|
+
userAgent={userAgent}
|
|
72
|
+
javaScriptEnabled={true}
|
|
73
|
+
domStorageEnabled={true}
|
|
74
|
+
forceDarkOn={true} // You already had this, which is correct!
|
|
75
|
+
// --- Injected JavaScript ---
|
|
76
|
+
// This runs the code from `forceDarkJS` to force the website content
|
|
77
|
+
// to have a black background, just in case forceDarkOn isn't enough.
|
|
78
|
+
// injectedJavaScript={forceDarkJS}
|
|
79
|
+
// Run this on the first load
|
|
80
|
+
onMessage={() => {}} // Required for injectedJavaScript to run on first load
|
|
81
|
+
directionalLockEnabled
|
|
82
|
+
sharedCookiesEnabled={true}
|
|
83
|
+
thirdPartyCookiesEnabled={true}
|
|
84
|
+
originWhitelist={["*"]}
|
|
85
|
+
onNavigationStateChange={(navState) => {
|
|
86
|
+
if (navState.url.includes("success")) {
|
|
87
|
+
handlePurchase(data);
|
|
88
|
+
}
|
|
89
|
+
if (navState.url.includes("cancel")) {
|
|
90
|
+
router.dismiss();
|
|
91
|
+
}
|
|
92
|
+
}}
|
|
93
|
+
/>
|
|
94
|
+
) : (
|
|
95
|
+
// Added a fallback view in case session_url is missing
|
|
96
|
+
<View style={tw`flex-1 items-center justify-center bg-black`}>
|
|
97
|
+
{/* You could put a loading spinner or error message here */}
|
|
98
|
+
</View>
|
|
99
|
+
)}
|
|
100
|
+
</View>
|
|
101
|
+
</View>
|
|
102
|
+
);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export default PurchaseScreen;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { router, useGlobalSearchParams } from "expo-router";
|
|
2
|
+
import { Platform, Pressable, Text, View } from "react-native";
|
|
3
|
+
|
|
4
|
+
import { Icon } from "@/assets/icons/Icon";
|
|
5
|
+
import TButton from "@/src/lib/buttons/TButton";
|
|
6
|
+
import tw from "@/src/lib/tailwind";
|
|
7
|
+
import { BlurView } from "expo-blur";
|
|
8
|
+
import React from "react";
|
|
9
|
+
import { SvgXml } from "react-native-svg";
|
|
10
|
+
|
|
11
|
+
const Success_modal = () => {
|
|
12
|
+
const params = useGlobalSearchParams();
|
|
13
|
+
// ?title='You’re All Set!'&subtitle='Your password has been changed successfully!'&buttonTitle='Back to login'&route='/auth
|
|
14
|
+
// const { title, subtitle, buttonTitle, route }: any = params;
|
|
15
|
+
const CommonContent = () => {
|
|
16
|
+
return (
|
|
17
|
+
<View style={tw`items-center gap-5 w-full`}>
|
|
18
|
+
<SvgXml width={80} height={80} xml={Icon.check2} />
|
|
19
|
+
|
|
20
|
+
<View style={tw`gap-2 w-full items-center`}>
|
|
21
|
+
<Text style={tw`text-white text-2xl font-semibold text-center`}>
|
|
22
|
+
{params?.message || "Email sent successfully"}
|
|
23
|
+
</Text>
|
|
24
|
+
</View>
|
|
25
|
+
|
|
26
|
+
<TButton
|
|
27
|
+
containerStyle={tw`w-full mt-3 h-13 self-center `}
|
|
28
|
+
gradinLayoutStyle={tw`rounded-xl`}
|
|
29
|
+
onPress={() => {
|
|
30
|
+
router?.dismiss();
|
|
31
|
+
// router.push("/home/tabs/account");
|
|
32
|
+
if (params?.route as string) {
|
|
33
|
+
router.push(params?.route as any);
|
|
34
|
+
} else {
|
|
35
|
+
router?.dismiss();
|
|
36
|
+
}
|
|
37
|
+
}}
|
|
38
|
+
title={(params?.b_text as string) || "Done"}
|
|
39
|
+
/>
|
|
40
|
+
</View>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<Pressable
|
|
46
|
+
onPress={() => {
|
|
47
|
+
router.dismiss();
|
|
48
|
+
}}
|
|
49
|
+
style={tw`flex-1 bg-black/45 items-center justify-center`}
|
|
50
|
+
>
|
|
51
|
+
{Platform.OS === "ios" ? (
|
|
52
|
+
<BlurView
|
|
53
|
+
style={tw`w-[90%] rounded-xl overflow-hidden items-center justify-center p-6 pb-8`}
|
|
54
|
+
tint="dark"
|
|
55
|
+
blurReductionFactor={5}
|
|
56
|
+
intensity={100}
|
|
57
|
+
experimentalBlurMethod="dimezisBlurView"
|
|
58
|
+
>
|
|
59
|
+
<CommonContent />
|
|
60
|
+
</BlurView>
|
|
61
|
+
) : (
|
|
62
|
+
<View
|
|
63
|
+
style={tw`w-[90%] bg-black/85 border border-secondary rounded-xl overflow-hidden items-center justify-center p-6 pb-8`}
|
|
64
|
+
>
|
|
65
|
+
<CommonContent />
|
|
66
|
+
</View>
|
|
67
|
+
)}
|
|
68
|
+
</Pressable>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export default Success_modal;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Text, View } from "react-native";
|
|
2
|
+
import { router, useGlobalSearchParams, usePathname } from "expo-router";
|
|
3
|
+
|
|
4
|
+
import React from "react";
|
|
5
|
+
import tw from "@/src/lib/tailwind";
|
|
6
|
+
|
|
7
|
+
const Taster = () => {
|
|
8
|
+
const params = useGlobalSearchParams();
|
|
9
|
+
const pathname = usePathname(); // current route path
|
|
10
|
+
|
|
11
|
+
React.useEffect(() => {
|
|
12
|
+
const currentPath = pathname; // save the path when modal loads
|
|
13
|
+
|
|
14
|
+
const timer = setTimeout(() => {
|
|
15
|
+
// check if user still on this modal page
|
|
16
|
+
if (router.canGoBack() && pathname === currentPath) {
|
|
17
|
+
router.dismiss();
|
|
18
|
+
}
|
|
19
|
+
}, Number(params?.time) || 2000);
|
|
20
|
+
|
|
21
|
+
return () => clearTimeout(timer);
|
|
22
|
+
}, [params?.time, pathname]);
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<View style={tw`p-4 bg-gray-900`}>
|
|
26
|
+
<Text style={tw`text-white`}>{params?.content}</Text>
|
|
27
|
+
</View>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default Taster;
|