feat: fixes and formatter

This commit is contained in:
2026-01-12 16:01:32 +01:00
parent 85c6084351
commit 11e632acff
353 changed files with 23315 additions and 27361 deletions
@@ -1,365 +1,330 @@
import { BlurView } from "expo-blur";
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import {
Image,
Pressable,
StyleSheet,
Text,
View,
useWindowDimensions,
} from "react-native";
import { icons, img } from "../../../assets";
import KaraokeLyrics from "../../../components/KaraokeLyrics";
import { usersRef } from "../../../config/firebase";
import { Routes } from "../../../navigation";
import { navigate } from "../../../navigation/NavigationService";
import { useUser } from "../../../providers/UserDataProvider";
import { Palette } from "../../../styles";
import { FONT_FAMILY } from "../../../styles/Fonts";
import { size } from "../../../styles/Style";
import CommentsPanel from "./CommentsPanel.web";
import {
getProjectLikes,
LIKE_TARGET,
toggleProjectLike,
} from "../../../utils/likes";
import { ensureAuthenticated } from "../../../utils/authRedirect";
import {
createPlaybackSharePayload,
openShareSheet,
} from "../../../utils/shareSheet";
import { SheetManager } from "react-native-actions-sheet";
import { Feather } from "@expo/vector-icons";
import { BlurView } from 'expo-blur'
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Image, Pressable, StyleSheet, Text, View, useWindowDimensions } from 'react-native'
import { icons, img } from '../../../assets'
import KaraokeLyrics from '../../../components/KaraokeLyrics'
import { usersRef } from '../../../config/firebase'
import { Routes } from '../../../navigation'
import { navigate } from '../../../navigation/NavigationService'
import { useUser } from '../../../providers/UserDataProvider'
import { Palette } from '../../../styles'
import { FONT_FAMILY } from '../../../styles/Fonts'
import { size } from '../../../styles/Style'
import CommentsPanel from './CommentsPanel.web'
import { getProjectLikes, LIKE_TARGET, toggleProjectLike } from '../../../utils/likes'
import { ensureAuthenticated } from '../../../utils/authRedirect'
import { createPlaybackSharePayload, openShareSheet } from '../../../utils/shareSheet'
import { SheetManager } from 'react-native-actions-sheet'
import { Feather } from '@expo/vector-icons'
// Debug logging toggle for web playback
const DEBUG_PLAYBACK_WEB = true;
const DEBUG_PLAYBACK_WEB = true
// NOTE: Web-only implementation that uses a native <video> element instead of expo-video
// - No Platform checks
// - Audio now comes directly from the MP4 playbackUrl
const PlaybackItem = ({
item,
isActive,
userCache,
getUserByUid,
onBackgroundSync,
}) => {
const { width: viewportWidth, height: viewportHeight } =
useWindowDimensions();
const PlaybackItem = ({ item, isActive, userCache, getUserByUid, onBackgroundSync }) => {
const { width: viewportWidth, height: viewportHeight } = useWindowDimensions()
const [layoutSize, setLayoutSize] = useState({
width: viewportWidth,
height: viewportHeight,
});
const [openComments] = useState(true);
const commentInputRef = useRef(null);
const { currentUID, followUser, unfollowUser } = useUser() || {};
})
const [openComments] = useState(true)
const commentInputRef = useRef(null)
const { currentUID, followUser, unfollowUser } = useUser() || {}
const videoUrl = item?.playbackUrl || null;
const videoRef = useRef(null); // HTMLVideoElement
const wasActiveRef = useRef(false);
const startedRef = useRef(false);
const videoUrl = item?.playbackUrl || null
const videoRef = useRef(null) // HTMLVideoElement
const wasActiveRef = useRef(false)
const startedRef = useRef(false)
const initialLikedBy = getProjectLikes(item, LIKE_TARGET.PLAYBACK);
const [isLiked, setIsLiked] = useState(
currentUID ? initialLikedBy.includes(currentUID) : false
);
const [likesCount, setLikesCount] = useState(initialLikedBy.length);
const initialLikedBy = getProjectLikes(item, LIKE_TARGET.PLAYBACK)
const [isLiked, setIsLiked] = useState(currentUID ? initialLikedBy.includes(currentUID) : false)
const [likesCount, setLikesCount] = useState(initialLikedBy.length)
const computedCommentsCount = useMemo(
() => Number(item?.commentsCount || 0),
[item?.commentsCount]
);
const [commentsCount, setCommentsCount] = useState(computedCommentsCount);
)
const [commentsCount, setCommentsCount] = useState(computedCommentsCount)
useEffect(() => {
setCommentsCount(computedCommentsCount);
}, [computedCommentsCount]);
setCommentsCount(computedCommentsCount)
}, [computedCommentsCount])
const [owner, setOwner] = useState(
item?.userId && userCache?.current?.get(item.userId)
? userCache.current.get(item.userId)
: null
);
item?.userId && userCache?.current?.get(item.userId) ? userCache.current.get(item.userId) : null
)
useEffect(() => {
let cancelled = false;
let cancelled = false
const run = async () => {
try {
const uid = item?.userId;
if (!uid || !userCache) return;
const cached = userCache.current.get(uid);
const uid = item?.userId
if (!uid || !userCache) return
const cached = userCache.current.get(uid)
if (cached) {
if (!cancelled) setOwner(cached);
return;
if (!cancelled) setOwner(cached)
return
}
const user = await getUserByUid?.(uid);
const user = await getUserByUid?.(uid)
if (!cancelled && user) {
userCache.current.set(uid, user);
setOwner(user);
userCache.current.set(uid, user)
setOwner(user)
}
} catch (_e) {}
};
run();
}
run()
return () => {
cancelled = true;
};
}, [item?.userId, userCache, getUserByUid]);
cancelled = true
}
}, [item?.userId, userCache, getUserByUid])
useEffect(() => {
const uid = item?.userId;
if (!uid) return;
const uid = item?.userId
if (!uid) return
const unsub = usersRef.doc(uid).onSnapshot(
(doc) => {
if (doc?.exists) {
const data = { id: doc.id, ...doc.data() };
setOwner(data);
const data = { id: doc.id, ...doc.data() }
setOwner(data)
try {
userCache?.current?.set(uid, data);
userCache?.current?.set(uid, data)
} catch (_e) {}
}
},
() => {}
);
return () => unsub?.();
}, [item?.userId, userCache]);
)
return () => unsub?.()
}, [item?.userId, userCache])
const [isFollowing, setIsFollowing] = useState(false);
const [isFollowing, setIsFollowing] = useState(false)
useEffect(() => {
const list = Array.isArray(owner?.followedBy) ? owner.followedBy : [];
setIsFollowing(currentUID ? list.includes(currentUID) : false);
}, [owner?.followedBy, currentUID]);
const list = Array.isArray(owner?.followedBy) ? owner.followedBy : []
setIsFollowing(currentUID ? list.includes(currentUID) : false)
}, [owner?.followedBy, currentUID])
useEffect(() => {
const lb = getProjectLikes(item, LIKE_TARGET.PLAYBACK);
setLikesCount(lb.length);
setIsLiked(currentUID ? lb.includes(currentUID) : false);
}, [item?.likes?.playback, currentUID]);
const lb = getProjectLikes(item, LIKE_TARGET.PLAYBACK)
setLikesCount(lb.length)
setIsLiked(currentUID ? lb.includes(currentUID) : false)
}, [item?.likes?.playback, currentUID])
useEffect(() => {
startedRef.current = false;
}, [videoUrl]);
startedRef.current = false
}, [videoUrl])
// Start/reset when active, using the video's own audio
useEffect(() => {
const el = videoRef.current;
if (!el) return;
const el = videoRef.current
if (!el) return
if (!isActive) {
try {
if (!el.paused) el.pause();
if (!el.paused) el.pause()
} catch (_e) {}
try {
el.muted = true;
el.muted = true
} catch (_e) {}
startedRef.current = false;
onBackgroundSync?.({ isPlaying: false });
return;
startedRef.current = false
onBackgroundSync?.({ isPlaying: false })
return
}
const start = async () => {
try {
if (!startedRef.current) {
startedRef.current = true;
startedRef.current = true
const resetToStart = () => {
try {
el.currentTime = 0;
el.currentTime = 0
} catch (_e) {}
};
}
if (el.readyState >= 1) {
resetToStart();
resetToStart()
} else {
const handleLoaded = () => {
resetToStart();
};
el.addEventListener("loadeddata", handleLoaded, { once: true });
resetToStart()
}
el.addEventListener('loadeddata', handleLoaded, { once: true })
}
}
el.muted = false;
el.muted = false
await el.play().catch((error) => {
if (DEBUG_PLAYBACK_WEB) {
console.log("[PlaybackItem.web] play() rejected", error);
console.log('[PlaybackItem.web] play() rejected', error)
}
});
})
onBackgroundSync?.({
currentTime: Number(el.currentTime || 0),
isPlaying: !el.paused,
});
})
} catch (_e) {
if (DEBUG_PLAYBACK_WEB) {
console.log("[PlaybackItem.web] start error", _e);
console.log('[PlaybackItem.web] start error', _e)
}
}
};
}
start();
}, [isActive, videoUrl, onBackgroundSync]);
start()
}, [isActive, videoUrl, onBackgroundSync])
// Pause all on unmount
useEffect(() => {
const el = videoRef.current;
const el = videoRef.current
return () => {
try {
if (el && !el.paused) el.pause();
if (el && !el.paused) el.pause()
} catch (_e) {}
onBackgroundSync?.({ isPlaying: false });
};
}, [onBackgroundSync]);
onBackgroundSync?.({ isPlaying: false })
}
}, [onBackgroundSync])
// Lyrics timing based on video clock
const alignedWords = useMemo(() => {
const idx = Number(item?.songIndex) || 0;
const ts = item?.musicTimestamps?.[idx];
const arr = Array.isArray(ts?.alignedWords) ? ts.alignedWords : [];
const idx = Number(item?.songIndex) || 0
const ts = item?.musicTimestamps?.[idx]
const arr = Array.isArray(ts?.alignedWords) ? ts.alignedWords : []
return arr.map((w) => ({
word: String(w?.word ?? ""),
word: String(w?.word ?? ''),
startS: Number(w?.startS ?? 0),
endS: Number(w?.endS ?? 0),
}));
}, [item?.musicTimestamps, item?.songIndex]);
}))
}, [item?.musicTimestamps, item?.songIndex])
const [currentTimeS, setCurrentTimeS] = useState(0);
const [currentTimeS, setCurrentTimeS] = useState(0)
useEffect(() => {
if (!isActive) return;
if (!isActive) return
const id = setInterval(() => {
try {
const el = videoRef.current;
const videoTime = Number(el?.currentTime || 0);
const safeTime = Number.isFinite(videoTime) ? videoTime : 0;
setCurrentTimeS(safeTime);
const el = videoRef.current
const videoTime = Number(el?.currentTime || 0)
const safeTime = Number.isFinite(videoTime) ? videoTime : 0
setCurrentTimeS(safeTime)
onBackgroundSync?.({
currentTime: safeTime,
isPlaying: !!el && !el.paused,
});
})
} catch (_e) {}
}, 250);
return () => clearInterval(id);
}, [isActive, onBackgroundSync]);
}, 250)
return () => clearInterval(id)
}, [isActive, onBackgroundSync])
useEffect(() => {
if (!onBackgroundSync) return undefined;
if (!onBackgroundSync) return undefined
if (isActive) {
wasActiveRef.current = true;
return undefined;
wasActiveRef.current = true
return undefined
}
if (wasActiveRef.current) {
onBackgroundSync({ isPlaying: false });
wasActiveRef.current = false;
onBackgroundSync({ isPlaying: false })
wasActiveRef.current = false
}
return undefined;
}, [isActive, onBackgroundSync]);
return undefined
}, [isActive, onBackgroundSync])
const descriptionText =
item?.description || item?.title || "Description chanson";
const descriptionText = item?.description || item?.title || 'Description chanson'
const creatorName = useMemo(() => {
if (owner?.displayName) return owner.displayName;
if (owner?.artistName) return owner.artistName;
if (owner?.userName) return owner.userName;
if (typeof item?.userName === "string") return item.userName;
return "";
}, [item?.userName, owner?.artistName, owner?.displayName, owner?.userName]);
if (owner?.displayName) return owner.displayName
if (owner?.artistName) return owner.artistName
if (owner?.userName) return owner.userName
if (typeof item?.userName === 'string') return item.userName
return ''
}, [item?.userName, owner?.artistName, owner?.displayName, owner?.userName])
const sharePayload = useMemo(() => {
if (!item?.id) return null;
if (!item?.id) return null
return createPlaybackSharePayload({
projectId: item.id,
title: typeof item?.title === "string" ? item.title.trim() : undefined,
title: typeof item?.title === 'string' ? item.title.trim() : undefined,
artist: creatorName || undefined,
playbackUrl: videoUrl || undefined,
});
}, [creatorName, item?.id, item?.title, videoUrl]);
})
}, [creatorName, item?.id, item?.title, videoUrl])
const handleShare = useCallback(() => {
if (sharePayload) {
openShareSheet(sharePayload);
openShareSheet(sharePayload)
}
}, [sharePayload]);
}, [sharePayload])
const handleReport = useCallback(() => {
if (!item?.id) return;
SheetManager.show("Report", {
if (!item?.id) return
SheetManager.show('Report', {
payload: {
targetType: "playback",
targetType: 'playback',
projectId: item?.id || null,
playbackId: item?.id || null,
title: item?.title || "",
title: item?.title || '',
ownerId: item?.userId || null,
},
});
}, [item?.id, item?.title, item?.userId]);
})
}, [item?.id, item?.title, item?.userId])
const handleLayout = useCallback((event) => {
const { width = 0, height = 0 } = event?.nativeEvent?.layout || {};
const { width = 0, height = 0 } = event?.nativeEvent?.layout || {}
setLayoutSize((prev) => ({
width: width > 0 ? width : prev.width,
height: height > 0 ? height : prev.height,
}));
}, []);
}))
}, [])
const layoutWidth = layoutSize.width || viewportWidth;
const layoutHeight = layoutSize.height || viewportHeight;
const layoutWidth = layoutSize.width || viewportWidth
const layoutHeight = layoutSize.height || viewportHeight
const horizontalPadding = 64;
const innerWidth = Math.max(layoutWidth - horizontalPadding * 2, 0);
const gapBetweenColumns = 40;
const minVideoWidth = 420;
const maxVideoWidth = 720;
const minCommentsWidth = 300;
const maxCommentsWidth = 420;
const horizontalPadding = 64
const innerWidth = Math.max(layoutWidth - horizontalPadding * 2, 0)
const gapBetweenColumns = 40
const minVideoWidth = 420
const maxVideoWidth = 720
const minCommentsWidth = 300
const maxCommentsWidth = 420
let desiredHeight = Math.max(420, layoutHeight * 0.8);
let videoWidth = Math.min(
maxVideoWidth,
Math.max(minVideoWidth, innerWidth * 0.58)
);
let videoHeight = videoWidth * (16 / 9);
let desiredHeight = Math.max(420, layoutHeight * 0.8)
let videoWidth = Math.min(maxVideoWidth, Math.max(minVideoWidth, innerWidth * 0.58))
let videoHeight = videoWidth * (16 / 9)
if (videoHeight > desiredHeight) {
videoHeight = desiredHeight;
videoWidth = videoHeight * (9 / 16);
videoHeight = desiredHeight
videoWidth = videoHeight * (9 / 16)
}
let commentsWidth = Math.min(
maxCommentsWidth,
Math.max(minCommentsWidth, innerWidth - videoWidth - gapBetweenColumns)
);
const panelHeight = Math.min(videoHeight, desiredHeight);
)
const panelHeight = Math.min(videoHeight, desiredHeight)
// Attach verbose event listeners on the HTML video element
useEffect(() => {
const el = videoRef.current;
if (!el || !DEBUG_PLAYBACK_WEB) return undefined;
const el = videoRef.current
if (!el || !DEBUG_PLAYBACK_WEB) return undefined
const handler = (e) => {
// Avoid heavy logs: only show key events and brief state
console.log("[PlaybackItem.web] video:", e.type, {
console.log('[PlaybackItem.web] video:', e.type, {
t: Number(el.currentTime || 0).toFixed(2),
paused: el.paused,
rs: el.readyState,
});
};
})
}
const events = [
"loadedmetadata",
"loadeddata",
"play",
"playing",
"pause",
"seeking",
"seeked",
"stalled",
"waiting",
"ended",
"error",
];
events.forEach((ev) => el.addEventListener(ev, handler));
'loadedmetadata',
'loadeddata',
'play',
'playing',
'pause',
'seeking',
'seeked',
'stalled',
'waiting',
'ended',
'error',
]
events.forEach((ev) => el.addEventListener(ev, handler))
return () => {
events.forEach((ev) => el.removeEventListener(ev, handler));
};
}, []);
events.forEach((ev) => el.removeEventListener(ev, handler))
}
}, [])
return (
<View
@@ -368,18 +333,13 @@ const PlaybackItem = ({
styles.itemContainerBase,
styles.itemContainerRow,
{
height: "90%",
height: '90%',
paddingHorizontal: horizontalPadding,
paddingVertical: Math.max((layoutHeight - panelHeight) / 2, 24),
},
]}
>
<View
style={[
styles.videoColumn,
{ width: videoWidth, marginRight: gapBetweenColumns },
]}
>
<View style={[styles.videoColumn, { width: videoWidth, marginRight: gapBetweenColumns }]}>
<View style={[styles.videoSurface, { height: panelHeight }]}>
<View style={styles.videoContainer}>
{!!videoUrl ? (
@@ -392,12 +352,12 @@ const PlaybackItem = ({
preload="auto"
// Using web CSS properties here on purpose
style={{
position: "absolute",
position: 'absolute',
inset: 0,
width: "100%",
height: "100%",
objectFit: "cover",
display: "block",
width: '100%',
height: '100%',
objectFit: 'cover',
display: 'block',
}}
/>
) : (
@@ -413,7 +373,7 @@ const PlaybackItem = ({
<Pressable
style={styles.ownerAvatarButton}
onPress={() => {
navigate(Routes.SingerProfile, { userId: item?.userId });
navigate(Routes.SingerProfile, { userId: item?.userId })
}}
>
{owner?.profilePictureURL && (
@@ -435,38 +395,32 @@ const PlaybackItem = ({
if (
!ensureAuthenticated(currentUID, {
onIntercept: () => {
setIsFollowing(false);
setIsFollowing(false)
},
})
) {
return;
return
}
try {
const next = !isFollowing;
setIsFollowing(next);
const next = !isFollowing
setIsFollowing(next)
setOwner((prev) => {
const fb = Array.isArray(prev?.followedBy)
? prev.followedBy
: [];
const fb = Array.isArray(prev?.followedBy) ? prev.followedBy : []
const newFb = next
? Array.from(new Set([...fb, currentUID]))
: fb.filter((x) => x !== currentUID);
return prev ? { ...prev, followedBy: newFb } : prev;
});
if (next) await followUser?.(owner.id);
else await unfollowUser?.(owner.id);
: fb.filter((x) => x !== currentUID)
return prev ? { ...prev, followedBy: newFb } : prev
})
if (next) await followUser?.(owner.id)
else await unfollowUser?.(owner.id)
} catch (_e) {
setIsFollowing((v) => !v);
setIsFollowing((v) => !v)
}
}}
>
<BlurView
tint="dark"
intensity={20}
style={styles.followButton}
>
<BlurView tint="dark" intensity={20} style={styles.followButton}>
<Text style={styles.followButtonText}>
{isFollowing ? "Suivi(e)" : "Suivre"}
{isFollowing ? 'Suivi(e)' : 'Suivre'}
</Text>
</BlurView>
</Pressable>
@@ -475,25 +429,23 @@ const PlaybackItem = ({
<Pressable
onPress={async () => {
try {
if (!item?.id) return;
if (!item?.id) return
if (!ensureAuthenticated(currentUID)) {
return;
return
}
const nextLiked = !isLiked;
setIsLiked(nextLiked);
setLikesCount((c) => Math.max(0, c + (nextLiked ? 1 : -1)));
const nextLiked = !isLiked
setIsLiked(nextLiked)
setLikesCount((c) => Math.max(0, c + (nextLiked ? 1 : -1)))
await toggleProjectLike({
projectId: item.id,
currentUID,
target: LIKE_TARGET.PLAYBACK,
next: nextLiked,
});
})
} catch (_e) {
setIsLiked((v) => !v);
setLikesCount((c) =>
isLiked ? c + 1 : Math.max(0, c - 1)
);
setIsLiked((v) => !v)
setLikesCount((c) => (isLiked ? c + 1 : Math.max(0, c - 1)))
}
}}
style={[styles.actionButton, styles.actionSpacing]}
@@ -503,37 +455,16 @@ const PlaybackItem = ({
style={styles.actionIcon}
resizeMode="contain"
/>
{!!likesCount && (
<Text style={styles.actionLabel}>{likesCount}</Text>
)}
{!!likesCount && <Text style={styles.actionLabel}>{likesCount}</Text>}
</Pressable>
<Pressable
onPress={() => {}}
style={[styles.actionButton, styles.actionSpacing]}
>
<Image
source={icons.chatBubble}
style={styles.actionIcon}
resizeMode="contain"
/>
{!!commentsCount && (
<Text style={styles.actionLabel}>{commentsCount}</Text>
)}
<Pressable onPress={() => {}} style={[styles.actionButton, styles.actionSpacing]}>
<Image source={icons.chatBubble} style={styles.actionIcon} resizeMode="contain" />
{!!commentsCount && <Text style={styles.actionLabel}>{commentsCount}</Text>}
</Pressable>
<Pressable
onPress={handleShare}
style={[styles.actionButton, styles.actionSpacing]}
>
<Image
source={icons.share}
style={styles.actionIcon}
resizeMode="contain"
/>
<Pressable onPress={handleShare} style={[styles.actionButton, styles.actionSpacing]}>
<Image source={icons.share} style={styles.actionIcon} resizeMode="contain" />
</Pressable>
<Pressable
onPress={handleReport}
style={[styles.actionButton, styles.actionSpacing]}
>
<Pressable onPress={handleReport} style={[styles.actionButton, styles.actionSpacing]}>
<Feather name="flag" size={20} color={Palette.white} />
<Text style={styles.actionLabel}>Signaler</Text>
</Pressable>
@@ -542,10 +473,7 @@ const PlaybackItem = ({
<View style={styles.lyricsContainer}>
<BlurView tint="dark" intensity={20} style={styles.lyricsCard}>
{alignedWords?.length > 0 ? (
<KaraokeLyrics
alignedWords={alignedWords}
currentTimeS={currentTimeS}
/>
<KaraokeLyrics alignedWords={alignedWords} currentTimeS={currentTimeS} />
) : (
<Text style={styles.lyricsText}>{descriptionText}</Text>
)}
@@ -556,52 +484,45 @@ const PlaybackItem = ({
</View>
{openComments && (
<View
style={[
styles.commentsColumn,
{ width: commentsWidth, height: panelHeight },
]}
>
<View style={[styles.commentsColumn, { width: commentsWidth, height: panelHeight }]}>
<CommentsPanel
projectId={item?.id}
description={descriptionText}
commentsCount={commentsCount}
onCommentAdded={() =>
setCommentsCount((c) => Math.max(0, Number(c || 0) + 1))
}
onCommentAdded={() => setCommentsCount((c) => Math.max(0, Number(c || 0) + 1))}
inputRef={commentInputRef}
panelHeight={panelHeight}
/>
</View>
)}
</View>
);
};
)
}
export default PlaybackItem;
export default PlaybackItem
const styles = StyleSheet.create({
itemContainerBase: {
width: "100%",
width: '100%',
paddingVertical: 36,
alignItems: "center",
alignItems: 'center',
},
itemContainerRow: {
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},
videoColumn: {
alignItems: "center",
justifyContent: "center",
alignItems: 'center',
justifyContent: 'center',
},
videoSurface: {
alignSelf: "center",
alignSelf: 'center',
aspectRatio: 9 / 16,
borderRadius: 32,
overflow: "hidden",
backgroundColor: "#07040D",
shadowColor: "#000",
overflow: 'hidden',
backgroundColor: '#07040D',
shadowColor: '#000',
shadowOpacity: 0.45,
shadowRadius: 30,
shadowOffset: { width: 0, height: 20 },
@@ -609,16 +530,16 @@ const styles = StyleSheet.create({
},
videoContainer: {
flex: 1,
position: "relative",
position: 'relative',
},
actionStack: {
position: "absolute",
position: 'absolute',
right: 18,
top: 150,
alignItems: "center",
alignItems: 'center',
},
ownerBlock: {
alignItems: "center",
alignItems: 'center',
marginBottom: 26,
},
ownerAvatarButton: {
@@ -629,7 +550,7 @@ const styles = StyleSheet.create({
borderRadius: 100,
},
followPressable: {
alignSelf: "center",
alignSelf: 'center',
},
followButton: {
paddingVertical: 8,
@@ -637,8 +558,8 @@ const styles = StyleSheet.create({
borderRadius: 14,
borderWidth: 1,
borderColor: Palette.white,
backgroundColor: "#FFFFFF20",
overflow: "hidden",
backgroundColor: '#FFFFFF20',
overflow: 'hidden',
},
followButtonText: {
fontSize: 14,
@@ -646,8 +567,8 @@ const styles = StyleSheet.create({
fontFamily: FONT_FAMILY.InterMedium,
},
actionButton: {
alignItems: "center",
justifyContent: "center",
alignItems: 'center',
justifyContent: 'center',
},
actionSpacing: {
marginTop: 24,
@@ -660,10 +581,10 @@ const styles = StyleSheet.create({
fontSize: 13,
color: Palette.white,
fontFamily: FONT_FAMILY.InterMedium,
textAlign: "center",
textAlign: 'center',
},
lyricsContainer: {
position: "absolute",
position: 'absolute',
left: 24,
right: 24,
bottom: 32,
@@ -673,7 +594,7 @@ const styles = StyleSheet.create({
paddingVertical: 12,
borderRadius: 22,
backgroundColor: Palette.glass,
overflow: "hidden",
overflow: 'hidden',
},
lyricsText: {
fontSize: 14,
@@ -681,6 +602,6 @@ const styles = StyleSheet.create({
fontFamily: FONT_FAMILY.InterRegular,
},
commentsColumn: {
alignSelf: "stretch",
alignSelf: 'stretch',
},
});
})