feat: fixes and formatter
This commit is contained in:
+214
-251
@@ -1,5 +1,5 @@
|
||||
import React, { useCallback, useMemo, useState } from "react";
|
||||
import { Image as ExpoImage } from "expo-image";
|
||||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
import { Image as ExpoImage } from 'expo-image'
|
||||
import {
|
||||
Linking,
|
||||
Platform,
|
||||
@@ -9,73 +9,70 @@ import {
|
||||
Text,
|
||||
View,
|
||||
Alert,
|
||||
} from "react-native";
|
||||
import useMinuit from "react-native-minuit/src/hooks/useMinuit.js";
|
||||
import * as FileSystem from "expo-file-system";
|
||||
import * as Sharing from "expo-sharing";
|
||||
import AppCheckbox from "../../components/AppCheckbox";
|
||||
import BorderGradientButton from "../../components/BorderGradientButton";
|
||||
import MusicLandHeader from "../../components/MusicLandHeader";
|
||||
import Page from "../../layouts/Page";
|
||||
import { Routes } from "../../navigation/Routes";
|
||||
import { goBack, navigate } from "../../navigation/NavigationService";
|
||||
import { useUser } from "../../providers/UserDataProvider";
|
||||
import { gutters, Palette, Style } from "../../styles";
|
||||
import { FONT_FAMILY } from "../../styles/Fonts";
|
||||
import CreateLyricsHeader from "../Writing/components/CreateLyricsHeader";
|
||||
import { background } from "../../assets";
|
||||
import { getStageAction } from "../../utils/projectStages";
|
||||
import ClubAdvantagesCard from "../Profile/components/ClubAdvantagesCard";
|
||||
import useGlobalLoading from "../../hooks/useGlobalLoading";
|
||||
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import { isWeb } from "../../hooks/useLayoutType";
|
||||
import { getArtistDisplayName } from "../../utils/artistName";
|
||||
import { toDate } from "../../utils/dateFormatting";
|
||||
import SubscriptionConfirmModal from "../../components/SubscriptionConfirmModal";
|
||||
} from 'react-native'
|
||||
import useMinuit from 'react-native-minuit/src/hooks/useMinuit.js'
|
||||
import * as FileSystem from 'expo-file-system'
|
||||
import * as Sharing from 'expo-sharing'
|
||||
import AppCheckbox from '../../components/AppCheckbox'
|
||||
import BorderGradientButton from '../../components/BorderGradientButton'
|
||||
import MusicLandHeader from '../../components/MusicLandHeader'
|
||||
import Page from '../../layouts/Page'
|
||||
import { Routes } from '../../navigation/Routes'
|
||||
import { goBack, navigate } from '../../navigation/NavigationService'
|
||||
import { useUser } from '../../providers/UserDataProvider'
|
||||
import { gutters, Palette, Style } from '../../styles'
|
||||
import { FONT_FAMILY } from '../../styles/Fonts'
|
||||
import CreateLyricsHeader from '../Writing/components/CreateLyricsHeader'
|
||||
import { background } from '../../assets'
|
||||
import { getStageAction } from '../../utils/projectStages'
|
||||
import ClubAdvantagesCard from '../Profile/components/ClubAdvantagesCard'
|
||||
import useGlobalLoading from '../../hooks/useGlobalLoading'
|
||||
import { MaterialCommunityIcons } from '@expo/vector-icons'
|
||||
import { isWeb } from '../../hooks/useLayoutType'
|
||||
import { getArtistDisplayName } from '../../utils/artistName'
|
||||
import { toDate } from '../../utils/dateFormatting'
|
||||
import SubscriptionConfirmModal from '../../components/SubscriptionConfirmModal'
|
||||
|
||||
const SongDownload = ({ route }) => {
|
||||
const {
|
||||
project: routeProject,
|
||||
selectedOption: routeSelectedOption,
|
||||
coverOptions: routeCoverOptions,
|
||||
} = route?.params || {};
|
||||
const { selectedProject, updateProjectData, hasActiveSubscription } =
|
||||
useUser();
|
||||
const { setTooltip } = useMinuit();
|
||||
const { setLoading } = useGlobalLoading();
|
||||
const [isDownloading, setIsDownloading] = useState(false);
|
||||
const [showConfirmModal, setShowConfirmModal] = useState(false);
|
||||
const [hasAcceptedPublication, setHasAcceptedPublication] = useState(true);
|
||||
} = route?.params || {}
|
||||
const { selectedProject, updateProjectData, hasActiveSubscription } = useUser()
|
||||
const { setTooltip } = useMinuit()
|
||||
const { setLoading } = useGlobalLoading()
|
||||
const [isDownloading, setIsDownloading] = useState(false)
|
||||
const [showConfirmModal, setShowConfirmModal] = useState(false)
|
||||
const [hasAcceptedPublication, setHasAcceptedPublication] = useState(true)
|
||||
|
||||
const projectForStage = useMemo(
|
||||
() => routeProject || selectedProject || null,
|
||||
[routeProject, selectedProject],
|
||||
);
|
||||
[routeProject, selectedProject]
|
||||
)
|
||||
|
||||
const coverOptions = useMemo(() => {
|
||||
if (Array.isArray(routeCoverOptions) && routeCoverOptions.length) {
|
||||
return routeCoverOptions.filter(Boolean);
|
||||
return routeCoverOptions.filter(Boolean)
|
||||
}
|
||||
if (Array.isArray(projectForStage?.cover?.options)) {
|
||||
return projectForStage.cover.options.filter(Boolean);
|
||||
return projectForStage.cover.options.filter(Boolean)
|
||||
}
|
||||
return [];
|
||||
}, [projectForStage?.cover?.options, routeCoverOptions]);
|
||||
return []
|
||||
}, [projectForStage?.cover?.options, routeCoverOptions])
|
||||
|
||||
const selectedOption = useMemo(() => {
|
||||
if (routeSelectedOption) {
|
||||
return routeSelectedOption;
|
||||
return routeSelectedOption
|
||||
}
|
||||
const selectedId =
|
||||
projectForStage?.cover?.selectedOptionId ||
|
||||
projectForStage?.cover?.selectedOption?.id ||
|
||||
null;
|
||||
projectForStage?.cover?.selectedOptionId || projectForStage?.cover?.selectedOption?.id || null
|
||||
if (selectedId && coverOptions.length) {
|
||||
const match = coverOptions.find((option) => option?.id === selectedId);
|
||||
if (match) return match;
|
||||
const match = coverOptions.find((option) => option?.id === selectedId)
|
||||
if (match) return match
|
||||
}
|
||||
return coverOptions[0] || null;
|
||||
}, [coverOptions, projectForStage?.cover, routeSelectedOption]);
|
||||
return coverOptions[0] || null
|
||||
}, [coverOptions, projectForStage?.cover, routeSelectedOption])
|
||||
|
||||
const coverUrl =
|
||||
selectedOption?.finalUrl ||
|
||||
@@ -83,46 +80,44 @@ const SongDownload = ({ route }) => {
|
||||
projectForStage?.coverUrl ||
|
||||
projectForStage?.cover?.result ||
|
||||
projectForStage?.cover?.generatedBackground ||
|
||||
null;
|
||||
null
|
||||
const trackTitle =
|
||||
typeof projectForStage?.title === "string" && projectForStage.title.trim()
|
||||
typeof projectForStage?.title === 'string' && projectForStage.title.trim()
|
||||
? projectForStage.title.trim()
|
||||
: "Musicland Track";
|
||||
: 'Musicland Track'
|
||||
|
||||
const continueFlow = useCallback(async () => {
|
||||
if (!selectedOption) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
try {
|
||||
await setLoading(true, { message: "Sauvegarde de ta pochette..." });
|
||||
const existingCover = projectForStage?.cover || {};
|
||||
const finalUrl =
|
||||
selectedOption.finalUrl || selectedOption.generatedUrl || coverUrl;
|
||||
await setLoading(true, { message: 'Sauvegarde de ta pochette...' })
|
||||
const existingCover = projectForStage?.cover || {}
|
||||
const finalUrl = selectedOption.finalUrl || selectedOption.generatedUrl || coverUrl
|
||||
const nextCoverData = {
|
||||
...existingCover,
|
||||
options: coverOptions,
|
||||
selectedOptionId: selectedOption.id,
|
||||
result: finalUrl,
|
||||
generatedBackground:
|
||||
selectedOption.generatedUrl || selectedOption.finalUrl || null,
|
||||
};
|
||||
generatedBackground: selectedOption.generatedUrl || selectedOption.finalUrl || null,
|
||||
}
|
||||
await updateProjectData({
|
||||
cover: nextCoverData,
|
||||
coverUrl: finalUrl,
|
||||
});
|
||||
})
|
||||
const nextProject = {
|
||||
...projectForStage,
|
||||
cover: nextCoverData,
|
||||
coverUrl: finalUrl,
|
||||
};
|
||||
const nextPlaybackStage = getStageAction("director", nextProject);
|
||||
const targetRoute = nextPlaybackStage?.route || Routes.Playback;
|
||||
const params = nextPlaybackStage?.params || { project: nextProject };
|
||||
navigate(targetRoute, params);
|
||||
}
|
||||
const nextPlaybackStage = getStageAction('director', nextProject)
|
||||
const targetRoute = nextPlaybackStage?.route || Routes.Playback
|
||||
const params = nextPlaybackStage?.params || { project: nextProject }
|
||||
navigate(targetRoute, params)
|
||||
} catch (error) {
|
||||
console.log("[SongDownload] continue error", error?.message);
|
||||
console.log('[SongDownload] continue error', error?.message)
|
||||
} finally {
|
||||
await setLoading(false);
|
||||
await setLoading(false)
|
||||
}
|
||||
}, [
|
||||
coverOptions,
|
||||
@@ -132,29 +127,29 @@ const SongDownload = ({ route }) => {
|
||||
selectedOption,
|
||||
setLoading,
|
||||
updateProjectData,
|
||||
]);
|
||||
])
|
||||
|
||||
const handleContinue = useCallback(() => {
|
||||
if (!hasAcceptedPublication) {
|
||||
if (setTooltip) {
|
||||
setTooltip({
|
||||
type: "error",
|
||||
text: "Confirme la diffusion sur Musicland et YouTube avant de continuer",
|
||||
});
|
||||
type: 'error',
|
||||
text: 'Confirme la diffusion sur Musicland et YouTube avant de continuer',
|
||||
})
|
||||
} else {
|
||||
Alert.alert(
|
||||
"Confirmation requise",
|
||||
"Confirme la diffusion sur Musicland et YouTube avant de continuer"
|
||||
);
|
||||
'Confirmation requise',
|
||||
'Confirme la diffusion sur Musicland et YouTube avant de continuer'
|
||||
)
|
||||
}
|
||||
return;
|
||||
return
|
||||
}
|
||||
if (hasActiveSubscription) {
|
||||
continueFlow();
|
||||
return;
|
||||
continueFlow()
|
||||
return
|
||||
}
|
||||
setShowConfirmModal(true);
|
||||
}, [continueFlow, hasAcceptedPublication, hasActiveSubscription, setTooltip]);
|
||||
setShowConfirmModal(true)
|
||||
}, [continueFlow, hasAcceptedPublication, hasActiveSubscription, setTooltip])
|
||||
|
||||
const handleDownload = useCallback(async () => {
|
||||
const downloadUrl =
|
||||
@@ -162,226 +157,208 @@ const SongDownload = ({ route }) => {
|
||||
projectForStage?.playbackUrl ||
|
||||
selectedOption?.finalUrl ||
|
||||
selectedOption?.generatedUrl ||
|
||||
null;
|
||||
null
|
||||
if (!downloadUrl || isDownloading) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
await setLoading(true, { message: "Préparation du téléchargement..." });
|
||||
const artist = getArtistDisplayName(projectForStage, "MusicLand");
|
||||
const createdDate = toDate(projectForStage?.createdAt) || new Date();
|
||||
const createdLabel = createdDate
|
||||
? createdDate.toISOString().split("T")[0]
|
||||
: "";
|
||||
await setLoading(true, { message: 'Préparation du téléchargement...' })
|
||||
const artist = getArtistDisplayName(projectForStage, 'MusicLand')
|
||||
const createdDate = toDate(projectForStage?.createdAt) || new Date()
|
||||
const createdLabel = createdDate ? createdDate.toISOString().split('T')[0] : ''
|
||||
|
||||
const triggerWebDownload = async (url, title) => {
|
||||
setIsDownloading(true);
|
||||
await setLoading(true, { message: "Préparation du téléchargement..." });
|
||||
setIsDownloading(true)
|
||||
await setLoading(true, { message: 'Préparation du téléchargement...' })
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
const response = await fetch(url)
|
||||
if (!response.ok) {
|
||||
throw new Error(`download_failed_${response.status}`);
|
||||
throw new Error(`download_failed_${response.status}`)
|
||||
}
|
||||
const contentType =
|
||||
response.headers.get("content-type") || "audio/mpeg";
|
||||
const contentType = response.headers.get('content-type') || 'audio/mpeg'
|
||||
const baseName =
|
||||
String(trackTitle || "musicland-track")
|
||||
.replace(/[\\/:*?"<>|]+/g, "-")
|
||||
.trim() || "musicland-track";
|
||||
const filename = `${baseName}.mp3`;
|
||||
const buffer = await response.arrayBuffer();
|
||||
String(trackTitle || 'musicland-track')
|
||||
.replace(/[\\/:*?"<>|]+/g, '-')
|
||||
.trim() || 'musicland-track'
|
||||
const filename = `${baseName}.mp3`
|
||||
const buffer = await response.arrayBuffer()
|
||||
|
||||
if (contentType.includes("audio")) {
|
||||
if (contentType.includes('audio')) {
|
||||
const toSynchSafe = (size) => {
|
||||
const out = new Uint8Array(4);
|
||||
out[0] = (size >> 21) & 0x7f;
|
||||
out[1] = (size >> 14) & 0x7f;
|
||||
out[2] = (size >> 7) & 0x7f;
|
||||
out[3] = size & 0x7f;
|
||||
return out;
|
||||
};
|
||||
const out = new Uint8Array(4)
|
||||
out[0] = (size >> 21) & 0x7f
|
||||
out[1] = (size >> 14) & 0x7f
|
||||
out[2] = (size >> 7) & 0x7f
|
||||
out[3] = size & 0x7f
|
||||
return out
|
||||
}
|
||||
|
||||
const concatBytes = (...arrays) => {
|
||||
const totalLength = arrays.reduce(
|
||||
(sum, arr) => sum + arr.length,
|
||||
0,
|
||||
);
|
||||
const result = new Uint8Array(totalLength);
|
||||
let offset = 0;
|
||||
const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0)
|
||||
const result = new Uint8Array(totalLength)
|
||||
let offset = 0
|
||||
arrays.forEach((arr) => {
|
||||
result.set(arr, offset);
|
||||
offset += arr.length;
|
||||
});
|
||||
return result;
|
||||
};
|
||||
result.set(arr, offset)
|
||||
offset += arr.length
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
const buildTextFrame = (id, value) => {
|
||||
const encoder = new TextEncoder();
|
||||
const textBytes = encoder.encode(value || "");
|
||||
const data = concatBytes(new Uint8Array([0x03]), textBytes);
|
||||
const encoder = new TextEncoder()
|
||||
const textBytes = encoder.encode(value || '')
|
||||
const data = concatBytes(new Uint8Array([0x03]), textBytes)
|
||||
const header = concatBytes(
|
||||
new TextEncoder().encode(id),
|
||||
toSynchSafe(data.length),
|
||||
new Uint8Array([0x00, 0x00]),
|
||||
);
|
||||
return concatBytes(header, data);
|
||||
};
|
||||
new Uint8Array([0x00, 0x00])
|
||||
)
|
||||
return concatBytes(header, data)
|
||||
}
|
||||
|
||||
const buildApicFrame = (imageBytes, mime) => {
|
||||
if (!imageBytes) return null;
|
||||
const encoder = new TextEncoder();
|
||||
const mimeBytes = encoder.encode(mime || "image/jpeg");
|
||||
if (!imageBytes) return null
|
||||
const encoder = new TextEncoder()
|
||||
const mimeBytes = encoder.encode(mime || 'image/jpeg')
|
||||
const data = concatBytes(
|
||||
new Uint8Array([0x03]),
|
||||
mimeBytes,
|
||||
new Uint8Array([0x00]), // mime terminator
|
||||
new Uint8Array([0x03]), // front cover
|
||||
new Uint8Array([0x00]), // empty description
|
||||
new Uint8Array(imageBytes),
|
||||
);
|
||||
new Uint8Array(imageBytes)
|
||||
)
|
||||
const header = concatBytes(
|
||||
new TextEncoder().encode("APIC"),
|
||||
new TextEncoder().encode('APIC'),
|
||||
toSynchSafe(data.length),
|
||||
new Uint8Array([0x00, 0x00]),
|
||||
);
|
||||
return concatBytes(header, data);
|
||||
};
|
||||
new Uint8Array([0x00, 0x00])
|
||||
)
|
||||
return concatBytes(header, data)
|
||||
}
|
||||
|
||||
const buildId3Tag = (audioBytes, coverBytes, coverMime) => {
|
||||
const frames = [];
|
||||
frames.push(buildTextFrame("TIT2", trackTitle));
|
||||
frames.push(buildTextFrame("TPE1", artist));
|
||||
frames.push(buildTextFrame("TDRC", createdLabel));
|
||||
const apic = buildApicFrame(coverBytes, coverMime);
|
||||
if (apic) frames.push(apic);
|
||||
const frames = []
|
||||
frames.push(buildTextFrame('TIT2', trackTitle))
|
||||
frames.push(buildTextFrame('TPE1', artist))
|
||||
frames.push(buildTextFrame('TDRC', createdLabel))
|
||||
const apic = buildApicFrame(coverBytes, coverMime)
|
||||
if (apic) frames.push(apic)
|
||||
|
||||
const framesData = concatBytes(...frames);
|
||||
const framesData = concatBytes(...frames)
|
||||
const header = concatBytes(
|
||||
new TextEncoder().encode("ID3"),
|
||||
new TextEncoder().encode('ID3'),
|
||||
new Uint8Array([0x04, 0x00]), // version 2.4.0
|
||||
new Uint8Array([0x00]), // flags
|
||||
toSynchSafe(framesData.length),
|
||||
);
|
||||
return concatBytes(header, framesData, audioBytes);
|
||||
};
|
||||
toSynchSafe(framesData.length)
|
||||
)
|
||||
return concatBytes(header, framesData, audioBytes)
|
||||
}
|
||||
|
||||
const fetchCoverBytes = async () => {
|
||||
if (!coverUrl) return { bytes: null, mime: null };
|
||||
if (!coverUrl) return { bytes: null, mime: null }
|
||||
try {
|
||||
const res = await fetch(coverUrl);
|
||||
const mime = res.headers?.get("content-type") || "image/jpeg";
|
||||
const bufferImage = await res.arrayBuffer();
|
||||
return { bytes: new Uint8Array(bufferImage), mime };
|
||||
const res = await fetch(coverUrl)
|
||||
const mime = res.headers?.get('content-type') || 'image/jpeg'
|
||||
const bufferImage = await res.arrayBuffer()
|
||||
return { bytes: new Uint8Array(bufferImage), mime }
|
||||
} catch {
|
||||
return { bytes: null, mime: null };
|
||||
return { bytes: null, mime: null }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const { bytes: coverBytes, mime: coverMime } =
|
||||
await fetchCoverBytes();
|
||||
const merged = buildId3Tag(
|
||||
new Uint8Array(buffer),
|
||||
coverBytes,
|
||||
coverMime,
|
||||
);
|
||||
const blob = new Blob([merged], { type: contentType });
|
||||
const blobUrl = URL.createObjectURL(blob);
|
||||
const downloadLink = document.createElement("a");
|
||||
downloadLink.href = blobUrl;
|
||||
downloadLink.download = filename;
|
||||
document.body.appendChild(downloadLink);
|
||||
downloadLink.click();
|
||||
document.body.removeChild(downloadLink);
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
const { bytes: coverBytes, mime: coverMime } = await fetchCoverBytes()
|
||||
const merged = buildId3Tag(new Uint8Array(buffer), coverBytes, coverMime)
|
||||
const blob = new Blob([merged], { type: contentType })
|
||||
const blobUrl = URL.createObjectURL(blob)
|
||||
const downloadLink = document.createElement('a')
|
||||
downloadLink.href = blobUrl
|
||||
downloadLink.download = filename
|
||||
document.body.appendChild(downloadLink)
|
||||
downloadLink.click()
|
||||
document.body.removeChild(downloadLink)
|
||||
URL.revokeObjectURL(blobUrl)
|
||||
} else {
|
||||
const blob = await response.blob();
|
||||
const blobUrl = URL.createObjectURL(blob);
|
||||
const downloadLink = document.createElement("a");
|
||||
downloadLink.href = blobUrl;
|
||||
downloadLink.download = filename;
|
||||
document.body.appendChild(downloadLink);
|
||||
downloadLink.click();
|
||||
document.body.removeChild(downloadLink);
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
const blob = await response.blob()
|
||||
const blobUrl = URL.createObjectURL(blob)
|
||||
const downloadLink = document.createElement('a')
|
||||
downloadLink.href = blobUrl
|
||||
downloadLink.download = filename
|
||||
document.body.appendChild(downloadLink)
|
||||
downloadLink.click()
|
||||
document.body.removeChild(downloadLink)
|
||||
URL.revokeObjectURL(blobUrl)
|
||||
}
|
||||
} finally {
|
||||
setIsDownloading(false);
|
||||
setIsDownloading(false)
|
||||
}
|
||||
};
|
||||
|
||||
if (isWeb) {
|
||||
await triggerWebDownload(downloadUrl, projectForStage?.title);
|
||||
await setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsDownloading(true);
|
||||
if (isWeb) {
|
||||
await triggerWebDownload(downloadUrl, projectForStage?.title)
|
||||
await setLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
setIsDownloading(true)
|
||||
try {
|
||||
const baseName =
|
||||
String(trackTitle || "musicland-track")
|
||||
.replace(/[\\/:*?"<>|]+/g, "-")
|
||||
.trim() || "musicland-track";
|
||||
const fileName = `${baseName}.mp3`;
|
||||
const targetUri = `${FileSystem.cacheDirectory || ""}${fileName}`;
|
||||
String(trackTitle || 'musicland-track')
|
||||
.replace(/[\\/:*?"<>|]+/g, '-')
|
||||
.trim() || 'musicland-track'
|
||||
const fileName = `${baseName}.mp3`
|
||||
const targetUri = `${FileSystem.cacheDirectory || ''}${fileName}`
|
||||
|
||||
const downloadResult = await FileSystem.downloadAsync(
|
||||
downloadUrl,
|
||||
targetUri,
|
||||
);
|
||||
const downloadResult = await FileSystem.downloadAsync(downloadUrl, targetUri)
|
||||
if (!downloadResult?.uri) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
if (Platform.OS === "android") {
|
||||
if (Platform.OS === 'android') {
|
||||
const permissions =
|
||||
await FileSystem.StorageAccessFramework.requestDirectoryPermissionsAsync();
|
||||
await FileSystem.StorageAccessFramework.requestDirectoryPermissionsAsync()
|
||||
if (!permissions.granted || !permissions.directoryUri) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
const base64 = await FileSystem.readAsStringAsync(downloadResult.uri, {
|
||||
encoding: FileSystem.EncodingType.Base64,
|
||||
});
|
||||
})
|
||||
try {
|
||||
const destUri =
|
||||
await FileSystem.StorageAccessFramework.createFileAsync(
|
||||
permissions.directoryUri,
|
||||
fileName,
|
||||
"audio/mpeg",
|
||||
);
|
||||
const destUri = await FileSystem.StorageAccessFramework.createFileAsync(
|
||||
permissions.directoryUri,
|
||||
fileName,
|
||||
'audio/mpeg'
|
||||
)
|
||||
await FileSystem.writeAsStringAsync(destUri, base64, {
|
||||
encoding: FileSystem.EncodingType.Base64,
|
||||
});
|
||||
})
|
||||
} catch (error) {
|
||||
console.log("[SongDownload] SAF write error", error?.message);
|
||||
console.log('[SongDownload] SAF write error', error?.message)
|
||||
}
|
||||
} else {
|
||||
if (await Sharing.isAvailableAsync()) {
|
||||
try {
|
||||
await Sharing.shareAsync(downloadResult.uri, {
|
||||
mimeType: "audio/mpeg",
|
||||
dialogTitle: "Enregistrer la musique",
|
||||
});
|
||||
mimeType: 'audio/mpeg',
|
||||
dialogTitle: 'Enregistrer la musique',
|
||||
})
|
||||
} catch (error) {
|
||||
console.log("[SongDownload] share error", error?.message);
|
||||
console.log('[SongDownload] share error', error?.message)
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
await Linking.openURL(downloadResult.uri);
|
||||
await Linking.openURL(downloadResult.uri)
|
||||
} catch (error) {
|
||||
console.log(
|
||||
"[SongDownload] open local file error",
|
||||
error?.message,
|
||||
);
|
||||
console.log('[SongDownload] open local file error', error?.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("[SongDownload] native download open error", error?.message);
|
||||
console.log('[SongDownload] native download open error', error?.message)
|
||||
} finally {
|
||||
setIsDownloading(false);
|
||||
await setLoading(false);
|
||||
setIsDownloading(false)
|
||||
await setLoading(false)
|
||||
}
|
||||
}, [coverUrl, isDownloading, projectForStage, selectedOption, trackTitle]);
|
||||
}, [coverUrl, isDownloading, projectForStage, selectedOption, trackTitle])
|
||||
|
||||
return (
|
||||
<Page backgroundImg={background.studioBG2} headerType="NONE">
|
||||
@@ -397,11 +374,7 @@ const SongDownload = ({ route }) => {
|
||||
<CreateLyricsHeader title="Ta pochette est validée" />
|
||||
<View style={styles.coverRow}>
|
||||
{coverUrl ? (
|
||||
<ExpoImage
|
||||
source={{ uri: coverUrl }}
|
||||
style={styles.coverImage}
|
||||
contentFit="cover"
|
||||
/>
|
||||
<ExpoImage source={{ uri: coverUrl }} style={styles.coverImage} contentFit="cover" />
|
||||
) : (
|
||||
<View style={[styles.coverImage, styles.coverPlaceholder]}>
|
||||
<Text style={styles.placeholderText}>Aucune pochette</Text>
|
||||
@@ -409,14 +382,8 @@ const SongDownload = ({ route }) => {
|
||||
)}
|
||||
|
||||
<Pressable style={styles.downloadTile} onPress={handleDownload}>
|
||||
<MaterialCommunityIcons
|
||||
name="download"
|
||||
size={22}
|
||||
color={Palette.white}
|
||||
/>
|
||||
<Text style={styles.downloadText}>
|
||||
Acheter ce morceau pour 1,99€
|
||||
</Text>
|
||||
<MaterialCommunityIcons name="download" size={22} color={Palette.white} />
|
||||
<Text style={styles.downloadText}>Acheter ce morceau pour 1,99€</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
|
||||
@@ -434,11 +401,7 @@ const SongDownload = ({ route }) => {
|
||||
</View>
|
||||
|
||||
<BorderGradientButton
|
||||
title={
|
||||
hasActiveSubscription
|
||||
? "Continuer"
|
||||
: "Continuer sans générer de revenus"
|
||||
}
|
||||
title={hasActiveSubscription ? 'Continuer' : 'Continuer sans générer de revenus'}
|
||||
onPress={handleContinue}
|
||||
containerStyle={styles.continueButton}
|
||||
/>
|
||||
@@ -450,13 +413,13 @@ const SongDownload = ({ route }) => {
|
||||
onContinue={continueFlow}
|
||||
/>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flexGrow: 1,
|
||||
width: "100%",
|
||||
width: '100%',
|
||||
paddingHorizontal: gutters * 1.2,
|
||||
paddingBottom: gutters * 1.8,
|
||||
paddingTop: gutters,
|
||||
@@ -481,9 +444,9 @@ const styles = StyleSheet.create({
|
||||
opacity: 0.8,
|
||||
},
|
||||
coverRow: {
|
||||
flexDirection: isWeb ? "row" : "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
flexDirection: isWeb ? 'row' : 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: gutters * 0.8,
|
||||
},
|
||||
coverImage: {
|
||||
@@ -491,24 +454,24 @@ const styles = StyleSheet.create({
|
||||
height: 150,
|
||||
borderRadius: 16,
|
||||
borderWidth: 1,
|
||||
borderColor: "rgba(255, 255, 255, 0.18)",
|
||||
borderColor: 'rgba(255, 255, 255, 0.18)',
|
||||
},
|
||||
coverPlaceholder: {
|
||||
...Style.centered,
|
||||
backgroundColor: "rgba(255, 255, 255, 0.06)",
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.06)',
|
||||
},
|
||||
placeholderText: {
|
||||
fontFamily: FONT_FAMILY.InterMedium,
|
||||
color: Palette.grayMid,
|
||||
},
|
||||
downloadTile: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 10,
|
||||
paddingVertical: gutters * 0.9,
|
||||
paddingHorizontal: gutters * 1.2,
|
||||
borderRadius: 14,
|
||||
backgroundColor: "#8C4BFF",
|
||||
backgroundColor: '#8C4BFF',
|
||||
borderWidth: 0,
|
||||
},
|
||||
downloadText: {
|
||||
@@ -522,6 +485,6 @@ const styles = StyleSheet.create({
|
||||
continueButton: {
|
||||
marginTop: gutters * 0.5,
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
export default SongDownload;
|
||||
export default SongDownload
|
||||
|
||||
Reference in New Issue
Block a user