home carousel
This commit is contained in:
@@ -14,7 +14,9 @@ import {
|
|||||||
View,
|
View,
|
||||||
useWindowDimensions,
|
useWindowDimensions,
|
||||||
} from "react-native";
|
} from "react-native";
|
||||||
|
import { responsiveHeight } from "../../actions/responsiveSizes";
|
||||||
import { ai } from "../../assets";
|
import { ai } from "../../assets";
|
||||||
|
import { gutters } from "../../styles";
|
||||||
import { getCreationStageStates } from "../../utils/projectStages";
|
import { getCreationStageStates } from "../../utils/projectStages";
|
||||||
import AnimatedPaginationDot from "../AnimatedPaginationDot/AnimatedPaginationDot";
|
import AnimatedPaginationDot from "../AnimatedPaginationDot/AnimatedPaginationDot";
|
||||||
import PersonaCard from "../cards/PersonaCard/PersonaCard";
|
import PersonaCard from "../cards/PersonaCard/PersonaCard";
|
||||||
@@ -422,6 +424,8 @@ const styles = StyleSheet.create({
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
width: "100%",
|
width: "100%",
|
||||||
flexDirection: "row",
|
flexDirection: "row",
|
||||||
|
paddingBottom: responsiveHeight(5),
|
||||||
|
paddingHorizontal: gutters,
|
||||||
},
|
},
|
||||||
containerWeb: {
|
containerWeb: {
|
||||||
paddingRight: 56,
|
paddingRight: 56,
|
||||||
|
|||||||
@@ -0,0 +1,529 @@
|
|||||||
|
import { BlurView } from "expo-blur";
|
||||||
|
import React, {
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
|
import {
|
||||||
|
Animated,
|
||||||
|
Image,
|
||||||
|
Platform,
|
||||||
|
StyleSheet,
|
||||||
|
View,
|
||||||
|
useWindowDimensions,
|
||||||
|
} from "react-native";
|
||||||
|
import { ai } from "../../assets";
|
||||||
|
import { getCreationStageStates } from "../../utils/projectStages";
|
||||||
|
import AnimatedPaginationDot from "../AnimatedPaginationDot/AnimatedPaginationDot";
|
||||||
|
import PersonaCard from "../cards/PersonaCard/PersonaCard";
|
||||||
|
import { LinearGradient } from "../LinearGradient/LinearGradient";
|
||||||
|
|
||||||
|
const STAGE_CARD_CONTENT = [
|
||||||
|
{
|
||||||
|
key: "songwriter",
|
||||||
|
title: "Nathalie",
|
||||||
|
description: "Let’s write lyrics together !",
|
||||||
|
image: ai.leftIcon,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "beatmaker",
|
||||||
|
title: "Theo",
|
||||||
|
description: "Come back, when you'll have lyrics!",
|
||||||
|
image: ai.rightIcon,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "designer",
|
||||||
|
title: "Bena",
|
||||||
|
description: "Come back when you want to generate cover!",
|
||||||
|
image: ai.leftIcon,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "director",
|
||||||
|
title: "John",
|
||||||
|
description: "Come back when your audio is ready!",
|
||||||
|
image: ai.rightIcon,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const WEB_SCROLL_INACTIVE_DELTA = 0.05;
|
||||||
|
const HOME_BACKGROUND_COLOR = "#425B87"; // Derived from the bottom color of the home hero image
|
||||||
|
const AnimatedImage = Animated.createAnimatedComponent(Image);
|
||||||
|
|
||||||
|
const FeatureCarousel = ({
|
||||||
|
style,
|
||||||
|
selectedProject,
|
||||||
|
stageStates: stageStatesProp,
|
||||||
|
activeIndex,
|
||||||
|
onActiveIndexChange,
|
||||||
|
backgroundImage,
|
||||||
|
}) => {
|
||||||
|
const stageStates = useMemo(() => {
|
||||||
|
if (stageStatesProp) {
|
||||||
|
return stageStatesProp;
|
||||||
|
}
|
||||||
|
return getCreationStageStates(selectedProject);
|
||||||
|
}, [stageStatesProp, selectedProject]);
|
||||||
|
|
||||||
|
const stageStatesByKey = useMemo(() => {
|
||||||
|
if (!Array.isArray(stageStates)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return stageStates.reduce((acc, stage) => {
|
||||||
|
if (stage?.key) {
|
||||||
|
acc[stage.key] = stage;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}, [stageStates]);
|
||||||
|
|
||||||
|
const carouselItems = useMemo(
|
||||||
|
() =>
|
||||||
|
STAGE_CARD_CONTENT.map((item) => {
|
||||||
|
const state = stageStatesByKey[item.key];
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
isLocked: state?.isLocked ?? true,
|
||||||
|
description: state?.description ?? item.description,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
[stageStatesByKey]
|
||||||
|
);
|
||||||
|
|
||||||
|
const { height: windowHeight } = useWindowDimensions();
|
||||||
|
const isWeb = Platform.OS === "web";
|
||||||
|
|
||||||
|
const [viewportHeight, setViewportHeight] = useState(() =>
|
||||||
|
Math.max(windowHeight, 1)
|
||||||
|
);
|
||||||
|
|
||||||
|
const updateSnapHeight = useCallback((height) => {
|
||||||
|
if (!height || Number.isNaN(height)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setViewportHeight((prev) => {
|
||||||
|
if (prev == null || Math.abs(prev - height) > 0.5) {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
return prev;
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updateSnapHeight(Math.max(windowHeight, 1));
|
||||||
|
}, [updateSnapHeight, windowHeight]);
|
||||||
|
|
||||||
|
const itemHeight = Math.max(viewportHeight, 1);
|
||||||
|
|
||||||
|
const scrollViewRef = useRef(null);
|
||||||
|
const alignTimeoutRef = useRef(null);
|
||||||
|
const activeIndexRef = useRef(
|
||||||
|
typeof activeIndex === "number" ? activeIndex : 0
|
||||||
|
);
|
||||||
|
const onActiveIndexChangeRef = useRef(onActiveIndexChange);
|
||||||
|
const scrollY = useRef(new Animated.Value(0)).current;
|
||||||
|
const parallaxTranslateY = useMemo(() => {
|
||||||
|
if (!itemHeight) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return scrollY.interpolate({
|
||||||
|
inputRange: [0, itemHeight],
|
||||||
|
outputRange: [0, -itemHeight * 0.25],
|
||||||
|
extrapolate: "clamp",
|
||||||
|
});
|
||||||
|
}, [itemHeight, scrollY]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onActiveIndexChangeRef.current = onActiveIndexChange;
|
||||||
|
}, [onActiveIndexChange]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof activeIndex === "number") {
|
||||||
|
activeIndexRef.current = activeIndex;
|
||||||
|
}
|
||||||
|
}, [activeIndex]);
|
||||||
|
|
||||||
|
const clampIndex = useCallback(
|
||||||
|
(index) => {
|
||||||
|
if (!carouselItems.length) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (index < 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (index >= carouselItems.length) {
|
||||||
|
return carouselItems.length - 1;
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
},
|
||||||
|
[carouselItems.length]
|
||||||
|
);
|
||||||
|
|
||||||
|
const clearPendingAlignment = useCallback(() => {
|
||||||
|
if (alignTimeoutRef.current != null) {
|
||||||
|
globalThis.clearTimeout(alignTimeoutRef.current);
|
||||||
|
alignTimeoutRef.current = null;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => () => clearPendingAlignment(), [clearPendingAlignment]);
|
||||||
|
|
||||||
|
const getScrollNode = useCallback(() => {
|
||||||
|
const node = scrollViewRef.current;
|
||||||
|
if (!node) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (typeof node.scrollTo === "function") {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
if (typeof node.getNode === "function") {
|
||||||
|
return node.getNode();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const scrollToIndex = useCallback(
|
||||||
|
(index, animated = true, heightOverride) => {
|
||||||
|
const target = getScrollNode();
|
||||||
|
if (!target) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const clamped = clampIndex(index);
|
||||||
|
const height =
|
||||||
|
heightOverride && heightOverride > 0 ? heightOverride : itemHeight;
|
||||||
|
|
||||||
|
if (!height) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSnapHeight(height);
|
||||||
|
const offset = clamped * height;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (typeof target.scrollTo === "function") {
|
||||||
|
target.scrollTo({ y: offset, animated });
|
||||||
|
} else if (typeof target.scrollToOffset === "function") {
|
||||||
|
target.scrollToOffset({ offset, animated });
|
||||||
|
}
|
||||||
|
} catch (_error) {
|
||||||
|
// ScrollView not ready yet, ignore.
|
||||||
|
}
|
||||||
|
activeIndexRef.current = clamped;
|
||||||
|
},
|
||||||
|
[clampIndex, getScrollNode, itemHeight, updateSnapHeight]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
scrollViewRef.current == null ||
|
||||||
|
typeof activeIndex !== "number" ||
|
||||||
|
activeIndex < 0 ||
|
||||||
|
activeIndex >= carouselItems.length ||
|
||||||
|
(isWeb && !itemHeight)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scrollToIndex(activeIndex);
|
||||||
|
}, [activeIndex, carouselItems.length, isWeb, itemHeight, scrollToIndex]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (scrollViewRef.current == null || (isWeb && !itemHeight)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scrollToIndex(activeIndexRef.current, false);
|
||||||
|
}, [carouselItems.length, isWeb, itemHeight, scrollToIndex]);
|
||||||
|
|
||||||
|
const alignToOffset = useCallback(
|
||||||
|
(offset, layoutHeight, options = {}) => {
|
||||||
|
const { forceSnap = false } = options || {};
|
||||||
|
const height =
|
||||||
|
layoutHeight && layoutHeight > 0 ? layoutHeight : itemHeight;
|
||||||
|
if (!height) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSnapHeight(height);
|
||||||
|
|
||||||
|
const currentIndex = activeIndexRef.current;
|
||||||
|
const rawIndex = height ? offset / height : currentIndex;
|
||||||
|
|
||||||
|
let nextIndex = currentIndex;
|
||||||
|
if (isWeb) {
|
||||||
|
const delta = rawIndex - currentIndex;
|
||||||
|
if (Math.abs(delta) > WEB_SCROLL_INACTIVE_DELTA) {
|
||||||
|
if (Math.abs(delta) <= 1) {
|
||||||
|
nextIndex = clampIndex(currentIndex + (delta > 0 ? 1 : -1));
|
||||||
|
} else {
|
||||||
|
nextIndex = clampIndex(currentIndex + Math.round(delta));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nextIndex = clampIndex(Math.round(rawIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasChanged = nextIndex !== activeIndexRef.current;
|
||||||
|
|
||||||
|
if (hasChanged) {
|
||||||
|
activeIndexRef.current = nextIndex;
|
||||||
|
const callback = onActiveIndexChangeRef.current;
|
||||||
|
if (callback) {
|
||||||
|
callback(nextIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forceSnap || hasChanged) {
|
||||||
|
scrollToIndex(nextIndex, true, height);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[clampIndex, isWeb, itemHeight, scrollToIndex, updateSnapHeight]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleScrollEnd = useCallback(
|
||||||
|
(event) => {
|
||||||
|
clearPendingAlignment();
|
||||||
|
const offsetY = event?.nativeEvent?.contentOffset?.y ?? 0;
|
||||||
|
const layoutHeight = event?.nativeEvent?.layoutMeasurement?.height;
|
||||||
|
alignToOffset(offsetY, layoutHeight, { forceSnap: true });
|
||||||
|
},
|
||||||
|
[alignToOffset, clearPendingAlignment]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleScroll = useCallback(
|
||||||
|
(event) => {
|
||||||
|
if (!isWeb) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const offsetY = event?.nativeEvent?.contentOffset?.y ?? 0;
|
||||||
|
const layoutHeight = event?.nativeEvent?.layoutMeasurement?.height;
|
||||||
|
clearPendingAlignment();
|
||||||
|
alignTimeoutRef.current = globalThis.setTimeout(() => {
|
||||||
|
alignToOffset(offsetY, layoutHeight, { forceSnap: false });
|
||||||
|
alignTimeoutRef.current = null;
|
||||||
|
}, 80);
|
||||||
|
},
|
||||||
|
[alignToOffset, clearPendingAlignment, isWeb]
|
||||||
|
);
|
||||||
|
|
||||||
|
const animatedScrollHandler = useMemo(
|
||||||
|
() =>
|
||||||
|
Animated.event([{ nativeEvent: { contentOffset: { y: scrollY } } }], {
|
||||||
|
useNativeDriver: false,
|
||||||
|
listener: isWeb ? handleScroll : undefined,
|
||||||
|
}),
|
||||||
|
[handleScroll, isWeb, scrollY]
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleLayout = useCallback(
|
||||||
|
(event) => {
|
||||||
|
const layoutHeight = event?.nativeEvent?.layout?.height ?? 0;
|
||||||
|
if (layoutHeight > 0) {
|
||||||
|
updateSnapHeight(layoutHeight);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[updateSnapHeight]
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderContent = useMemo(
|
||||||
|
() =>
|
||||||
|
carouselItems.map((item, index) => {
|
||||||
|
const card = (
|
||||||
|
<View style={styles.cardWrapper}>
|
||||||
|
<PersonaCard
|
||||||
|
item={item}
|
||||||
|
index={index}
|
||||||
|
isLock={item.isLocked}
|
||||||
|
height={itemHeight}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
const containerStyle = [styles.backgroundItem, { height: itemHeight }];
|
||||||
|
const isFirstItem = index === 0 && backgroundImage;
|
||||||
|
|
||||||
|
if (isFirstItem) {
|
||||||
|
const parallaxStyles = [
|
||||||
|
styles.parallaxImage,
|
||||||
|
itemHeight ? { height: itemHeight * 1.2 } : null,
|
||||||
|
itemHeight ? { top: -itemHeight * 0.1 } : null,
|
||||||
|
{ width: "100%" },
|
||||||
|
].filter(Boolean);
|
||||||
|
if (parallaxTranslateY) {
|
||||||
|
parallaxStyles.push({
|
||||||
|
transform: [{ translateY: parallaxTranslateY }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
key={item.key}
|
||||||
|
style={[...containerStyle, styles.parallaxContainer]}
|
||||||
|
>
|
||||||
|
<AnimatedImage
|
||||||
|
source={backgroundImage}
|
||||||
|
style={parallaxStyles}
|
||||||
|
resizeMode="cover"
|
||||||
|
/>
|
||||||
|
<LinearGradient
|
||||||
|
colors={[
|
||||||
|
"rgba(66, 91, 135, 0)",
|
||||||
|
"rgba(66, 91, 135, 0.4)",
|
||||||
|
HOME_BACKGROUND_COLOR,
|
||||||
|
]}
|
||||||
|
locations={[0, 0.75, 1]}
|
||||||
|
style={styles.backgroundGradient}
|
||||||
|
pointerEvents="none"
|
||||||
|
/>
|
||||||
|
{card}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View key={item.key} style={[containerStyle, styles.plainBackground]}>
|
||||||
|
{card}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
[backgroundImage, carouselItems, itemHeight, parallaxTranslateY]
|
||||||
|
);
|
||||||
|
|
||||||
|
const snapOffsets = useMemo(() => {
|
||||||
|
if (!itemHeight || !isWeb) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return carouselItems.map((_, index) => index * itemHeight);
|
||||||
|
}, [carouselItems, isWeb, itemHeight]);
|
||||||
|
|
||||||
|
const blurIntensity = isWeb ? 80 : 30;
|
||||||
|
const dotsWrapperStyle = useMemo(
|
||||||
|
() => [
|
||||||
|
styles.dotsWrapperBase,
|
||||||
|
isWeb ? styles.dotsWrapperWeb : styles.dotsWrapperNative,
|
||||||
|
],
|
||||||
|
[isWeb]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={[styles.container, isWeb && styles.containerWeb, style]}
|
||||||
|
onLayout={handleLayout}
|
||||||
|
>
|
||||||
|
<Animated.ScrollView
|
||||||
|
ref={scrollViewRef}
|
||||||
|
showsVerticalScrollIndicator={false}
|
||||||
|
pagingEnabled={!isWeb}
|
||||||
|
bounces={false}
|
||||||
|
overScrollMode="never"
|
||||||
|
scrollEventThrottle={16}
|
||||||
|
snapToAlignment="start"
|
||||||
|
snapToInterval={!isWeb && itemHeight ? itemHeight : undefined}
|
||||||
|
snapToOffsets={snapOffsets}
|
||||||
|
decelerationRate={!isWeb ? "fast" : "normal"}
|
||||||
|
style={styles.list}
|
||||||
|
contentContainerStyle={styles.scrollContent}
|
||||||
|
onScroll={animatedScrollHandler}
|
||||||
|
onMomentumScrollEnd={handleScrollEnd}
|
||||||
|
onScrollEndDrag={isWeb ? handleScrollEnd : undefined}
|
||||||
|
>
|
||||||
|
{renderContent}
|
||||||
|
</Animated.ScrollView>
|
||||||
|
<BlurView
|
||||||
|
intensity={blurIntensity}
|
||||||
|
tint={Platform.OS === "web" ? undefined : "dark"}
|
||||||
|
style={dotsWrapperStyle}
|
||||||
|
pointerEvents="none"
|
||||||
|
>
|
||||||
|
<AnimatedPaginationDot
|
||||||
|
data={carouselItems}
|
||||||
|
scrollValue={scrollY}
|
||||||
|
itemDimension={itemHeight}
|
||||||
|
orientation={AnimatedPaginationDot.orientation.VERTICAL}
|
||||||
|
expandingDotSize={24}
|
||||||
|
inactiveDotOpacity={0.3}
|
||||||
|
containerStyle={styles.dotsContainer}
|
||||||
|
dotStyle={styles.dot}
|
||||||
|
baseDotSize={8}
|
||||||
|
/>
|
||||||
|
</BlurView>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FeatureCarousel;
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
width: "100%",
|
||||||
|
flexDirection: "row",
|
||||||
|
backgroundColor: HOME_BACKGROUND_COLOR,
|
||||||
|
},
|
||||||
|
containerWeb: {
|
||||||
|
// paddingRight: 56,
|
||||||
|
},
|
||||||
|
list: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
scrollContent: {
|
||||||
|
flexGrow: 1,
|
||||||
|
},
|
||||||
|
backgroundItem: {
|
||||||
|
width: "100%",
|
||||||
|
justifyContent: "flex-start",
|
||||||
|
},
|
||||||
|
parallaxContainer: {
|
||||||
|
overflow: "hidden",
|
||||||
|
position: "relative",
|
||||||
|
backgroundColor: HOME_BACKGROUND_COLOR,
|
||||||
|
},
|
||||||
|
parallaxImage: {
|
||||||
|
position: "absolute",
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
},
|
||||||
|
backgroundGradient: {
|
||||||
|
...StyleSheet.absoluteFillObject,
|
||||||
|
},
|
||||||
|
plainBackground: {
|
||||||
|
backgroundColor: HOME_BACKGROUND_COLOR,
|
||||||
|
},
|
||||||
|
cardWrapper: {
|
||||||
|
flex: 1,
|
||||||
|
width: "100%",
|
||||||
|
justifyContent: "flex-start",
|
||||||
|
zIndex: 1,
|
||||||
|
},
|
||||||
|
dotsWrapperBase: {
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
borderRadius: 16,
|
||||||
|
paddingVertical: 12,
|
||||||
|
paddingHorizontal: 8,
|
||||||
|
overflow: "hidden",
|
||||||
|
backgroundColor: "rgba(18, 18, 18, 0.2)",
|
||||||
|
},
|
||||||
|
dotsWrapperWeb: {
|
||||||
|
position: "absolute",
|
||||||
|
right: 12,
|
||||||
|
top: 0,
|
||||||
|
bottom: 0,
|
||||||
|
maxHeight: "75%",
|
||||||
|
alignSelf: "center",
|
||||||
|
},
|
||||||
|
dotsWrapperNative: {
|
||||||
|
marginLeft: 12,
|
||||||
|
alignSelf: "center",
|
||||||
|
},
|
||||||
|
dotsContainer: {
|
||||||
|
flexDirection: "column",
|
||||||
|
},
|
||||||
|
dot: {
|
||||||
|
width: 8,
|
||||||
|
height: 8,
|
||||||
|
marginHorizontal: 0,
|
||||||
|
marginVertical: 6,
|
||||||
|
borderRadius: 999,
|
||||||
|
backgroundColor: "rgba(255,255,255,0.4)",
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -18,6 +18,7 @@ export default function PersonaCard({ item, index, isLock = false, height }) {
|
|||||||
justifyContent: "center",
|
justifyContent: "center",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
height: cardHeight,
|
height: cardHeight,
|
||||||
|
width: "100%",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<View
|
<View
|
||||||
|
|||||||
+1
-1
@@ -75,7 +75,7 @@ export default ({
|
|||||||
style={{
|
style={{
|
||||||
height: "100%",
|
height: "100%",
|
||||||
paddingTop: isWeb ? gutters / 2 : 0,
|
paddingTop: isWeb ? gutters / 2 : 0,
|
||||||
padding: gutters,
|
// padding: gutters,
|
||||||
paddingBottom: 0,
|
paddingBottom: 0,
|
||||||
...(isWeb ? { maxHeight: "100svh" } : {}),
|
...(isWeb ? { maxHeight: "100svh" } : {}),
|
||||||
width: computedWidth,
|
width: computedWidth,
|
||||||
|
|||||||
@@ -249,7 +249,11 @@ const Home = () => {
|
|||||||
const continueDisabled = !hasActiveProject || stageLocked || !stageRoute;
|
const continueDisabled = !hasActiveProject || stageLocked || !stageRoute;
|
||||||
const startDisabled = !songwriterAction?.route;
|
const startDisabled = !songwriterAction?.route;
|
||||||
return (
|
return (
|
||||||
<Page shareBtn backgroundImg={background.homeBGWeb} headerType="NONE">
|
<Page
|
||||||
|
shareBtn
|
||||||
|
headerType="NONE"
|
||||||
|
containerStyle={{ width: "100%", maxWidth: "100%" }}
|
||||||
|
>
|
||||||
<View style={styles.root}>
|
<View style={styles.root}>
|
||||||
<View
|
<View
|
||||||
style={{
|
style={{
|
||||||
@@ -291,6 +295,7 @@ const Home = () => {
|
|||||||
stageStates={stageStates}
|
stageStates={stageStates}
|
||||||
activeIndex={activeStageIndex}
|
activeIndex={activeStageIndex}
|
||||||
onActiveIndexChange={setActiveStageIndex}
|
onActiveIndexChange={setActiveStageIndex}
|
||||||
|
backgroundImage={background.homeBGWeb}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View
|
<View
|
||||||
@@ -342,6 +347,7 @@ export default Home;
|
|||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
root: {
|
root: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
|
backgroundColor: "#425B87",
|
||||||
},
|
},
|
||||||
heroImage: {
|
heroImage: {
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
|
|||||||
Reference in New Issue
Block a user