tiefen_space_app/components/MediaPickerModal/index.jsx

308 lines
8.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {
View,
Text,
TouchableOpacity,
Image,
Modal,
Platform,
} from "react-native";
import React, { useState, useEffect } from "react";
import { useTailwind } from "tailwind-rn";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { ImagePicker } from "expo-image-multiple-picker";
import { Icon, Badge } from "@rneui/themed";
import * as MediaLibrary from "expo-media-library";
import * as Linking from "expo-linking";
import MyModal from "../MyModal";
/*
props格式
visible 控制modal可见性
setVisible 控制modal可见性
type 选择的媒体类型 "image"|"video"|"mix"
maxCount 最大选择数量
setAssets 向父组件返回媒体
*/
export default function MediaPickerModal({
visible,
setVisible,
type,
maxCount,
setAssets,
}) {
const tailwind = useTailwind();
const insets = useSafeAreaInsets();
const [album, setAlbum] = useState({ title: "全部" });
//前往打开权限弹窗
const [overlayVisible, setOverlayVisible] = useState(false);
//保存当前权限状态
const [permissionStatus, setPermissionStatus] = useState();
//检查并获取权限
useEffect(() => {
if (visible) {
async function requestMediaLibraryPermissions() {
//权限检查、获取
// 第一步:检查是否已有权限
const { status } = await MediaLibrary.getPermissionsAsync();
if (status === "granted") {
// 如果已有权限,直接执行操作
setPermissionStatus("granted");
return;
}
// 第二步:如果没有权限,请求用户授权
const permission = await MediaLibrary.requestPermissionsAsync();
if (permission.status === "denied") {
// 用户拒绝了权限请求,打开应用设置页面
setPermissionStatus("denied");
setVisible(false);
setOverlayVisible(true);
return;
}
if (permission.status === "granted") {
// 用户同意了权限请求,执行操作
setPermissionStatus("granted");
}
}
requestMediaLibraryPermissions();
}
}, [visible]);
//将ios中assets的路径从ph://改为file://
const changeToLocalUri = async (assets) => {
const editedAssets = await Promise.all(
assets.map(async (item) => {
const info = await MediaLibrary.getAssetInfoAsync(item.id);
const uri = info.localUri;
if (Platform.OS === "ios") {
return { ...item, old_uri: item.uri, uri };
}
return { ...item, old_uri: item.uri };
})
);
return editedAssets;
};
//让视频秒数格式化
function formatDuration(duration) {
let minutes = Math.floor(duration / 60);
let seconds = Math.round(duration % 60);
if (seconds < 10) {
seconds = "0" + seconds;
}
return `${minutes}:${seconds}`;
}
//header组件
const ImagePickerHeader = (props) => {
return (
<View
style={{
paddingTop: insets.top,
height: 100,
...tailwind("px-4 bg-white flex-row justify-between items-center"),
}}
>
{props.view == "album" && (
<>
<View style={tailwind("w-10")}></View>
<Text style={tailwind("text-lg text-center")}>选择相册</Text>
<TouchableOpacity
onPress={() => {
setVisible(false);
}}
style={tailwind("flex-row items-center")}
>
<Text
style={{
color: "#4387d6",
...tailwind("text-lg font-semibold ml-1"),
}}
>
取消
</Text>
</TouchableOpacity>
</>
)}
{props.view == "gallery" && (
<>
<TouchableOpacity onPress={props.goToAlbum}>
<Icon type="ionicon" name="chevron-back" size={32} />
</TouchableOpacity>
<Text
style={tailwind("text-lg text-center w-1/2")}
numberOfLines={1}
ellipsizeMode="tail"
>
{props.album.title}
</Text>
{props.imagesPicked > 0 ? (
<TouchableOpacity
onPress={props.save}
style={tailwind("flex-row items-center")}
>
<Badge value={props.imagesPicked} status="primary" />
<Text
style={{
color: "#4387d6",
...tailwind("text-lg font-semibold ml-1"),
}}
>
完成
</Text>
</TouchableOpacity>
) : (
<TouchableOpacity
onPress={() => {
setVisible(false);
}}
style={tailwind("flex-row items-center")}
>
<Text
style={{
color: "#4387d6",
...tailwind("text-lg font-semibold ml-1"),
}}
>
取消
</Text>
</TouchableOpacity>
)}
</>
)}
</View>
);
};
//check组件
const ImagePickerCheck = () => {
return (
<View
style={{
width: "100%",
height: "100%",
alignItems: "center",
justifyContent: "center",
backgroundColor: "rgba(0,0,0,0.6)",
}}
>
<Icon type="ionicon" name="checkmark" color="white" size={32} />
</View>
);
};
//album组件
const ImagePickerAlbum = (props) => {
return (
<TouchableOpacity
onPress={() => props.goToGallery(props.album)}
style={{ flex: 1, height: 200 }}
>
<Image
source={{ uri: props.thumb.uri }}
style={{ width: "100%", height: "100%" }}
blurRadius={10}
></Image>
<View
style={{
position: "absolute",
width: "100%",
height: "100%",
backgroundColor: "rgba(0,0,0,0.2)",
justifyContent: "flex-end",
}}
>
<View style={{ padding: 5, flexDirection: "row" }}>
<Icon type="ionicon" name="folder-open" color="white" size={16} />
<Text
style={{
color: "white",
fontSize: 16,
marginLeft: 5,
}}
>
{props.album.title}
</Text>
</View>
</View>
</TouchableOpacity>
);
};
//video组件
const ImagePickerVideo = (props) => {
return (
<View style={tailwind("flex-1 justify-end")}>
<View
style={tailwind(
"flex-row justify-between items-center bg-black px-2"
)}
>
<Icon type="ionicon" name="videocam" color="white" size={16} />
<Text numberOfLines={1} style={tailwind("text-xs text-white")}>
{formatDuration(props.duration)}
</Text>
</View>
</View>
);
};
return (
<>
<Modal
visible={visible && permissionStatus == "granted"}
transparent={true}
statusBarTranslucent
animationType="slide"
>
<View
style={{
...tailwind("bg-white flex-1"),
}}
>
<ImagePicker
theme={{
header: ImagePickerHeader,
check: ImagePickerCheck,
album: ImagePickerAlbum,
video: ImagePickerVideo,
}}
onSave={async (assets) => {
const editedAssets = await changeToLocalUri(assets);
setAssets(editedAssets);
setVisible(false);
}}
onCancel={() => {
setVisible(false);
}}
galleryColumns={3}
albumColumns={2}
multiple
onSelectAlbum={(album) => setAlbum(album)}
selectedAlbum={album}
limit={maxCount}
video={type === "video" || type === "mix"}
image={type === "image" || type === "mix"}
/>
</View>
</Modal>
<MyModal
visible={overlayVisible}
setVisible={setOverlayVisible}
title="您还未打开媒体权限"
content="是否前往设置开启权限?"
cancel={() => {
setOverlayVisible(false);
}}
confirm={() => {
Linking.openSettings();
setOverlayVisible(false);
}}
/>
</>
);
}