feat: add delete button for local activities (single and bulk)
- Detail screen: Delete button (top-right, red) with confirmation alert; deletes SQLite row and original file via expo-file-system - Feed screen: long-press card to enter select mode; checkbox + blue border on selected cards; bottom action bar with bulk Delete N button; header switches to show count + Cancel - db/queries: deleteActivity (returns original_path) and deleteActivities (bulk, returns all original paths)
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
import { Camera, GeoJSONSource, Layer, Map } from '@maplibre/maplibre-react-native';
|
||||
import * as FileSystem from 'expo-file-system';
|
||||
import { useLocalSearchParams, useRouter } from 'expo-router';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { ActivityIndicator, Modal, Pressable, ScrollView, StyleSheet, Text, View } from 'react-native';
|
||||
import { ActivityIndicator, Alert, Modal, Pressable, ScrollView, StyleSheet, Text, View } from 'react-native';
|
||||
import Svg, { Defs, LinearGradient, Path, Stop } from 'react-native-svg';
|
||||
import { useActivity, useSetting } from '@/db/queries';
|
||||
import { useSQLiteContext } from 'expo-sqlite';
|
||||
import { deleteActivity, useActivity, useSetting } from '@/db/queries';
|
||||
|
||||
const MAP_STYLE = 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json';
|
||||
|
||||
@@ -25,6 +27,7 @@ type Timeseries = {
|
||||
export default function ActivityScreen() {
|
||||
const { id } = useLocalSearchParams<{ id: string }>();
|
||||
const router = useRouter();
|
||||
const db = useSQLiteContext();
|
||||
const row = useActivity(id);
|
||||
const instanceUrl = useSetting('instance_url')?.replace(/\/$/, '') ?? '';
|
||||
const token = useSetting('api_token') ?? '';
|
||||
@@ -34,6 +37,27 @@ export default function ActivityScreen() {
|
||||
const [loadingMap, setLoadingMap] = useState(false);
|
||||
const [loadingChart, setLoadingChart] = useState(false);
|
||||
|
||||
async function confirmDelete() {
|
||||
Alert.alert(
|
||||
'Delete activity',
|
||||
'This will permanently remove this activity from your device.',
|
||||
[
|
||||
{ text: 'Cancel', style: 'cancel' },
|
||||
{
|
||||
text: 'Delete',
|
||||
style: 'destructive',
|
||||
onPress: async () => {
|
||||
const originalPath = await deleteActivity(db, id);
|
||||
if (originalPath) {
|
||||
try { await FileSystem.deleteAsync(originalPath, { idempotent: true }); } catch {}
|
||||
}
|
||||
router.back();
|
||||
},
|
||||
},
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// instanceUrl and token are in the dep array to avoid a stale-closure bug in
|
||||
// release builds: Hermes executes effects sooner and captures empty strings if
|
||||
// the deps are omitted. Guards on geojson/timeseries prevent double-fetching.
|
||||
@@ -90,9 +114,14 @@ export default function ActivityScreen() {
|
||||
|
||||
return (
|
||||
<ScrollView style={styles.container} contentContainerStyle={styles.content}>
|
||||
<Pressable style={styles.backButton} onPress={() => router.back()}>
|
||||
<Text style={styles.backText}>← Back</Text>
|
||||
</Pressable>
|
||||
<View style={styles.topBar}>
|
||||
<Pressable style={styles.backButton} onPress={() => router.back()}>
|
||||
<Text style={styles.backText}>← Back</Text>
|
||||
</Pressable>
|
||||
<Pressable style={styles.deleteButton} onPress={confirmDelete}>
|
||||
<Text style={styles.deleteText}>Delete</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
|
||||
<Text style={styles.sport}>{detail.sport ?? 'Activity'}</Text>
|
||||
<Text style={styles.title}>{detail.title}</Text>
|
||||
@@ -367,8 +396,11 @@ const styles = StyleSheet.create({
|
||||
content: { paddingBottom: 40 },
|
||||
center: { flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor: '#09090b' },
|
||||
notFound: { color: '#71717a', fontSize: 16 },
|
||||
backButton: { paddingHorizontal: 16, paddingTop: 60, paddingBottom: 12 },
|
||||
topBar: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingTop: 60, paddingBottom: 12 },
|
||||
backButton: { paddingHorizontal: 16 },
|
||||
backText: { color: '#60a5fa', fontSize: 15 },
|
||||
deleteButton: { paddingHorizontal: 16 },
|
||||
deleteText: { color: '#f87171', fontSize: 15 },
|
||||
sport: { color: '#71717a', fontSize: 12, fontWeight: '600', letterSpacing: 0.8, paddingHorizontal: 16, marginBottom: 4 },
|
||||
title: { color: '#f4f4f5', fontSize: 22, fontWeight: '700', paddingHorizontal: 16, marginBottom: 4 },
|
||||
date: { color: '#71717a', fontSize: 13, paddingHorizontal: 16, marginBottom: 16 },
|
||||
|
||||
Reference in New Issue
Block a user