clear more ticket, sub and design

This commit is contained in:
Thomas Demirdjian
2025-12-10 15:39:32 +01:00
parent b1ab6a549f
commit aacbd5411e
15 changed files with 1001 additions and 145 deletions
+99 -5
View File
@@ -1,6 +1,6 @@
import { BlurView } from "expo-blur";
import React from "react";
import { Image, TextInput, View } from "react-native";
import React, { useCallback, useEffect, useRef } from "react";
import { Image, Pressable, TextInput } from "react-native";
import { icons } from "../assets";
import { Palette } from "../styles";
import { FONT_FAMILY } from "../styles/Fonts";
@@ -10,8 +10,99 @@ const SearchBar = ({
placeholder = "Que souhaites-tu écouter?",
textInputProps = {},
}) => {
const inputRef = useRef(null);
const hasForwardedFocusRef = useRef(false);
const focusFallbackRef = useRef(null);
const {
onFocus: onFocusProp,
onPressIn: onPressInProp,
ref: textInputRefProp,
...restTextInputProps
} = textInputProps;
const forwardFocus = useCallback(
(event) => {
if (hasForwardedFocusRef.current) return;
hasForwardedFocusRef.current = true;
onFocusProp?.(event);
},
[onFocusProp],
);
const handleFocus = useCallback(
(event) => {
forwardFocus(event);
},
[forwardFocus],
);
const focusInput = useCallback(() => {
const node = inputRef.current;
if (!node) {
return;
}
if (
typeof node.isFocused === "function" &&
node.isFocused() &&
hasForwardedFocusRef.current
) {
return;
}
hasForwardedFocusRef.current = false;
node.focus?.();
if (focusFallbackRef.current) {
cancelAnimationFrame(focusFallbackRef.current);
}
focusFallbackRef.current = requestAnimationFrame(() => {
// Some platforms do not propagate the focus event when focus() is called programmatically.
if (!hasForwardedFocusRef.current) {
forwardFocus();
}
if (typeof node.isFocused === "function" && !node.isFocused()) {
node.focus?.();
}
});
}, [forwardFocus]);
useEffect(() => {
return () => {
if (focusFallbackRef.current) {
cancelAnimationFrame(focusFallbackRef.current);
}
};
}, []);
const setRefs = useCallback(
(node) => {
inputRef.current = node;
if (typeof textInputRefProp === "function") {
textInputRefProp(node);
} else if (textInputRefProp) {
textInputRefProp.current = node;
}
},
[textInputRefProp],
);
const handleInputPressIn = useCallback(
(event) => {
focusInput();
onPressInProp?.(event);
},
[focusInput, onPressInProp],
);
return (
<View style={{ borderRadius: 12, overflow: "hidden" }}>
<Pressable
onPressIn={focusInput}
style={{ borderRadius: 12, overflow: "hidden" }}
>
<BlurView
intensity={80}
style={{
@@ -24,6 +115,7 @@ const SearchBar = ({
>
<Image source={icons.search} />
<TextInput
ref={setRefs}
placeholder={placeholder}
placeholderTextColor={Palette.gray}
style={{
@@ -32,10 +124,12 @@ const SearchBar = ({
fontFamily: FONT_FAMILY.HelveticaNeueRegular,
flex: 1,
}}
{...textInputProps}
onPressIn={handleInputPressIn}
onFocus={handleFocus}
{...restTextInputProps}
/>
</BlurView>
</View>
</Pressable>
);
};
+32
View File
@@ -0,0 +1,32 @@
import React from "react";
import { Image } from "react-native";
import { subBadges } from "../assets";
const allowedLevels = new Set(["starter", "pro", "premium"]);
const normalizeLevel = (value) => {
if (typeof value !== "string") {
return null;
}
const normalized = value.trim().toLowerCase();
return allowedLevels.has(normalized) ? normalized : null;
};
const SubscriptionBadge = ({ level = null, size = 20, style = null }) => {
const normalized = normalizeLevel(level);
const source = normalized ? subBadges[normalized] : null;
if (!source) {
return null;
}
return (
<Image
source={source}
style={[{ width: size, height: size }, style]}
resizeMode="contain"
/>
);
};
export default React.memo(SubscriptionBadge);
+6 -20
View File
@@ -18,7 +18,7 @@ import { isWeb } from "../../hooks/useLayoutType";
import CreditAmount from "../CreditAmount";
import { useStripe } from "../../providers/StripeProvider";
const WEB_MODAL_MAX_WIDTH = 1000;
const WEB_MODAL_MAX_WIDTH = 820;
const formatCurrency = (amount, currency = "eur") => {
if (typeof amount !== "number") {
return null;
@@ -229,14 +229,6 @@ function CoinPackCard({
) : null}
{pack?.name ? <Text style={styles.packName}>{pack.name}</Text> : null}
</View>
<View style={styles.perksList}>
<Text style={styles.perkItem}>
- Diffusion sur mes plates formes de streaming
</Text>
<Text style={styles.perkItem}>
*Eligible au Hit parade Chanson/Video
</Text>
</View>
<View style={styles.priceBlock}>
{formattedPrice ? (
<Text style={styles.packPrice}>{formattedPrice}</Text>
@@ -388,6 +380,9 @@ const CoinPackModal = ({ visible, onClose }) => {
width: "100%",
maxWidth: modalMaxWidth,
alignSelf: "center",
borderWidth: isWeb ? 1 : 0,
borderColor: "rgba(255,255,255,0.16)",
backgroundColor: isWeb ? "rgba(12, 10, 18, 0.85)" : undefined,
}}
>
<View style={styles.container}>
@@ -452,6 +447,7 @@ const styles = StyleSheet.create({
gap: 24,
alignItems: "center",
paddingBottom: gutters,
paddingHorizontal: isWeb ? gutters * 1.5 : 0,
width: "100%",
maxWidth: isWeb ? WEB_MODAL_MAX_WIDTH : undefined,
},
@@ -503,7 +499,7 @@ const styles = StyleSheet.create({
maxWidth: isWeb ? WEB_MODAL_MAX_WIDTH : "100%",
alignSelf: "center",
gap: gutters,
paddingHorizontal: isWeb ? 8 : 0,
paddingHorizontal: isWeb ? gutters * 1.5 : 0,
},
packListWeb: {
flexDirection: "row",
@@ -567,16 +563,6 @@ const styles = StyleSheet.create({
color: "rgba(255, 255, 255, 0.72)",
textAlign: "center",
},
perksList: {
gap: 6,
alignItems: "center",
},
perkItem: {
fontFamily: FONT_FAMILY.InterRegular,
fontSize: 13,
color: "rgba(255, 255, 255, 0.72)",
textAlign: "center",
},
priceBlock: {
gap: 2,
alignItems: "center",