296 lines
12 KiB
TypeScript
296 lines
12 KiB
TypeScript
import CancelSvg from '@/assets/icons/svg/cancel.svg';
|
|
import DownloadSvg from '@/assets/icons/svg/download.svg';
|
|
import FolderSvg from "@/assets/icons/svg/folder.svg";
|
|
import ReturnArrow from "@/assets/icons/svg/returnArrow.svg";
|
|
import YesSvg from "@/assets/icons/svg/yes.svg";
|
|
import { ContentPart } from '@/types/ask';
|
|
import { TFunction } from "i18next";
|
|
import React from "react";
|
|
import { FlatList, Image, Modal, StyleSheet, TouchableOpacity, View } from "react-native";
|
|
import ContextMenu from "../gusture/contextMenu";
|
|
import { ThemedText } from "../ThemedText";
|
|
import { mergeArrays, saveMediaToGallery } from "./utils";
|
|
import VideoPlayer from './VideoPlayer';
|
|
|
|
interface SelectModelProps {
|
|
modalDetailsVisible: { visible: boolean, content: any };
|
|
setModalDetailsVisible: React.Dispatch<React.SetStateAction<{ visible: boolean, content: any }>>;
|
|
insets: { top: number };
|
|
setSelectedImages: React.Dispatch<React.SetStateAction<string[]>>;
|
|
selectedImages: string[];
|
|
t: TFunction;
|
|
cancel: boolean;
|
|
setCancel: React.Dispatch<React.SetStateAction<boolean>>;
|
|
}
|
|
const SelectModel = ({ modalDetailsVisible, setModalDetailsVisible, insets, setSelectedImages, selectedImages, t, cancel, setCancel }: SelectModelProps) => {
|
|
const items = modalDetailsVisible?.content?.filter((media: ContentPart) => media.type !== 'text');
|
|
|
|
return (
|
|
<Modal
|
|
animationType="fade"
|
|
visible={modalDetailsVisible.visible}
|
|
transparent={false}
|
|
statusBarTranslucent={true}
|
|
onRequestClose={() => {
|
|
setModalDetailsVisible({ visible: false, content: [] });
|
|
}}
|
|
>
|
|
<View style={[detailsStyles.container, { paddingTop: insets?.top }]}>
|
|
<View style={detailsStyles.header}>
|
|
<TouchableOpacity onPress={() => setModalDetailsVisible({ visible: false, content: [] })}>
|
|
<ReturnArrow />
|
|
</TouchableOpacity>
|
|
<ThemedText style={detailsStyles.headerText}>{t('ask.selectPhoto', { ns: 'ask' })}</ThemedText>
|
|
<FolderSvg />
|
|
</View>
|
|
<View style={{ flex: 1 }}>
|
|
<FlatList
|
|
data={items}
|
|
numColumns={3}
|
|
keyExtractor={(item) => item.id}
|
|
showsVerticalScrollIndicator={false}
|
|
contentContainerStyle={detailsStyles.gridContainer}
|
|
initialNumToRender={12}
|
|
maxToRenderPerBatch={12}
|
|
updateCellsBatchingPeriod={50}
|
|
windowSize={10}
|
|
removeClippedSubviews={true}
|
|
renderItem={({ item }) => {
|
|
const itemId = item?.id || item?.video?.id;
|
|
const isSelected = selectedImages.includes(itemId);
|
|
|
|
return (
|
|
<View style={detailsStyles.gridItemContainer} key={itemId}>
|
|
|
|
<View style={detailsStyles.gridItem}>
|
|
{isSelected && (
|
|
<ThemedText style={detailsStyles.imageNumber}>
|
|
{selectedImages.indexOf(itemId) + 1}
|
|
</ThemedText>
|
|
)}
|
|
<ContextMenu
|
|
items={[
|
|
{
|
|
svg: <DownloadSvg width={20} height={20} />,
|
|
label: t("ask:ask.save"),
|
|
onPress: () => {
|
|
const imageUrl = item?.url;
|
|
if (imageUrl) {
|
|
saveMediaToGallery(imageUrl, t);
|
|
}
|
|
},
|
|
textStyle: { color: '#4C320C' }
|
|
},
|
|
{
|
|
svg: <CancelSvg width={20} height={20} color='red' />,
|
|
label: t("ask:ask.cancel"),
|
|
onPress: () => setCancel(true),
|
|
textStyle: { color: 'red' }
|
|
}
|
|
]}
|
|
cancel={cancel}
|
|
menuStyle={{
|
|
backgroundColor: 'white',
|
|
borderRadius: 8,
|
|
padding: 8,
|
|
minWidth: 150,
|
|
shadowColor: '#000',
|
|
shadowOffset: { width: 0, height: 2 },
|
|
shadowOpacity: 0.25,
|
|
shadowRadius: 4,
|
|
elevation: 5,
|
|
}}
|
|
>
|
|
{item?.type === 'image' ? (
|
|
<Image
|
|
source={{ uri: item?.url }}
|
|
style={detailsStyles.image}
|
|
onError={(error) => console.log('Image load error:', error.nativeEvent.error)}
|
|
onLoad={() => console.log('Image loaded successfully')}
|
|
loadingIndicatorSource={require('@/assets/images/png/placeholder.png')}
|
|
/>
|
|
) : (
|
|
<VideoPlayer
|
|
videoUrl={item?.url || ""}
|
|
style={{
|
|
width: '100%',
|
|
height: '100%',
|
|
alignSelf: 'center',
|
|
justifyContent: 'center',
|
|
}}
|
|
/>
|
|
)}
|
|
</ContextMenu>
|
|
|
|
<TouchableOpacity
|
|
style={[
|
|
detailsStyles.circleMarker,
|
|
isSelected && detailsStyles.circleMarkerSelected
|
|
]}
|
|
onPress={() => {
|
|
setSelectedImages(prev =>
|
|
isSelected
|
|
? prev.filter(id => id !== itemId)
|
|
: [...prev, itemId]
|
|
);
|
|
}}
|
|
activeOpacity={0.8}
|
|
>
|
|
{isSelected && <YesSvg width={16} height={16} />}
|
|
</TouchableOpacity>
|
|
</View>
|
|
</View>
|
|
);
|
|
}}
|
|
/>
|
|
</View>
|
|
<View style={detailsStyles.footer}>
|
|
<TouchableOpacity
|
|
style={detailsStyles.continueButton}
|
|
onPress={async () => {
|
|
// 如果用户没有选择 则为选择全部
|
|
if (selectedImages?.length < 0) {
|
|
setSelectedImages(mergeArrays(modalDetailsVisible?.content?.image_material_infos || [], modalDetailsVisible?.content?.video_material_infos || [])?.map((item) => {
|
|
return item.id || item.video?.id
|
|
}))
|
|
}
|
|
setModalDetailsVisible({ visible: false, content: [] })
|
|
}}
|
|
activeOpacity={0.8}
|
|
>
|
|
<ThemedText style={detailsStyles.continueButtonText}>
|
|
{t('ask.continueAsking', { ns: 'ask' })}
|
|
</ThemedText>
|
|
</TouchableOpacity>
|
|
</View>
|
|
</View>
|
|
</Modal >
|
|
)
|
|
}
|
|
|
|
|
|
const detailsStyles = StyleSheet.create({
|
|
gridContainer: {
|
|
flex: 1,
|
|
paddingHorizontal: 8,
|
|
paddingTop: 8,
|
|
},
|
|
gridItemContainer: {
|
|
width: '33.33%',
|
|
aspectRatio: 1,
|
|
padding: 1,
|
|
},
|
|
flatListContent: {
|
|
paddingBottom: 100,
|
|
paddingHorizontal: 8,
|
|
paddingTop: 8,
|
|
},
|
|
headerText: {
|
|
fontSize: 20,
|
|
fontWeight: 'bold',
|
|
color: "#4C320C"
|
|
},
|
|
container: {
|
|
flex: 1,
|
|
padding: 0,
|
|
margin: 0,
|
|
backgroundColor: '#fff',
|
|
width: '100%',
|
|
height: '100%',
|
|
position: 'relative',
|
|
},
|
|
imageNumber: {
|
|
fontSize: 16,
|
|
fontWeight: 'bold',
|
|
color: '#fff',
|
|
position: 'absolute',
|
|
top: 10,
|
|
left: 10,
|
|
zIndex: 10,
|
|
},
|
|
imageNumberText: {
|
|
fontSize: 16,
|
|
fontWeight: 'bold',
|
|
color: '#fff',
|
|
},
|
|
numberText: {
|
|
position: 'absolute',
|
|
top: 10,
|
|
left: 10,
|
|
width: 28,
|
|
height: 28,
|
|
borderRadius: 14,
|
|
backgroundColor: 'rgba(0, 122, 255, 0.9)',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
shadowColor: '#000',
|
|
shadowOffset: { width: 0, height: 2 },
|
|
shadowOpacity: 0.25,
|
|
shadowRadius: 3.84,
|
|
elevation: 5,
|
|
},
|
|
header: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
padding: 16,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: '#eee',
|
|
},
|
|
gridItem: {
|
|
flex: 1,
|
|
backgroundColor: '#f5f5f5',
|
|
borderRadius: 8,
|
|
overflow: 'hidden',
|
|
position: 'relative',
|
|
},
|
|
image: {
|
|
width: '100%',
|
|
height: '100%',
|
|
resizeMode: 'cover',
|
|
},
|
|
circleMarker: {
|
|
position: 'absolute',
|
|
top: 10,
|
|
right: 10,
|
|
width: 28,
|
|
height: 28,
|
|
borderRadius: 14,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
borderWidth: 2,
|
|
borderColor: '#fff',
|
|
},
|
|
circleMarkerSelected: {
|
|
backgroundColor: '#FFB645',
|
|
},
|
|
markerText: {
|
|
fontSize: 16,
|
|
fontWeight: 'bold',
|
|
color: '#000',
|
|
},
|
|
footer: {
|
|
position: 'absolute',
|
|
bottom: 20,
|
|
left: 0,
|
|
right: 0,
|
|
paddingHorizontal: 16,
|
|
zIndex: 10,
|
|
paddingVertical: 10,
|
|
},
|
|
continueButton: {
|
|
backgroundColor: '#E2793F',
|
|
borderRadius: 32,
|
|
padding: 16,
|
|
alignItems: 'center',
|
|
width: '100%',
|
|
zIndex: 10,
|
|
},
|
|
continueButtonText: {
|
|
color: '#fff',
|
|
fontSize: 18,
|
|
fontWeight: 'bold',
|
|
}
|
|
});
|
|
|
|
export default SelectModel |