SettingTopPageとSoundSettingsのリファクタリング、不要なコードの削除とオーディオプレイヤー機能の追加

This commit is contained in:
harukin-expo-dev-env
2026-03-31 11:17:19 +00:00
parent ec9b6dd1bc
commit af4e0d9438
3 changed files with 76 additions and 77 deletions

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useState } from "react";
import React from "react";
import {
View,
Text,
@@ -6,7 +6,6 @@ import {
ScrollView,
Linking,
Image,
Platform,
} from "react-native";
import * as Updates from "expo-updates";
import { useWindowDimensions } from "react-native";
@@ -16,16 +15,9 @@ import TouchableScale from "react-native-touchable-scale";
import { SwitchArea } from "../atom/SwitchArea";
import { useNotification } from "../../stateBox/useNotifications";
import { SheetHeaderItem } from "@/components/atom/SheetHeaderItem";
import { Asset } from "expo-asset";
import {
useAudioPlayer,
setAudioModeAsync,
} from "expo-audio";
import type { AudioSource } from "expo-audio";
import { useThemeColors, type ColorThemePref } from "@/lib/theme/useThemeColors";
const versionCode = "6.2.1.1"; // Update this version code as needed
const settingsPreviewSound = require("../../assets/sound/rikka-test.mp3");
const versionCode = "7.0"; // Update this version code as needed
export const SettingTopPage = ({
testNFC,
@@ -38,60 +30,6 @@ export const SettingTopPage = ({
const { colors, fixed, colorTheme, setColorTheme } = useThemeColors();
const navigation = useNavigation<any>();
// expo-asset でローカルパスを取得し、expo-audio に渡す
const [resolvedSource, setResolvedSource] = useState<AudioSource>(null);
useEffect(() => {
let mounted = true;
const resolve = async () => {
try {
const asset = Asset.fromModule(settingsPreviewSound);
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);
const onPressHeaderImage = useCallback(async () => {
try {
// カーナビ風: 他のアプリの音楽を止めず音量を下げて上書き再生 (duckOthers)
// iOS: playsInSilentMode: true でサイレントモード時も再生
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]);
return (
<View style={{ height: "100%", backgroundColor: fixed.primary }}>
<SheetHeaderItem title="アプリの設定画面" LeftItem={{
@@ -101,17 +39,15 @@ export const SettingTopPage = ({
<ScrollView style={{ flex: 1, backgroundColor: colors.backgroundSecondary }}>
<View style={{ height: 300, padding: 10 }}>
<View style={{ flex: 1 }} />
<TouchableOpacity activeOpacity={0.9} onPress={onPressHeaderImage}>
<Image
source={require("../../assets/Header.png")}
style={{
aspectRatio: 8.08,
height: undefined,
width: width - 20,
borderRadius: 5,
}}
/>
</TouchableOpacity>
<Image
source={require("../../assets/Header.png")}
style={{
aspectRatio: 8.08,
height: undefined,
width: width - 20,
borderRadius: 5,
}}
/>
<View style={{ flexDirection: "row", paddingTop: 10 }}>
<View style={{ flex: 1 }} />
<Text style={{ color: colors.text }}>: {versionCode}</Text>

View File

@@ -1,17 +1,58 @@
import React, { useEffect, useState } from "react";
import { View, Text, ScrollView } from "react-native";
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"))
@@ -20,9 +61,28 @@ export const SoundSettings = () => {
});
}, []);
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 File

@@ -44,6 +44,9 @@
},
"beta7.0": {
"channel": "fdroid"
},
"production7.0": {
"channel": "familymart"
}
},
"submit": {