1 Commits

Author SHA1 Message Date
harukin-expo-dev-env
7ba9a750ea feat: 録画の削除機能を追加し、スワイプ操作での削除を実装 2026-06-01 12:11:00 +00:00

View File

@@ -1,5 +1,6 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect, useRef } from "react";
import { import {
Alert,
View, View,
Text, Text,
ScrollView, ScrollView,
@@ -11,6 +12,7 @@ import {
import { Switch } from "@rneui/themed"; import { Switch } from "@rneui/themed";
import { useNavigation } from "@react-navigation/native"; import { useNavigation } from "@react-navigation/native";
import { MaterialCommunityIcons } from "@expo/vector-icons"; import { MaterialCommunityIcons } from "@expo/vector-icons";
import Swipeable from "react-native-gesture-handler/Swipeable";
import { SheetHeaderItem } from "@/components/atom/SheetHeaderItem"; import { SheetHeaderItem } from "@/components/atom/SheetHeaderItem";
import { AS } from "../../storageControl"; import { AS } from "../../storageControl";
import { STORAGE_KEYS } from "@/constants"; import { STORAGE_KEYS } from "@/constants";
@@ -302,6 +304,7 @@ export const DataSourceSettings = () => {
useState<JrDataSystemUiVariant>( useState<JrDataSystemUiVariant>(
getJrDataSystemUiVariant(DEFAULT_JR_DATA_SYSTEM_ENV), getJrDataSystemUiVariant(DEFAULT_JR_DATA_SYSTEM_ENV),
); );
const recordingSwipeRefs = useRef<Record<string, { close: () => void } | null>>({});
const applyJrDataSystemEnv = (env: JrDataSystemEnvironmentKey) => { const applyJrDataSystemEnv = (env: JrDataSystemEnvironmentKey) => {
setJrDataSystemEnv(env); setJrDataSystemEnv(env);
@@ -358,6 +361,28 @@ export const DataSourceSettings = () => {
applyJrDataSystemEnv(env); applyJrDataSystemEnv(env);
}; };
const closeRecordingSwipe = (id: string) => {
recordingSwipeRefs.current[id]?.close();
};
const confirmDeleteRecording = (id: string, label: string) => {
closeRecordingSwipe(id);
Alert.alert(
"録画を削除",
`${label} の録画を削除しますか?`,
[
{ text: "キャンセル", style: "cancel" },
{
text: "削除",
style: "destructive",
onPress: () => {
void deleteRecording(id);
},
},
],
);
};
return ( return (
<View style={[styles.container, { backgroundColor: fixed.primary }]}> <View style={[styles.container, { backgroundColor: fixed.primary }]}>
<SheetHeaderItem <SheetHeaderItem
@@ -553,13 +578,16 @@ export const DataSourceSettings = () => {
month: 'numeric', day: 'numeric', month: 'numeric', day: 'numeric',
hour: '2-digit', minute: '2-digit', hour: '2-digit', minute: '2-digit',
}); });
return ( const recordingRow = (
<View <TouchableOpacity
key={rec.id} onPress={() => startPlayback(rec.id)}
disabled={isPlaying}
activeOpacity={0.72}
style={{ style={{
flexDirection: 'row', alignItems: 'center', flexDirection: 'row', alignItems: 'center',
backgroundColor: colors.backgroundSecondary, backgroundColor: colors.backgroundSecondary,
borderRadius: 8, padding: 10, gap: 8, borderRadius: 8, padding: 12, gap: 10,
opacity: isPlaying ? 0.6 : 1,
}} }}
> >
<View style={{ flex: 1 }}> <View style={{ flex: 1 }}>
@@ -570,29 +598,71 @@ export const DataSourceSettings = () => {
{rec.snapshotCount} / {durationLabel} {rec.snapshotCount} / {durationLabel}
</Text> </Text>
</View> </View>
{!isPlaying && ( <View style={{ alignItems: 'flex-end', gap: 4 }}>
<TouchableOpacity <View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
onPress={() => startPlayback(rec.id)} <MaterialCommunityIcons
name={isPlaying ? 'pause-circle-outline' : 'play-circle-outline'}
size={18}
color={isPlaying ? colors.textTertiary : '#43a047'}
/>
<Text
style={{
color: isPlaying ? colors.textTertiary : colors.textPrimary,
fontSize: 12,
fontWeight: '600',
}}
>
{isPlaying ? '再生中は操作不可' : 'タップで再生'}
</Text>
</View>
{!isPlaying && (
<Text style={{ color: colors.textTertiary, fontSize: 10 }}>
</Text>
)}
</View>
<MaterialCommunityIcons
name="chevron-right"
size={18}
color={colors.iconSecondary}
/>
</TouchableOpacity>
);
if (isPlaying) {
return <View key={rec.id}>{recordingRow}</View>;
}
return (
<Swipeable
key={rec.id}
ref={(instance) => {
recordingSwipeRefs.current[rec.id] = instance;
}}
friction={2}
overshootRight={false}
rightThreshold={48}
renderRightActions={() => (
<View
style={{ style={{
backgroundColor: '#43a047', borderRadius: 6, width: 96,
paddingHorizontal: 10, paddingVertical: 6, borderRadius: 8,
backgroundColor: '#e53935',
alignItems: 'center',
justifyContent: 'center',
marginLeft: 6,
}} }}
> >
<Text style={{ color: '#fff', fontWeight: 'bold', fontSize: 12 }}></Text> <MaterialCommunityIcons name="trash-can-outline" size={18} color="#fff" />
</TouchableOpacity> <Text style={{ color: '#fff', fontSize: 11, fontWeight: 'bold', marginTop: 4 }}>
</Text>
</View>
)} )}
{!isPlaying && ( onSwipeableOpen={() => confirmDeleteRecording(rec.id, dateLabel)}
<TouchableOpacity >
onPress={() => deleteRecording(rec.id)} {recordingRow}
style={{ </Swipeable>
borderColor: '#e53935', borderWidth: 1, borderRadius: 6,
paddingHorizontal: 10, paddingVertical: 6,
}}
>
<Text style={{ color: '#e53935', fontSize: 12 }}></Text>
</TouchableOpacity>
)}
</View>
); );
})} })}
</View> </View>