Files
jrshikoku/components/Settings/SoundSettings.tsx

119 lines
3.7 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useCallback, useEffect, useState } from "react";
import { View, Text, ScrollView, Platform } from "react-native";
import { Switch } from "@rneui/themed";
import { useNavigation } from "@react-navigation/native";
import { SheetHeaderItem } from "@/components/atom/SheetHeaderItem";
import { AS } from "../../storageControl";
import { STORAGE_KEYS } from "@/constants";
import { useThemeColors } from "@/lib/theme";
import { Asset } from "expo-asset";
import { useAudioPlayer, setAudioModeAsync } from "expo-audio";
import type { AudioSource } from "expo-audio";
const previewSound = require("../../assets/sound/rikka-test.mp3");
export const SoundSettings = () => {
const { goBack } = useNavigation();
const { colors, fixed } = useThemeColors();
const [delayAnnouncement, setDelayAnnouncement] = useState(false);
// expo-asset でローカルパスを取得し、expo-audio に渡す
const [resolvedSource, setResolvedSource] = useState<AudioSource>(null);
useEffect(() => {
let mounted = true;
const resolve = async () => {
try {
const asset = Asset.fromModule(previewSound);
await asset.downloadAsync();
const localUri = asset.localUri;
if (!mounted) return;
if (localUri) {
// Android の expo-audio は file:// URI を正しく処理できないため prefix を除去
// iOS は file:// プレフィックスが必要なのでそのまま使用
const source =
Platform.OS === "android"
? { uri: localUri.replace(/^file:\/\//, "") }
: { uri: localUri };
setResolvedSource(source);
}
} catch (error) {
if (!mounted) return;
console.warn("Failed to resolve audio asset", error);
}
};
resolve();
return () => {
mounted = false;
};
}, []);
const previewPlayer = useAudioPlayer(resolvedSource);
useEffect(() => {
AS.getItem(STORAGE_KEYS.SOUND_DELAY_ANNOUNCEMENT)
.then((v) => setDelayAnnouncement(v === true || v === "true"))
.catch(() => {
// 未設定時はデフォルト値 false のまま
});
}, []);
const playPreview = useCallback(async () => {
try {
await setAudioModeAsync({
playsInSilentMode: true,
shouldPlayInBackground: false,
interruptionMode: "duckOthers",
});
if (previewPlayer.playing) previewPlayer.pause();
previewPlayer.volume = 1;
await previewPlayer.seekTo(0);
previewPlayer.play();
} catch (error) {
console.warn("Failed to play preview sound", error);
}
}, [previewPlayer]);
const handleToggle = (value: boolean) => {
setDelayAnnouncement(value);
AS.setItem(STORAGE_KEYS.SOUND_DELAY_ANNOUNCEMENT, value.toString());
if (value) {
playPreview();
}
};
return (
<View style={{ height: "100%", backgroundColor: fixed.primary }}>
<SheetHeaderItem
title="サウンド機能(β)"
LeftItem={{ title: " 設定", onPress: goBack }}
/>
<ScrollView style={{ flex: 1, backgroundColor: colors.background }}>
<View
style={{
flexDirection: "row",
alignItems: "center",
paddingHorizontal: 15,
paddingVertical: 14,
borderBottomWidth: 1,
borderBottomColor: colors.borderSecondary ?? "#ccc",
backgroundColor: colors.surface,
}}
>
<Text style={{ flex: 1, fontSize: 16, color: colors.text }}>
</Text>
<Switch
value={delayAnnouncement}
onValueChange={handleToggle}
color={fixed.primary}
/>
</View>
</ScrollView>
</View>
);
};