Merge commit '7bbb5b972fdb2eb429e5f71d6dd30e279b47ad10'

This commit is contained in:
harukin-expo-dev-env 2025-08-13 12:50:56 +00:00
commit 0890c1b9ff
10 changed files with 334 additions and 144 deletions

View File

@ -0,0 +1,122 @@
import { trainPosition } from "@/lib/trainPositionTextArray";
import React, { FC } from "react";
import { View, Text, TextStyle, ViewStyle } from "react-native";
type stateBox = {
currentTrainData: any;
platformNumber: any;
title: string;
style?: ViewStyle;
mode?: number;
platformDescription: string;
lineNumber: string;
};
export const PositionBox: FC<stateBox> = (props) => {
const {
currentTrainData,
platformNumber,
title,
style,
mode,
platformDescription,
lineNumber,
} = props;
let firstText = "";
let secondText = "";
let marginText = "";
let externalText = "";
const { isBetween, Pos: PosData } = trainPosition(currentTrainData);
if (isBetween === true) {
const { from, to } = PosData;
firstText = from;
secondText = to;
marginText = mode == 2 ? "→" : "↓";
} else {
const { Pos } = PosData;
if (Pos !== "") {
firstText = Pos;
if (platformNumber) {
secondText = `${platformNumber}番乗り場`;
if (lineNumber) {
externalText = `${lineNumber}番線`;
}
} else if (lineNumber) {
secondText = `${lineNumber}番線`;
}
}
}
return (
<View style={{ ...(mode == 2 ? boxStyle2 : boxStyle), ...style }}>
<Text style={{ fontSize: 12, color: "#0099CC" }}>{title}</Text>
<View style={{ flex: 1 }} />
<View style={{ flexDirection: mode == 2 ? "row" : "column" }}>
{firstText && (
<Text style={mode == 2 ? boxTextStyle2 : (isBetween ? boxTextStyle : boxTextStyleBig)}>
{firstText}
</Text>
)}
{marginText && (
<Text style={{ color: "#0099CC", textAlign: "right" }}>
{marginText}
</Text>
)}
{secondText && (
<Text style={mode == 2 ? boxTextStyle2 :(isBetween ? boxTextStyle : boxTextStyleMini)}>
{secondText}
</Text>
)}
</View>
{(platformDescription || externalText) && (
<View style={{ flexDirection: mode == 2 ? "row" : "column" }}>
<Text
style={{
...{ ...(mode == 2 ? boxTextStyle2 : boxTextStyle) },
fontSize: 10,
}}
>
{" " + externalText}
{" " + platformDescription}
</Text>
</View>
)}
</View>
);
};
const boxStyle: ViewStyle = {
flex: 1,
backgroundColor: "white",
borderRadius: 10,
padding: 10,
margin: 10,
};
const boxStyle2: ViewStyle = {
flex: 1,
backgroundColor: "white",
borderRadius: 10,
padding: 5,
margin: 5,
};
const boxTextStyle2: TextStyle = {
fontSize: 18,
color: "#0099CC",
textAlign: "right",
};
const boxTextStyleBig: TextStyle = {
fontSize: 28,
color: "#0099CC",
textAlign: "right",
};
const boxTextStyleMini: TextStyle = {
fontSize: 16,
color: "#0099CC",
textAlign: "right",
};
const boxTextStyle: TextStyle = {
fontSize: 25,
color: "#0099CC",
textAlign: "right",
};

View File

@ -15,21 +15,7 @@ export const StateBox: FC<stateBox> = (props) => {
<Text style={{ fontSize: 12, color: "#0099CC" }}>{title}</Text>
<View style={{ flex: 1 }} />
<View style={{ flexDirection: mode == 2 ? "row" : "column" }}>
{text?.match("") ? (
<>
<Text style={mode == 2 ? boxTextStyle2 : boxTextStyle}>
{text.split("")[0]}
</Text>
<Text style={{ color: "#0099CC", textAlign: "right" }}>
{mode == 2 ? "→" : "↓"}
</Text>
<Text style={mode == 2 ? boxTextStyle2 : boxTextStyle}>
{text.split("")[1]}
</Text>
</>
) : (
<Text style={mode == 2 ? boxTextStyle2 : boxTextStyle}>{text}</Text>
)}
<Text style={mode == 2 ? boxTextStyle2 : boxTextStyle}>{text}</Text>
</View>
{endText && (
<View style={{ flexDirection: mode == 2 ? "row" : "column" }}>

View File

@ -1,6 +1,7 @@
import React, { useState, useEffect } from "react";
import { View, TouchableOpacity, useWindowDimensions } from "react-native";
import { StateBox } from "./StateBox";
import { PositionBox } from "./PositionBox";
import { useDeviceOrientationChange } from "../../../stateBox/useDeviceOrientationChange";
import { getStationList2 } from "../../../lib/getStationList";
import { useCurrentTrain } from "../../../stateBox/useCurrentTrain";
@ -26,54 +27,58 @@ export const TrainDataView = ({
const [mapsStationData, setMapsStationData] = useState(undefined);
const [platformNumber, setPlatformNumber] = useState();
const [lineNumber, setLineNumber] = useState();
const [platformDescription, setPlatformDescription] = useState();
useEffect(() => {
//currentTrainData.Pos = "鴨川~端岡"; //test
if (!currentTrainData) return;
fetch(
`https://n8n.haruk.in/webhook/JR-shikoku-PosID?PosNum=${currentTrainData?.PosNum}&Line=${currentTrainData?.Line}`
`https://n8n.haruk.in/webhook/JR-shikoku-PosID-v3?PosId=${currentTrainData?.PosNum}&lineName=${currentTrainData?.Line}&StationName=${currentTrainData?.Pos}`
)
.then((res) => res.json())
.then((data) => {
setPlatformNumber(data?.type == "Station" ? data?.platform : undefined);
setPlatformDescription(
data?.type == "Station" ? data?.description : undefined
);
if (!data) return;
const {
type,
stationName,
lineNumber,
platformNumber,
position,
description,
} = data;
if (type == "Station") {
setLineNumber(lineNumber);
setPlatformNumber(platformNumber);
setPlatformDescription(description);
} else {
setLineNumber(undefined);
setPlatformNumber(undefined);
setPlatformDescription(undefined);
}
});
}, [currentTrainData]);
useEffect(() => {
getStationList2().then(setMapsStationData);
}, []);
const onLine = !!currentPosition?.toString().length;
const trainPositionText = (trainData) => {
const { isBetween, Pos: PosData } = trainPosition(trainData);
const { from, to, Pos } = PosData;
if (isBetween === true) return `${from}${to}`;
if (Pos == "") return "";
return `${Pos}${platformNumber ? ` ${platformNumber}番線` : ""}`;
};
const [dialog, setDialog] = useState(false);
const [deleteDialog, setDeleteDialog] = useState(false);
const [posInput, setPosInput] = useState("");
const [descInput, setDescInput] = useState("");
const [stationInput, setStationInput] = useState("");
const [stationNumberInput, setStationNumberInput] = useState("");
const [lineInput, setLineInput] = useState("");
return (
<>
<TrainPositionDataPush
dialog={dialog}
setDialog={setDialog}
currentTrainData={currentTrainData}
stationInput={stationInput}
stationNumberInput={stationNumberInput}
lineInput={lineInput}
setLineInput={setLineInput}
posInput={posInput}
descInput={descInput}
setPosInput={setPosInput}
descInput={descInput}
setDescInput={setDescInput}
station={{
Station_JP: trainPositionText(currentTrainData),
StationNumber: currentPosition[0],
}}
/>
<View
style={{
@ -95,19 +100,18 @@ export const TrainDataView = ({
platformDescription == undefined
)
return;
setStationInput(`${Pos.from}${Pos.to}`);
setStationNumberInput(
getStationID(currentTrainData?.Pos, stationList)
);
setPosInput(platformNumber?.toString() || "");
setDeleteDialog(true);
setLineInput(lineNumber?.toString() || "");
} else {
setStationInput(Pos.Pos);
setStationNumberInput(
getStationID(currentTrainData?.Pos, stationList)
);
setDescInput(platformDescription || "");
setPosInput(platformNumber?.toString() || "");
setLineInput(lineNumber?.toString() || "");
setDialog(true);
}
}}
@ -134,11 +138,13 @@ export const TrainDataView = ({
SheetManager.hide("EachTrainInfo");
}}
>
<StateBox
<PositionBox
mode={mode}
title={`現在地 ${currentPosition?.toString()}${onLine ? "▶️" : ""}`}
text={trainPositionText(currentTrainData)}
endText={platformDescription ? `${platformDescription}` : ""}
currentTrainData={currentTrainData}
platformNumber={platformNumber}
lineNumber={lineNumber}
platformDescription={platformDescription || ""}
style={
onLine
? { borderWidth: 1, borderColor: "red", borderStyle: "solid" }

View File

@ -50,45 +50,66 @@ export const HeaderText: FC<Props> = ({
const { expoPushToken } = useNotification();
// 列車名、種別、フォントの取得
const [typeName, trainName, fontAvailable, isOneMan, infogram, isEdit] =
useMemo(() => {
const { type, trainName, trainNumDistance, infogram, isEdit } =
customTrainDataDetector(trainNum, allCustomTrainData);
const [typeString, fontAvailable, isOneMan] = getStringConfig(
type,
trainNum
);
switch (true) {
case trainName !== "":
// 特急の場合は、列車名を取得
// 列番対称データがある場合はそれから列車番号を取得
return [
typeString,
trainName +
(trainNumDistance !== null
? ` ${parseInt(trainNum) - trainNumDistance}`
: ""),
fontAvailable,
isOneMan,
infogram,
isEdit,
];
case trainData[trainData.length - 1] === undefined:
return [typeString, "", fontAvailable, isOneMan, infogram, isEdit];
default:
// 行先がある場合は、行先を取得
return [
typeString,
migrateTrainName(
trainData[trainData.length - 1].split(",")[0] + "行き"
),
fontAvailable,
isOneMan,
infogram,
isEdit
];
}
}, [trainData]);
const [
typeName,
trainName,
fontAvailable,
isOneMan,
infogram,
isEdit,
uwasa,
vehicleFormation,
] = useMemo(() => {
const { type, trainName, trainNumDistance, infogram, isEdit, uwasa, vehicleFormation } =
customTrainDataDetector(trainNum, allCustomTrainData);
const [typeString, fontAvailable, isOneMan] = getStringConfig(
type,
trainNum
);
switch (true) {
case trainName !== "":
// 特急の場合は、列車名を取得
// 列番対称データがある場合はそれから列車番号を取得
return [
typeString,
trainName +
(trainNumDistance !== null
? ` ${parseInt(trainNum) - trainNumDistance}`
: ""),
fontAvailable,
isOneMan,
infogram,
isEdit,
uwasa,
vehicleFormation
];
case trainData[trainData.length - 1] === undefined:
return [
typeString,
"",
fontAvailable,
isOneMan,
infogram,
isEdit,
uwasa,
vehicleFormation
];
default:
// 行先がある場合は、行先を取得
return [
typeString,
migrateTrainName(
trainData[trainData.length - 1].split(",")[0] + "行き"
),
fontAvailable,
isOneMan,
infogram,
isEdit,
uwasa,
vehicleFormation
];
}
}, [trainData]);
return (
<View
@ -121,7 +142,19 @@ export const HeaderText: FC<Props> = ({
{isOneMan && <OneManText />}
<Text style={textConfig}>{trainName}</Text>
<InfogramText infogram={infogram} />
{isEdit &&<FontAwesome name="commenting-o" size={20} color="white" style={{ marginLeft: 5 }} onPress={()=>alert("このアイコン、列車データはコミュニティによってリアルタイム追加されています。")} />}
{isEdit && (
<FontAwesome
name="commenting-o"
size={20}
color="white"
style={{ marginLeft: 5 }}
onPress={() =>
alert(
`[このアイコン、列車データはコミュニティによってリアルタイム追加されています。]\n使用車両情報:\n${vehicleFormation}\n投稿者メモ:\n${uwasa || "なし"}`
)
}
/>
)}
</TouchableOpacity>
<View style={{ flex: 1 }} />

View File

@ -197,6 +197,19 @@ export const FixedContentBottom = (props) => {
</Text>
</TextBox>
<Text style={{ fontWeight: "bold", fontSize: 20 }}>その他</Text>
<TextBox
backgroundColor="rgb(88, 101, 242)"
flex={1}
onPressButton={() => Linking.openURL("https://twitter.com/Xprocess_main/status/1955242437817012300")}
>
<Text style={{ color: "white", fontWeight: "bold", fontSize: 20 }}>
公式Discordのご案内
</Text>
<Text style={{ color: "white", fontSize: 18 }}>
皆さんの目撃情報をアプリに反映しませんかDiscordに登録して運用を報告しましょう
</Text>
</TextBox>
<TextBox
backgroundColor="linear-gradient(120deg, rgba(247,135,54,0.208) 0%, rgba(54,125,247,0.208) 100%)"
flex={1}

View File

@ -17,7 +17,7 @@ import { SwitchArea } from "../atom/SwitchArea";
import { useNotification } from "../../stateBox/useNotifications";
import { SheetHeaderItem } from "@/components/atom/SheetHeaderItem";
const versionCode = "6.1.3"; // Update this version code as needed
const versionCode = "6.1.4"; // Update this version code as needed
export const SettingTopPage = ({
testNFC,

View File

@ -1,6 +1,5 @@
import React, { FC, useEffect, useState } from "react";
import { Linking, TouchableOpacity, Text } from "react-native";
import { Dialog, Button, Input } from "react-native-elements";
import { TouchableOpacity } from "react-native";
import { checkDuplicateTrainData } from "../../lib/checkDuplicateTrainData";
import { getTrainDelayStatus } from "../../lib/getTrainDelayStatus";
import { getTrainType } from "../../lib/getTrainType";
@ -121,6 +120,7 @@ export const EachData: FC<Props> = (props) => {
const [dialog, setDialog] = useState(false);
const [deleteDialog, setDeleteDialog] = useState(false);
const [posInput, setPosInput] = useState("");
const [lineInput, setLineInput] = useState("");
const [descInput, setDescInput] = useState("");
const [stationInput, setStationInput] = useState("");
const [stationNumberInput, setStationNumberInput] = useState("");
@ -157,13 +157,13 @@ export const EachData: FC<Props> = (props) => {
setDialog={setDialog}
{...{
currentTrainData,
stationInput,
stationNumberInput,
lineInput,
setLineInput,
posInput,
descInput,
setPosInput,
setDescInput,
station,
descInput,
setDescInput
}}
/>
<TouchableOpacity
@ -215,6 +215,8 @@ export const EachData: FC<Props> = (props) => {
setPlatformNumber={setPlatformNumber}
platformDescription={platformDescription}
platformNumber={platformNumber}
lineInput={lineInput}
setLineInput={setLineInput}
key={d.train + "-trainPosition"}
/>
)}

View File

@ -1,4 +1,4 @@
import React, { FC, useEffect } from "react";
import React, { FC, useEffect, useState } from "react";
import { Text, TextStyle, View, TouchableOpacity } from "react-native";
import { useStationList } from "../../../stateBox/useStationList";
import {
@ -24,9 +24,11 @@ type Props = {
setDialog: (dialog: boolean) => void;
setDeleteDialog: (dialog: boolean) => void;
platformDescription: string;
platformNumber: number;
platformNumber: string;
setPlatformDescription: (desc: string) => void;
setPlatformNumber: (num: number) => void;
setPlatformNumber: (num: string) => void;
lineInput: string;
setLineInput: (line: string) => void;
};
export const TrainPosition: FC<Props> = ({
@ -43,28 +45,55 @@ export const TrainPosition: FC<Props> = ({
setPlatformNumber,
platformDescription,
platformNumber,
setLineInput,
}) => {
const { currentTrain } = useCurrentTrain();
const { stationList } = useStationList();
type data = {
type: string;
lineNumber: string;
platformNumber: string;
position: string;
stationName: string;
description: string;
};
const [database, setDatabase] = useState<data>(null);
const [text, setText] = useState("");
const [masterText, setMasterText] = useState("");
useEffect(() => {
const text = `${currentTrainData?.PosNum} ${currentTrainData?.Line} ${currentTrainData?.Pos}`;
setText(trainIDSwitch ? text : masterText);
return () => {
setText("");
};
}, [masterText, trainIDSwitch]);
useEffect(() => {
fetch(
`https://n8n.haruk.in/webhook/JR-shikoku-PosID?PosNum=${currentTrainData?.PosNum}&Line=${currentTrainData?.Line}`
`https://n8n.haruk.in/webhook/JR-shikoku-PosID-v3?PosId=${currentTrainData?.PosNum}&lineName=${currentTrainData?.Line}&StationName=${currentTrainData?.Pos}`
)
.then((res) => res.json())
.then((data: { type: string; platform: number; description: string }) => {
setPlatformNumber(data?.type == "Station" ? data?.platform : undefined);
setPlatformDescription(
data?.type == "Station" ? data?.description : undefined
);
.then((data: data) => {
const { type, platformNumber, description, lineNumber } = data;
setDatabase(data);
const { isBetween, Pos } = trainPosition(currentTrainData);
if (isBetween === true) {
// 移動中
setMasterText(`現在地:${Pos.from}${Pos.to}間を走行中`);
} else {
if (Pos.Pos) {
let platform = platformNumber ? `${platformNumber}番乗り場` : "";
let line = lineNumber ? `${lineNumber}番線` : "";
setMasterText(
`現在地:${Pos.Pos} ${platform || line} ${description || ""}`
);
} else {
setMasterText("");
}
}
});
}, [currentTrainData, currentTrain]);
const trainPositionText = (trainData: trainDataType) => {
const { isBetween, Pos } = trainPosition(trainData);
if (isBetween === true) return `現在地:${Pos.from}${Pos.to}間を走行中`;
else return Pos.Pos == "" ? "" : `現在地:${Pos.Pos}`;
};
return () => {
setMasterText("");
};
}, [currentTrainData?.PosNum, currentTrainData?.Line, currentTrainData?.Pos]);
return (
<TouchableOpacity
@ -80,22 +109,18 @@ export const TrainPosition: FC<Props> = ({
}}
onLongPress={() => {
const { isBetween, Pos } = trainPosition(currentTrainData);
setStationNumberInput(getStationID(currentTrainData?.Pos, stationList));
setPosInput(database?.platformNumber?.toString() || "");
if (isBetween === true) {
if (platformNumber == undefined && platformDescription == undefined)
if (database?.platformNumber == undefined && database?.description == undefined)
return;
setStationInput(`${Pos.from}${Pos.to}`);
setStationNumberInput(
getStationID(currentTrainData?.Pos, stationList)
);
setPosInput(platformNumber?.toString() || "");
setDeleteDialog(true);
} else {
setStationInput(Pos.Pos);
setStationNumberInput(
getStationID(currentTrainData?.Pos, stationList)
);
setDescInput(platformDescription || "");
setPosInput(platformNumber?.toString() || "");
setStationInput(currentTrainData?.Pos);
setDescInput(database?.description || "");
setLineInput(database?.lineNumber?.toString() || "");
setPlatformNumber(database?.platformNumber?.toString() || "");
setDialog(true);
}
}}
@ -105,13 +130,7 @@ export const TrainPosition: FC<Props> = ({
style={{ ...descriptionStyle, color: "green" }}
numberOfLines={numberOfLines}
>
{`${
trainIDSwitch
? currentTrainData?.PosNum + currentTrainData?.Line
: trainPositionText(currentTrainData)
} ${platformNumber ? platformNumber + "番線" : ""} ${
platformDescription ? "(" + platformDescription + ")" : ""
}`}
{text}
</Text>
</View>
</TouchableOpacity>

View File

@ -17,7 +17,7 @@ export const TrainPositionDataDelete: FC<Props> = ({
stationNumberInput,
}) => {
const sendPlatformData = () => {
fetch(`https://n8n.haruk.in/webhook/JR-shikoku-PosID`, {
fetch(`https://n8n.haruk.in/webhook/JR-shikoku-PosID-v3`, {
method: "DELETE",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({

View File

@ -1,46 +1,47 @@
import React, { FC, useState } from "react";
import React, { FC } from "react";
import { Text } from "react-native";
import { Dialog, Input, Button } from "react-native-elements";
import { trainDataType } from "../../../lib/trainPositionTextArray";
import { useCurrentTrain } from "../../../stateBox/useCurrentTrain";
import { getStationID } from "@/lib/eachTrainInfoCoreLib/getStationData";
import { useStationList } from "@/stateBox/useStationList";
type Props = {
dialog: boolean;
setDialog: (dialog: boolean) => void;
currentTrainData: trainDataType;
stationInput: string;
stationNumberInput: string;
posInput: string;
descInput: string;
lineInput: string;
setPosInput: (pos: string) => void;
setDescInput: (desc: string) => void;
station: {
Station_JP: string;
StationNumber: string;
};
setLineInput: (line: string) => void;
};
export const TrainPositionDataPush: FC<Props> = ({
dialog,
setDialog,
currentTrainData,
stationInput,
stationNumberInput,
lineInput,
setLineInput,
posInput,
descInput,
setPosInput,
descInput,
setDescInput,
station,
}) => {
const { stationList } = useStationList();
const sendPlatformData = () => {
fetch(`https://n8n.haruk.in/webhook/JR-shikoku-PosID`, {
fetch(`https://n8n.haruk.in/webhook/JR-shikoku-PosID-v3`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
PosId: currentTrainData?.PosNum,
lineName: currentTrainData?.Line,
PlatformNum: parseInt(posInput),
Description: descInput,
StationName: station.Station_JP,
StationId: station.StationNumber,
PosId: currentTrainData?.PosNum, //自動位置情報ID
StationId: getStationID(currentTrainData?.Pos, stationList), //自動駅ID
StationName: currentTrainData?.Pos, //自動:駅名、漢字
lineName: currentTrainData?.Line, //自動位置情報路線ID(koutoku/yosan)
Description: descInput, //手動入力、参考情報
platformNumber: parseInt(posInput), //手動入力、乗り場表記
lineNumber: parseInt(lineInput), //手動入力、番線表記
}),
})
.then(() => {
@ -55,22 +56,30 @@ export const TrainPositionDataPush: FC<Props> = ({
};
return (
<Dialog isVisible={dialog} onBackdropPress={() => setDialog(false)}>
<Text style={{ fontSize: 20, fontWeight: "bold" }}>稿</Text>
<Text>: {currentTrainData?.Line}</Text>
<Text>ID: {currentTrainData?.PosNum}</Text>
<Text>: {stationInput}</Text>
<Text>: {currentTrainData?.Pos}</Text>
<Text>: {stationNumberInput}</Text>
<Input
label="番線"
label="乗り場"
inputMode="numeric"
value={posInput}
onChangeText={setPosInput}
/>
<Input
label="番線"
inputMode="numeric"
value={lineInput}
onChangeText={setLineInput}
/>
<Input
label="参考情報"
inputMode="text"
value={descInput}
onChangeText={setDescInput}
/>
<Text style={{ fontSize: 12, fontWeight: "bold" }}>稿稿</Text>
<Button title="送信" onPress={sendPlatformData} />
</Dialog>
);