feat: share knop op dashboard kaart — deelt kaart als PNG via expo-sharing

This commit is contained in:
eddy 2026-07-20 18:58:24 +02:00
commit dc85cd3ff0

View file

@ -1,5 +1,7 @@
import React from 'react'
import { View, Text, StyleSheet } from 'react-native'
import React, { useRef } from 'react'
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native'
import ViewShot from 'react-native-view-shot'
import * as Sharing from 'expo-sharing'
import { Measurement } from '../types'
type Props = { measurements: Measurement[] }
@ -16,6 +18,8 @@ const rows: { label: string; key: keyof Measurement; suffix: string }[] = [
]
export default function LatestMeasurementCard({ measurements }: Props) {
const viewShotRef = useRef<ViewShot>(null)
if (measurements.length === 0) {
return (
<View style={styles.card}>
@ -28,47 +32,70 @@ export default function LatestMeasurementCard({ measurements }: Props) {
const previous = measurements[1]
const diff = previous ? (latest.weight - previous.weight) : null
const handleShare = async () => {
try {
const uri = await viewShotRef.current?.capture?.()
if (uri) {
await Sharing.shareAsync(uri, { mimeType: 'image/png' })
}
} catch {}
}
return (
<View style={styles.card}>
{previous && (
<View style={styles.previous}>
<Text style={styles.previousTitle}>Voorlaatste meting</Text>
<Text style={styles.previousDate}>
{new Date(previous.measuredAt).toLocaleDateString('nl-NL')} — {previous.weight.toFixed(1)} kg
</Text>
<Text style={styles.diffLabel}>
Verschil {diff !== null && (diff > 0 ? '+' : '')}{diff?.toFixed(1)} kg
</Text>
<View style={styles.divider} />
</View>
)}
<View style={styles.header}>
<View style={{ flex: 1 }} />
<TouchableOpacity onPress={handleShare} hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}>
<Text style={styles.shareIcon}>↗</Text>
</TouchableOpacity>
</View>
<Text style={styles.title}>Laatste meting</Text>
<Text style={styles.date}>{new Date(latest.measuredAt).toLocaleDateString('nl-NL')}</Text>
{rows.map(r => (
<View key={r.key} style={styles.row}>
<Text style={styles.rowLabel}>{r.label}</Text>
<Text style={styles.rowValue}>
{typeof latest[r.key] === 'number'
? (latest[r.key] as number).toFixed(1)
: latest[r.key]}{' '}
{r.suffix}
</Text>
<ViewShot ref={viewShotRef} options={{ format: 'png', quality: 1 }}>
<View style={styles.shotContent}>
{previous && (
<View style={styles.previous}>
<Text style={styles.previousTitle}>Voorlaatste meting</Text>
<Text style={styles.previousDate}>
{new Date(previous.measuredAt).toLocaleDateString('nl-NL')} — {previous.weight.toFixed(1)} kg
</Text>
<Text style={styles.diffLabel}>
Verschil {diff !== null && (diff > 0 ? '+' : '')}{diff?.toFixed(1)} kg
</Text>
<View style={styles.divider} />
</View>
)}
<Text style={styles.title}>Laatste meting</Text>
<Text style={styles.date}>{new Date(latest.measuredAt).toLocaleDateString('nl-NL')}</Text>
{rows.map(r => (
<View key={r.key} style={styles.row}>
<Text style={styles.rowLabel}>{r.label}</Text>
<Text style={styles.rowValue}>
{typeof latest[r.key] === 'number'
? (latest[r.key] as number).toFixed(1)
: latest[r.key]}{' '}
{r.suffix}
</Text>
</View>
))}
</View>
))}
</ViewShot>
</View>
)
}
const styles = StyleSheet.create({
card: { backgroundColor: '#fff', borderRadius: 12, padding: 16, margin: 16, shadowColor: '#000', shadowOpacity: 0.1, shadowRadius: 8, elevation: 3 },
header: { flexDirection: 'row', alignItems: 'center', marginBottom: 8 },
shareIcon: { fontSize: 22, color: '#2196F3' },
shotContent: { backgroundColor: '#fff', padding: 16 },
title: { fontSize: 18, fontWeight: '700', marginBottom: 4 },
date: { fontSize: 14, color: '#666', marginBottom: 12 },
row: { flexDirection: 'row', paddingVertical: 4 },
rowLabel: { fontSize: 14, color: '#555', flex: 1 },
rowValue: { fontSize: 14, fontWeight: '600' },
emptyText: { fontSize: 16, color: '#999', textAlign: 'center', padding: 20 },
previous: { marginTop: 4 },
previous: { marginBottom: 4 },
divider: { height: 1, backgroundColor: '#eee', marginVertical: 12 },
previousTitle: { fontSize: 18, fontWeight: '700', marginBottom: 4 },
previousDate: { fontSize: 14, color: '#666', marginBottom: 4 },