106 lines
2.7 KiB
TypeScript
106 lines
2.7 KiB
TypeScript
import { TouchableOpacity, Text, View, StyleSheet, Platform } from 'react-native'
|
|
import { Ionicons } from '@expo/vector-icons'
|
|
import * as WebBrowser from 'expo-web-browser'
|
|
|
|
interface Attachment {
|
|
id: string
|
|
name: string
|
|
storagePath: string
|
|
sizeBytes: number | null
|
|
mimeType?: string | null
|
|
}
|
|
|
|
const API_URL = process.env.EXPO_PUBLIC_API_URL ?? 'http://localhost:3000'
|
|
|
|
function getFileIcon(mimeType?: string | null): keyof typeof Ionicons.glyphMap {
|
|
if (!mimeType) return 'document-outline'
|
|
if (mimeType.includes('pdf')) return 'document-text-outline'
|
|
if (mimeType.startsWith('image/')) return 'image-outline'
|
|
if (mimeType.includes('spreadsheet') || mimeType.includes('excel')) return 'grid-outline'
|
|
if (mimeType.includes('word') || mimeType.includes('document')) return 'document-outline'
|
|
return 'attach-outline'
|
|
}
|
|
|
|
function formatSize(bytes: number): string {
|
|
if (bytes < 1024) return `${bytes} B`
|
|
if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`
|
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
|
|
}
|
|
|
|
export function AttachmentRow({ attachment }: { attachment: Attachment }) {
|
|
const url = `${API_URL}/uploads/${attachment.storagePath}`
|
|
const icon = getFileIcon(attachment.mimeType)
|
|
|
|
return (
|
|
<TouchableOpacity
|
|
onPress={() => WebBrowser.openBrowserAsync(url)}
|
|
style={styles.row}
|
|
activeOpacity={0.75}
|
|
>
|
|
<View style={styles.iconBox}>
|
|
<Ionicons name={icon} size={20} color="#003B7E" />
|
|
</View>
|
|
|
|
<View style={styles.info}>
|
|
<Text style={styles.name} numberOfLines={1}>
|
|
{attachment.name}
|
|
</Text>
|
|
{attachment.sizeBytes != null && (
|
|
<Text style={styles.meta}>{formatSize(attachment.sizeBytes)}</Text>
|
|
)}
|
|
</View>
|
|
|
|
<View style={styles.openChip}>
|
|
<Ionicons name="download-outline" size={13} color="#003B7E" />
|
|
<Text style={styles.openText}>Öffnen</Text>
|
|
</View>
|
|
</TouchableOpacity>
|
|
)
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
row: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 12,
|
|
paddingVertical: 13,
|
|
},
|
|
iconBox: {
|
|
width: 42,
|
|
height: 42,
|
|
borderRadius: 12,
|
|
backgroundColor: '#EFF6FF',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
flexShrink: 0,
|
|
},
|
|
info: {
|
|
flex: 1,
|
|
},
|
|
name: {
|
|
fontSize: 13,
|
|
fontWeight: '600',
|
|
color: '#0F172A',
|
|
lineHeight: 18,
|
|
},
|
|
meta: {
|
|
fontSize: 11,
|
|
color: '#94A3B8',
|
|
marginTop: 2,
|
|
},
|
|
openChip: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 4,
|
|
backgroundColor: '#EFF6FF',
|
|
paddingHorizontal: 11,
|
|
paddingVertical: 6,
|
|
borderRadius: 99,
|
|
},
|
|
openText: {
|
|
fontSize: 12,
|
|
color: '#003B7E',
|
|
fontWeight: '600',
|
|
},
|
|
})
|