last fixes, home and videos
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
import React from "react";
|
||||
import { StyleSheet, Text, View } from "react-native";
|
||||
import CoinIcon from "./CoinIcon";
|
||||
import { FONT_FAMILY } from "../styles/Fonts";
|
||||
|
||||
const defaultFormatOptions = {
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: 0,
|
||||
};
|
||||
|
||||
const formatAmount = (value, options = defaultFormatOptions) => {
|
||||
if (typeof value !== "number" || !Number.isFinite(value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return new Intl.NumberFormat("fr-FR", {
|
||||
...defaultFormatOptions,
|
||||
...options,
|
||||
}).format(value);
|
||||
} catch (_error) {
|
||||
return `${value}`;
|
||||
}
|
||||
};
|
||||
|
||||
const extractNumericValue = (value) => {
|
||||
if (typeof value === "number" && Number.isFinite(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (typeof value === "string") {
|
||||
const parsed = Number(value);
|
||||
if (Number.isFinite(parsed)) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const CreditAmount = ({
|
||||
value,
|
||||
style,
|
||||
textStyle,
|
||||
iconSize = 18,
|
||||
iconStyle,
|
||||
iconPosition = "right",
|
||||
gap = 6,
|
||||
showPlus = false,
|
||||
formatterOptions,
|
||||
accessibilityLabel,
|
||||
}) => {
|
||||
const numericValue = React.useMemo(
|
||||
() => extractNumericValue(value),
|
||||
[value],
|
||||
);
|
||||
|
||||
const resolvedValue =
|
||||
numericValue !== null
|
||||
? numericValue
|
||||
: value ?? 0;
|
||||
|
||||
const formattedValue =
|
||||
numericValue !== null
|
||||
? formatAmount(resolvedValue, formatterOptions) ?? `${resolvedValue}`
|
||||
: typeof resolvedValue === "string"
|
||||
? resolvedValue
|
||||
: `${resolvedValue}`;
|
||||
|
||||
const prefix =
|
||||
numericValue !== null && showPlus && numericValue > 0 ? "+" : "";
|
||||
|
||||
const a11yLabel =
|
||||
accessibilityLabel || `${prefix}${formattedValue} pièces`;
|
||||
|
||||
const containerStyles = Array.isArray(style)
|
||||
? [styles.container, { gap }, ...style]
|
||||
: [styles.container, { gap }, style];
|
||||
|
||||
const textStyles = Array.isArray(textStyle)
|
||||
? [styles.value, ...textStyle]
|
||||
: [styles.value, textStyle];
|
||||
|
||||
const iconStyles = Array.isArray(iconStyle)
|
||||
? [styles.icon, ...iconStyle]
|
||||
: [styles.icon, iconStyle];
|
||||
|
||||
return (
|
||||
<View
|
||||
style={containerStyles}
|
||||
accessibilityRole="text"
|
||||
accessibilityLabel={a11yLabel}
|
||||
>
|
||||
{iconPosition === "left" ? (
|
||||
<CoinIcon size={iconSize} style={iconStyles} />
|
||||
) : null}
|
||||
<Text style={textStyles}>{`${prefix}${formattedValue}`}</Text>
|
||||
{iconPosition === "right" ? (
|
||||
<CoinIcon size={iconSize} style={iconStyles} />
|
||||
) : null}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
},
|
||||
value: {
|
||||
fontFamily: FONT_FAMILY.InterSemiBold,
|
||||
fontSize: 16,
|
||||
color: "#fff",
|
||||
},
|
||||
icon: {
|
||||
width: 18,
|
||||
height: 18,
|
||||
},
|
||||
});
|
||||
|
||||
export default CreditAmount;
|
||||
@@ -11,6 +11,7 @@ const MusicLandHeader = ({
|
||||
showSkip = false,
|
||||
progress = 1,
|
||||
logo = null,
|
||||
style,
|
||||
}) => {
|
||||
const isWeb = Platform.OS === "web";
|
||||
const backButtonStyle = isWeb
|
||||
@@ -34,7 +35,7 @@ const MusicLandHeader = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={{ alignItems: "center", gap: 16 }}>
|
||||
<View style={{ alignItems: "center", gap: 16, ...style }}>
|
||||
<View style={{ width: "100%", ...Style.containerRow, gap: 16 }}>
|
||||
<Pressable style={backButtonStyle} onPress={onPressBack}>
|
||||
<Image
|
||||
@@ -72,14 +73,16 @@ const MusicLandHeader = ({
|
||||
</Pressable>
|
||||
)}
|
||||
</View>
|
||||
<Image
|
||||
source={logo}
|
||||
style={{
|
||||
alignSelf: "center",
|
||||
height: isWeb ? 150 : 100,
|
||||
resizeMode: "contain",
|
||||
}}
|
||||
/>
|
||||
{logo && (
|
||||
<Image
|
||||
source={logo}
|
||||
style={{
|
||||
alignSelf: "center",
|
||||
height: isWeb ? 150 : 100,
|
||||
resizeMode: "contain",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { StyleSheet, Text, View } from "react-native";
|
||||
import { Gesture, GestureDetector } from "react-native-gesture-handler";
|
||||
import Animated, {
|
||||
@@ -24,6 +24,7 @@ export default ({
|
||||
const offset = useSharedValue(0);
|
||||
const boxWidth = useSharedValue(INITIAL_BOX_SIZE);
|
||||
const [layout, setLayout] = useState(null);
|
||||
const seekingRef = useRef(false);
|
||||
|
||||
const SLIDER_WIDTH = layout?.width;
|
||||
const MAX_VALUE = SLIDER_WIDTH - INITIAL_BOX_SIZE;
|
||||
@@ -31,12 +32,34 @@ export default ({
|
||||
const pan = Gesture.Pan()
|
||||
.enabled(seekEnabled)
|
||||
.onBegin(() => {
|
||||
if (seekEnabled && typeof onSeekStart === "function") {
|
||||
// Notify JS thread that user started seeking (e.g., pause audio)
|
||||
if (
|
||||
seekEnabled &&
|
||||
typeof onSeekStart === "function" &&
|
||||
!seekingRef.current
|
||||
) {
|
||||
seekingRef.current = true;
|
||||
runOnJS(onSeekStart)();
|
||||
}
|
||||
})
|
||||
.onStart(() => {
|
||||
if (
|
||||
seekEnabled &&
|
||||
typeof onSeekStart === "function" &&
|
||||
!seekingRef.current
|
||||
) {
|
||||
seekingRef.current = true;
|
||||
runOnJS(onSeekStart)();
|
||||
}
|
||||
})
|
||||
.onChange((event) => {
|
||||
if (
|
||||
seekEnabled &&
|
||||
typeof onSeekStart === "function" &&
|
||||
!seekingRef.current
|
||||
) {
|
||||
seekingRef.current = true;
|
||||
runOnJS(onSeekStart)();
|
||||
}
|
||||
offset.value =
|
||||
Math.abs(offset.value) <= MAX_VALUE
|
||||
? offset.value + event.changeX <= 0
|
||||
@@ -50,14 +73,22 @@ export default ({
|
||||
boxWidth.value = newWidth;
|
||||
})
|
||||
.onEnd(() => {
|
||||
if (!seekEnabled || !onSeek || !MAX_VALUE) return;
|
||||
const ratio =
|
||||
MAX_VALUE > 0 ? Math.min(1, Math.max(0, offset.value / MAX_VALUE)) : 0;
|
||||
// Reanimated -> JS thread bridge
|
||||
runOnJS(onSeek)(ratio);
|
||||
if (seekEnabled && typeof onSeek === "function" && MAX_VALUE) {
|
||||
const ratio =
|
||||
MAX_VALUE > 0
|
||||
? Math.min(1, Math.max(0, offset.value / MAX_VALUE))
|
||||
: 0;
|
||||
// Reanimated -> JS thread bridge
|
||||
runOnJS(onSeek)(ratio);
|
||||
}
|
||||
if (seekingRef.current && typeof onSeekEnd === "function") {
|
||||
seekingRef.current = false;
|
||||
runOnJS(onSeekEnd)();
|
||||
}
|
||||
})
|
||||
.onFinalize(() => {
|
||||
if (seekEnabled && typeof onSeekEnd === "function") {
|
||||
if (seekingRef.current && typeof onSeekEnd === "function") {
|
||||
seekingRef.current = false;
|
||||
runOnJS(onSeekEnd)();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from "react";
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Image,
|
||||
Linking,
|
||||
Modal,
|
||||
Pressable,
|
||||
@@ -11,7 +10,6 @@ import {
|
||||
View,
|
||||
} from "react-native";
|
||||
import { BlurView } from "expo-blur";
|
||||
import { icons } from "../../assets";
|
||||
import BorderGradientButton from "../BorderGradientButton";
|
||||
import GradientButton from "../GradientButton";
|
||||
import CreateLyricsHeader from "../../screens/Writing/components/CreateLyricsHeader";
|
||||
@@ -19,6 +17,7 @@ import { Palette, gutters } from "../../styles";
|
||||
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
import { getFunctionsClient } from "../../config/firebase";
|
||||
import { isWeb } from "../../hooks/useLayoutType";
|
||||
import CreditAmount from "../CreditAmount";
|
||||
|
||||
const WEB_MODAL_MAX_WIDTH = 1000;
|
||||
const FUNCTIONS_REGION = "europe-west1";
|
||||
@@ -73,10 +72,12 @@ function CoinPackCard({ pack, selected, onSelect }) {
|
||||
>
|
||||
<BlurView intensity={20} tint="dark" style={styles.cardBlur}>
|
||||
<View style={styles.cardHeader}>
|
||||
<View style={styles.coinRow}>
|
||||
<Image source={icons.coin} style={styles.coinIcon} />
|
||||
<Text style={styles.coinAmount}>{pack?.coinAmount} pièces</Text>
|
||||
</View>
|
||||
<CreditAmount
|
||||
value={pack?.coinAmount}
|
||||
style={styles.coinRow}
|
||||
textStyle={styles.coinAmount}
|
||||
iconSize={26}
|
||||
/>
|
||||
{pack?.name ? <Text style={styles.packName}>{pack.name}</Text> : null}
|
||||
</View>
|
||||
{pack?.description ? (
|
||||
@@ -395,11 +396,6 @@ const styles = StyleSheet.create({
|
||||
alignItems: "center",
|
||||
gap: 10,
|
||||
},
|
||||
coinIcon: {
|
||||
width: 26,
|
||||
height: 26,
|
||||
resizeMode: "contain",
|
||||
},
|
||||
coinAmount: {
|
||||
fontFamily: FONT_FAMILY.InterBold,
|
||||
fontSize: 20,
|
||||
|
||||
Reference in New Issue
Block a user