tiefen_space_app/components/MediaPickerModal/index.jsx

308 lines
8.5 KiB
React
Raw Normal View History

2023-12-29 00:27:44 +08:00
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);
}}
/>
</>
);
}