DataSourceAccordionCardコンポーネントを追加し、鉄道運用Hubのデータソース設定を改善

This commit is contained in:
harukin-expo-dev-env
2026-03-04 14:55:18 +00:00
parent c49aeeb331
commit 87f1cf2b1e

View File

@@ -1,12 +1,151 @@
import React, { useState, useEffect } from "react";
import { View, Text, ScrollView, StyleSheet } from "react-native";
import { View, Text, ScrollView, StyleSheet, Image, TouchableOpacity, Linking } from "react-native";
import { Switch } from "react-native-elements";
import { useNavigation } from "@react-navigation/native";
import { MaterialCommunityIcons } from "@expo/vector-icons";
import { SheetHeaderItem } from "@/components/atom/SheetHeaderItem";
import { AS } from "../../storageControl";
import { STORAGE_KEYS } from "@/constants";
import { useTrainMenu } from "@/stateBox/useTrainMenu";
const HUB_LOGO_PNG = require("@/assets/icons/hub_logo.png");
/* ------------------------------------------------------------------ */
/* DataSourceAccordionCard */
/* ------------------------------------------------------------------ */
type Feature = { icon: string; label: string; text: string };
type DataSourceAccordionCardProps = {
/** ロゴ画像 (require) */
logo: any;
/** アクセントカラー */
accentColor: string;
/** データソース名 */
title: string;
/** 1行サブタイトル */
tagline: string;
/** スイッチの値 */
enabled: boolean;
/** スイッチ変更ハンドラ */
onToggle: (v: boolean) => void;
/** 説明文 */
description: string;
/** 機能リスト */
features: Feature[];
/** フッターリンクラベル */
linkLabel: string;
/** フッターリンク URL */
linkUrl: string;
};
const DataSourceAccordionCard: React.FC<DataSourceAccordionCardProps> = ({
logo,
accentColor,
title,
tagline,
enabled,
onToggle,
description,
features,
linkLabel,
linkUrl,
}) => {
const [expanded, setExpanded] = useState(false);
return (
<View style={[styles.accordionCard, enabled && styles.accordionCardEnabled]}>
{/* ── ヘッダー行(常時表示) ── */}
<View style={styles.accordionHeader}>
{/* 左:ロゴ */}
<Image source={logo} style={styles.accordionLogo} />
{/* 中央:タイトル+タグライン */}
<View style={styles.accordionTitles}>
<Text style={styles.accordionTitle}>{title}</Text>
<Text style={styles.accordionTagline}>{tagline}</Text>
</View>
{/* 右:スイッチ */}
<Switch
value={enabled}
onValueChange={onToggle}
color={accentColor}
style={styles.accordionSwitch}
/>
</View>
{/* スイッチ状態テキスト */}
<View style={styles.accordionStatusRow}>
<View style={[styles.statusDot, { backgroundColor: enabled ? accentColor : "#ccc" }]} />
<Text style={[styles.statusText, { color: enabled ? accentColor : "#aaa" }]}>
{enabled ? "有効 — 編成データを取得します" : "無効 — データを取得しません"}
</Text>
</View>
{/* ── 展開トリガー ── */}
<TouchableOpacity
style={styles.accordionToggleRow}
onPress={() => setExpanded((v) => !v)}
activeOpacity={0.6}
>
<Text style={styles.accordionToggleLabel}>
{expanded ? "詳細を閉じる" : "鉄道運用Hub について"}
</Text>
<MaterialCommunityIcons
name={expanded ? "chevron-up" : "chevron-down"}
size={16}
color="#888"
/>
</TouchableOpacity>
{/* ── 展開コンテンツ ── */}
{expanded && (
<View style={styles.accordionBody}>
{/* 説明文 */}
<Text style={styles.bodyDesc}>{description}</Text>
{/* 機能リスト */}
<View style={styles.bodyFeatures}>
{features.map((f) => (
<View key={f.icon} style={styles.featureRow}>
<View style={styles.featureIcon}>
<MaterialCommunityIcons name={f.icon as any} size={14} color="#444" />
</View>
<Text style={styles.featureLabel}>{f.label}</Text>
<Text style={styles.featureText}>{f.text}</Text>
</View>
))}
</View>
{/* リンク */}
<TouchableOpacity
style={styles.bodyLink}
onPress={() => Linking.openURL(linkUrl)}
activeOpacity={0.7}
>
<MaterialCommunityIcons name="open-in-new" size={13} color="#555" />
<Text style={styles.bodyLinkText}>{linkLabel}</Text>
</TouchableOpacity>
</View>
)}
</View>
);
};
/* ------------------------------------------------------------------ */
/* 定数 */
/* ------------------------------------------------------------------ */
const UNYOHUB_FEATURES: Feature[] = [
{ icon: "map-marker-radius-outline", label: "走行位置", text: "どの編成がどの駅間を走っているかリアルタイムで確認" },
{ icon: "timetable", label: "時刻表", text: "充当編成情報付きの駅時刻表を閲覧" },
{ icon: "clipboard-list-outline", label: "運用データ", text: "一日の全運用を一覧表示・過去日付への遡り閲覧" },
{ icon: "train-car", label: "編成表", text: "車両メーカー・竣工日・運用履歴を閲覧" },
{ icon: "table-search", label: "運用表", text: "列車番号・両数・出庫場所などで運用を検索" },
];
/* ------------------------------------------------------------------ */
/* DataSourceSettings */
/* ------------------------------------------------------------------ */
export const DataSourceSettings = () => {
const navigation = useNavigation();
const { updatePermission, dataSourcePermission } = useTrainMenu();
@@ -40,41 +179,39 @@ export const DataSourceSettings = () => {
<Text style={styles.noPermissionSubText}>Hubまたはアプリ管理者の権限が必要です</Text>
</View>
) : (
<ScrollView style={styles.content}>
<View style={styles.section}>
<ScrollView style={styles.content} contentContainerStyle={styles.contentInner}>
<Text style={styles.sectionTitle}></Text>
<View style={styles.settingItem}>
<View style={styles.settingTextContainer}>
<Text style={styles.settingTitle}>Hub</Text>
<Text style={styles.settingDescription}>
{"\n"}
</Text>
</View>
<Switch
value={useUnyohub}
onValueChange={handleToggleUnyohub}
color="#0099CC"
/>
</View>
</View>
<View style={styles.infoSection}>
<Text style={styles.infoText}>
{"\n\n"}
</Text>
</View>
</ScrollView>
<DataSourceAccordionCard
logo={HUB_LOGO_PNG}
accentColor="#0099CC"
title="鉄道運用Hub"
tagline="コミュニティによる列車運用情報サービス"
enabled={useUnyohub}
onToggle={handleToggleUnyohub}
description={
"鉄道ファン有志の目撃情報をもとに、どの編成がどの列車に充当されているかをリアルタイムで共有・確認できる無料 Web サービスです。JR 四国をはじめ全国多数の路線系統に対応しています。\n\nデータがある列車では地図上に黄色いマークが表示され、列車情報画面の編成表示も更新されます。"
}
features={UNYOHUB_FEATURES}
linkLabel="unyohub.2pd.jp を開くJR四国"
linkUrl="https://unyohub.2pd.jp/railroad_shikoku/"
/>
<View style={styles.infoSection}>
<Text style={styles.infoText}>
{"\n\n"}
</Text>
</View>
</ScrollView>
)}
</View>
);
};
const styles = StyleSheet.create({
/* ── 権限なし ── */
noPermissionContainer: {
flex: 1,
backgroundColor: "#f8f8fc",
@@ -94,6 +231,7 @@ const styles = StyleSheet.create({
color: "#666",
textAlign: "center",
},
/* ── レイアウト ── */
container: {
flex: 1,
backgroundColor: "#0099CC",
@@ -102,46 +240,152 @@ const styles = StyleSheet.create({
flex: 1,
backgroundColor: "#f8f8fc",
},
section: {
backgroundColor: "white",
marginTop: 20,
marginHorizontal: 10,
borderRadius: 10,
padding: 15,
contentInner: {
paddingHorizontal: 14,
paddingBottom: 40,
gap: 12,
},
sectionTitle: {
fontSize: 18,
fontWeight: "bold",
color: "#333",
marginBottom: 15,
fontSize: 13,
fontWeight: "600",
color: "#888",
letterSpacing: 0.5,
marginTop: 20,
marginLeft: 4,
},
settingItem: {
/* ── アコーディオンカード ── */
accordionCard: {
backgroundColor: "#fff",
borderRadius: 14,
borderWidth: 1,
borderColor: "#e4e4e4",
overflow: "hidden",
},
accordionCardEnabled: {
borderColor: "#0099CC44",
},
accordionHeader: {
flexDirection: "row",
alignItems: "center",
paddingVertical: 10,
paddingHorizontal: 14,
paddingTop: 14,
paddingBottom: 6,
gap: 10,
},
settingTextContainer: {
accordionLogo: {
width: 40,
height: 40,
borderRadius: 8,
flexShrink: 0,
},
accordionTitles: {
flex: 1,
marginRight: 10,
gap: 2,
},
settingTitle: {
fontSize: 16,
accordionTitle: {
fontSize: 15,
fontWeight: "bold",
color: "#111",
},
accordionTagline: {
fontSize: 11,
color: "#888",
},
accordionSwitch: {
flexShrink: 0,
},
accordionStatusRow: {
flexDirection: "row",
alignItems: "center",
gap: 6,
paddingHorizontal: 14,
paddingBottom: 10,
},
statusDot: {
width: 7,
height: 7,
borderRadius: 4,
},
statusText: {
fontSize: 12,
fontWeight: "500",
},
/* ── 展開トリガー ── */
accordionToggleRow: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
paddingHorizontal: 14,
paddingVertical: 10,
borderTopWidth: StyleSheet.hairlineWidth,
borderTopColor: "#ebebeb",
},
accordionToggleLabel: {
fontSize: 12,
color: "#666",
fontWeight: "500",
},
/* ── 展開コンテンツ ── */
accordionBody: {
borderTopWidth: StyleSheet.hairlineWidth,
borderTopColor: "#ebebeb",
padding: 14,
gap: 10,
backgroundColor: "#fafafa",
},
bodyDesc: {
fontSize: 12,
color: "#444",
lineHeight: 19,
},
bodyFeatures: {
gap: 7,
borderTopWidth: StyleSheet.hairlineWidth,
borderTopColor: "#e4e4e4",
paddingTop: 8,
},
featureRow: {
flexDirection: "row",
alignItems: "flex-start",
gap: 6,
},
featureIcon: {
width: 22,
alignItems: "center",
paddingTop: 1,
flexShrink: 0,
},
featureLabel: {
fontSize: 12,
fontWeight: "bold",
color: "#333",
marginBottom: 5,
width: 62,
flexShrink: 0,
},
settingDescription: {
fontSize: 13,
color: "#666",
lineHeight: 18,
featureText: {
fontSize: 12,
color: "#555",
flex: 1,
lineHeight: 17,
},
bodyLink: {
flexDirection: "row",
alignItems: "center",
gap: 5,
borderTopWidth: StyleSheet.hairlineWidth,
borderTopColor: "#e4e4e4",
paddingTop: 8,
marginTop: 2,
},
bodyLinkText: {
fontSize: 12,
color: "#555",
},
/* ── 注意書き ── */
infoSection: {
backgroundColor: "#fff3cd",
marginTop: 20,
marginHorizontal: 10,
borderRadius: 10,
padding: 15,
marginBottom: 30,
padding: 14,
},
infoText: {
fontSize: 13,