From a4e3e0833d5aa33ba77ee6389e7af75dfe473cc8 Mon Sep 17 00:00:00 2001 From: harukin-expo-dev-env Date: Thu, 26 Mar 2026 08:56:31 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20UI=E3=81=AE=E8=89=B2=E8=A8=AD=E5=AE=9A?= =?UTF-8?q?=E3=82=92=E3=83=80=E3=83=BC=E3=82=AF=E3=83=A2=E3=83=BC=E3=83=89?= =?UTF-8?q?=E3=81=AB=E5=AF=BE=E5=BF=9C=E3=81=95=E3=81=9B=E3=80=81=E8=A6=96?= =?UTF-8?q?=E8=AA=8D=E6=80=A7=E3=82=92=E5=90=91=E4=B8=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../EachTrainInfo/EachStopList.tsx | 2 +- .../EachTrainInfoCore.tsx | 2 +- components/AllTrainDiagramView.tsx | 2 +- .../Apps/FixedPositionBox/FixedTrainBox.tsx | 1 + components/Apps/WebView.tsx | 1 + components/Menu/Carousel/CarouselBox.tsx | 2 +- components/Menu/RailScope/SearchUnitBox.tsx | 2 +- components/Menu/StationPagination.tsx | 1 + components/Settings/LauncherIconSettings.tsx | 9 +- components/Settings/NotificationSettings.tsx | 2 +- components/StationDiagram/ListViewItem.tsx | 4 +- lib/webViewInjectjavascript.ts | 124 ++++++++++++++++-- stateBox/useTrainMenu.tsx | 2 + 13 files changed, 131 insertions(+), 23 deletions(-) diff --git a/components/ActionSheetComponents/EachTrainInfo/EachStopList.tsx b/components/ActionSheetComponents/EachTrainInfo/EachStopList.tsx index dceddc5..cc538b6 100644 --- a/components/ActionSheetComponents/EachTrainInfo/EachStopList.tsx +++ b/components/ActionSheetComponents/EachTrainInfo/EachStopList.tsx @@ -310,7 +310,7 @@ const TimeText: FC<{ const [seString, seType] = parseSeString(se); return ( - + {!!currentTrainData?.delay && currentTrainData?.delay != "入線" && currentTrainData?.delay != 0 && ( diff --git a/components/ActionSheetComponents/EachTrainInfoCore.tsx b/components/ActionSheetComponents/EachTrainInfoCore.tsx index aa96aa3..ebe8270 100644 --- a/components/ActionSheetComponents/EachTrainInfoCore.tsx +++ b/components/ActionSheetComponents/EachTrainInfoCore.tsx @@ -205,7 +205,7 @@ export const EachTrainInfoCore = ({ } > {customTrainType.data === "notService" && ( - + この列車には乗車できません。 )} diff --git a/components/AllTrainDiagramView.tsx b/components/AllTrainDiagramView.tsx index ec1aa52..1d5c19c 100644 --- a/components/AllTrainDiagramView.tsx +++ b/components/AllTrainDiagramView.tsx @@ -348,7 +348,7 @@ export const AllTrainDiagramView: FC = () => { onEndEditing={() => {}} onChange={(ret) => setInput(ret.nativeEvent.text)} value={input} - style={{ flex: 1 }} + style={{ flex: 1, color: colors.text }} /> diff --git a/components/Apps/FixedPositionBox/FixedTrainBox.tsx b/components/Apps/FixedPositionBox/FixedTrainBox.tsx index 148f694..268f19e 100644 --- a/components/Apps/FixedPositionBox/FixedTrainBox.tsx +++ b/components/Apps/FixedPositionBox/FixedTrainBox.tsx @@ -1028,6 +1028,7 @@ const EachStopData: FC = (props) => { color: colors.text, backgroundColor: colors.background, fontWeight: "bold", + opacity: 0.5, }} > {arrivalMinute} diff --git a/components/Apps/WebView.tsx b/components/Apps/WebView.tsx index f2f7e4c..55b9a8c 100644 --- a/components/Apps/WebView.tsx +++ b/components/Apps/WebView.tsx @@ -149,6 +149,7 @@ export const AppsWebView = ({ openStationACFromEachTrainInfo }) => { return ( { const active = index == listIndex; const numberKey = d[0].StationNumber + index; - return dotButton ? ( + return dotButton && d[0].StationNumber ? ( setListIndex(index)} currentStation={d} diff --git a/components/Menu/RailScope/SearchUnitBox.tsx b/components/Menu/RailScope/SearchUnitBox.tsx index 8ca8bee..ae98146 100644 --- a/components/Menu/RailScope/SearchUnitBox.tsx +++ b/components/Menu/RailScope/SearchUnitBox.tsx @@ -111,7 +111,7 @@ export const SearchUnitBox = ({ setStationSource({ type: "search", query: ret.nativeEvent.text, lineId }) } value={query} - style={{ flex: 1, height: "100%", paddingVertical: 0 }} + style={{ flex: 1, height: "100%", paddingVertical: 0, color: colors.text }} /> {query && ( = (props) => { setAnimation(animation + 1 < data.length ? animation + 1 : 0); }, 2000); + if (data.length === 0 || !data[animation]) return null; const lineID = data[animation].StationNumber.slice(0, 1); const lineName = data[animation].StationNumber.slice(1); const size = active ? 24 : 18; diff --git a/components/Settings/LauncherIconSettings.tsx b/components/Settings/LauncherIconSettings.tsx index 3c1cc88..af938e9 100644 --- a/components/Settings/LauncherIconSettings.tsx +++ b/components/Settings/LauncherIconSettings.tsx @@ -31,7 +31,8 @@ export const LauncherIconSettings = () => { <> 現在のアイコン: @@ -43,7 +44,7 @@ export const LauncherIconSettings = () => { flexDirection: "column", alignItems: "center", justifyContent: "center", - backgroundColor: "#DDDDDD", + backgroundColor: colors.backgroundTertiary, padding: 10, }} > @@ -63,7 +64,7 @@ export const LauncherIconSettings = () => { resizeMode="contain" /> - JR四国非公式アプリ + JR四国非公式アプリ ) : ( @@ -112,7 +113,7 @@ export const LauncherIconSettings = () => { }} > - {id} + {id} ); })} diff --git a/components/Settings/NotificationSettings.tsx b/components/Settings/NotificationSettings.tsx index d9f5d2a..f40c0ac 100644 --- a/components/Settings/NotificationSettings.tsx +++ b/components/Settings/NotificationSettings.tsx @@ -70,7 +70,7 @@ export const NotificationSettings = () => { str="怪レい列車" /> Clipboard.setStringAsync(expoPushToken)} > 通知を受け取りたい項目を選択してください。チェックボックスを選び、右上の「登録実行」を押すと設定が反映され、通知が届くようになります。 diff --git a/components/StationDiagram/ListViewItem.tsx b/components/StationDiagram/ListViewItem.tsx index ae2e187..3340db0 100644 --- a/components/StationDiagram/ListViewItem.tsx +++ b/components/StationDiagram/ListViewItem.tsx @@ -88,7 +88,7 @@ export const ListViewItem: FC<{ const defaultLineColor = station.length > 0 ? lineColorList[station[0]?.StationNumber?.slice(0, 1)] - : "black"; + : undefined; // to_data_colorが設定されていればそれを最優先 const finalColor = to_data_color && to_data_color.length > 0 @@ -350,7 +350,7 @@ export const ListViewItem: FC<{ flex: 1, paddingHorizontal: 10, fontWeight: "bold", - color: isCancelled ? "gray" : lineColor, + color: isCancelled ? "gray" : (lineColor ?? colors.text), textDecorationLine: isCancelled ? "line-through" : "none", }} > diff --git a/lib/webViewInjectjavascript.ts b/lib/webViewInjectjavascript.ts index d4f2db2..8c9c04c 100644 --- a/lib/webViewInjectjavascript.ts +++ b/lib/webViewInjectjavascript.ts @@ -18,6 +18,8 @@ export interface InjectJavascriptOptions { useUnyohub: string; /** えれサイト使用スイッチ ("true" | "false") */ useElesite: string; + /** ダークモードかどうか */ + isDark: boolean; } export const injectJavascriptData = ({ @@ -28,6 +30,7 @@ export const injectJavascriptData = ({ uiSetting, useUnyohub, useElesite, + isDark, }: InjectJavascriptOptions): string => { // 一番上のメニュー非表示 地図スイッチによって切り替え const topMenu = @@ -49,11 +52,73 @@ export const injectJavascriptData = ({ // 上部ヘッダーの取り扱い、自動再読み込み、setStringsの実行 const bootData = ` - // 停止中 点滅アニメーション CSS を動的注入 + // ダークモードフラグ & テーマ色定数 + const _isDark = ${isDark}; + const _t = _isDark + ? { bg:'#0d1117', bgSec:'#161b22', bgTer:'#21262d', text:'#e6edf3', textSec:'#8b949e', border:'#30363d', cardBg:'#48505acc', shadow:'rgba(0,0,0,0.5)' } + : { bg:'#ffffff', bgSec:'#f8f8fc', bgTer:'#f0f0f0', text:'#000000', textSec:'#555555', border:'#cccccc', cardBg:'#ffffffcc', shadow:'rgba(0,0,0,0.2)' }; + + // ダークモード用: インライン背景色をHSL変換で暗くするヘルパー + // 彩度を維持しつつ明度(L)を指定値に設定 + const _darkenBg = _isDark ? function(el, targetL) { + try { + var bg = el.style.backgroundColor; + if (!bg || bg === 'transparent') return; + var m = bg.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/); + if (!m) return; + var r = +m[1]/255, g = +m[2]/255, b = +m[3]/255; + var mx = Math.max(r,g,b), mn = Math.min(r,g,b); + var l = (mx+mn)/2; + if (l < 0.12) return; + var h = 0, s = 0, d = mx - mn; + if (d > 0) { + s = l > 0.5 ? d/(2-mx-mn) : d/(mx+mn); + if (mx===r) h = ((g-b)/d + (g1)t-=1; + if(t<1/6) return p+(q-p)*6*t; + if(t<1/2) return q; + if(t<2/3) return p+(q-p)*(2/3-t)*6; + return p; + }; + var q2 = nl < 0.5 ? nl*(1+ns) : nl+ns-nl*ns; + var p2 = 2*nl - q2; + var nr = hue2rgb(p2,q2,h+1/3); + var ng = hue2rgb(p2,q2,h); + var nb = hue2rgb(p2,q2,h-1/3); + el.style.backgroundColor = 'rgb('+(nr*255|0)+','+(ng*255|0)+','+(nb*255|0)+')'; + } catch(e) {} + } : function(){}; + + // 停止中 点滅アニメーション CSS + ダークモード CSS を動的注入 (function(){ const s = document.createElement('style'); s.textContent = '@keyframes _jrs_blink{0%,55%{opacity:1}70%,90%{opacity:0}100%{opacity:1}}' + - '@keyframes _jrs_glow{0%,55%{box-shadow:0 0 12px 3px rgba(255,0,0,0.9)}70%,90%{box-shadow:0 0 6px 2px rgba(255,0,0,0.45)}100%{box-shadow:0 0 12px 3px rgba(255,0,0,0.9)}}'; + '@keyframes _jrs_glow{0%,55%{box-shadow:0 0 12px 3px rgba(255,0,0,0.9)}70%,90%{box-shadow:0 0 6px 2px rgba(255,0,0,0.45)}100%{box-shadow:0 0 12px 3px rgba(255,0,0,0.9)}}' + + (_isDark ? ( + 'html,body{background-color:#2b2f36!important;}' + + '#main{background-color:#2b2f36!important;}' + + '#disp{background-color:transparent!important;}' + + '#header{background-color:#343a42!important;color:#e6edf3!important;border-bottom:1px solid #444c56!important;}' + + '#headerStr{color:#e6edf3!important;}' + + '#topHeader{background-color:#343a42!important;border-bottom:1px solid #444c56!important;}' + + '#topHeader div{color:#e6edf3!important;}' + + '#topHeader .accordion{background-color:#3b4148!important;color:#e6edf3!important;}' + + '[id^="stationBlock"]{background-color:rgba(0,0,0,0.55)!important;}' + + '#disp>[id*="~"]{background-color:rgba(0,0,0,0.35)!important;}' + + '#pMENU_2,#pMENU_3,#pMENU_k{background-color:#343a42!important;border-color:#444c56!important;}' + + '#pMENU_2 div,#pMENU_3 div,#pMENU_k div{color:#e6edf3!important;}' + + '#modal_content{background-color:#343a42!important;color:#e6edf3!important;}' + + '#modal_content button{background-color:#3b4148!important;color:#e6edf3!important;border-color:#444c56!important;}' + + '.accordionClass{background-color:#343a42!important;color:#e6edf3!important;}' + + 'select{background-color:#3b4148!important;color:#e6edf3!important;border-color:#444c56!important;}' + + '#upTrainCrossBar,#dwTrainCrossBar{opacity:0.85!important;}' + ) : ''); document.head.appendChild(s); })(); @@ -752,7 +817,7 @@ export const injectJavascriptData = ({ gradient = getColors[0]; } - const optionalTextColor = optionalText.includes("最終") ? "red" : "black"; + const optionalTextColor = optionalText.includes("最終") ? "red" : _t.text; const unyohubFormation = getUnyohubFormation(列番データ); const hasUnyohub = unyohubFormation !== null; const unyohubStale = hasUnyohub && isUnyohubStale(列番データ); @@ -797,11 +862,11 @@ export const injectJavascriptData = ({ badgeHtml += "
E
"; } - 行き先情報.insertAdjacentHTML('beforebegin', "
" + badgeHtml + "

" + (TrainNumberOverride ? TrainNumberOverride : TrainNumber) + "

" + (isWanman ? "ワンマン " : "") + "

" + viaData + "

" + optionalText + "

" + trainName + "

" + (ToData ? ToData + "行" : ToData) + "

" + trainType + "

" + (hasProblem ? "‼️停止中‼️" : "") + "

"); + 行き先情報.insertAdjacentHTML('beforebegin', "
" + badgeHtml + "

" + (TrainNumberOverride ? TrainNumberOverride : TrainNumber) + "

" + (isWanman ? "ワンマン " : "") + "

" + viaData + "

" + optionalText + "

" + trainName + "

" + (ToData ? ToData + "行" : ToData) + "

" + trainType + "

" + (hasProblem ? "‼️停止中‼️" : "") + "

"); `: ` - 行き先情報.insertAdjacentHTML('beforebegin', "

" + returnText1 + "

"); - 行き先情報.insertAdjacentHTML('beforebegin', "

" + (ToData ? ToData + "行 " : ToData) + "

" + (TrainNumberOverride ? TrainNumberOverride : TrainNumber) + "

"); - 行き先情報.insertAdjacentHTML('beforebegin', "

" + (hasProblem ? "‼️停止中‼️" : "") + "

"); + 行き先情報.insertAdjacentHTML('beforebegin', "

" + returnText1 + "

"); + 行き先情報.insertAdjacentHTML('beforebegin', "

" + (ToData ? ToData + "行 " : ToData) + "

" + (TrainNumberOverride ? TrainNumberOverride : TrainNumber) + "

"); + 行き先情報.insertAdjacentHTML('beforebegin', "

" + (hasProblem ? "‼️停止中‼️" : "") + "

"); `} } `; @@ -816,24 +881,36 @@ const setNewTrainItem = (element,hasProblem,type)=>{ const data = trainDataList.find(e => e.train_id === 列番データ); const _bc = TRAIN_TYPE_CFG[data.type]; element.style.borderColor = _bc ? _bc.borderColor : 'black'; - element.style.backgroundColor = _bc ? _bc.bgColor : '#ffffffcc'; + if (_bc) { + if (_isDark) { + if (_bc.bgColor === '#ffffffcc') { element.style.backgroundColor = _t.cardBg; } + else if (_bc.bgColor === '#ffd0a9ff') { element.style.backgroundColor = '#5c3a1acc'; } + else if (_bc.bgColor === '#c7c7c7cc') { element.style.backgroundColor = '#555b62cc'; } + else { element.style.backgroundColor = _bc.bgColor; } + } else { + element.style.backgroundColor = _bc.bgColor; + } + } else { + element.style.backgroundColor = _t.cardBg; + } }else{ + element.style.backgroundColor = _t.cardBg; if(element.getAttribute('offclick').includes("express")){ element.style.borderColor = '#ff0000ff'; }else if(element.getAttribute('offclick').includes("rapid")){ element.style.borderColor = '#008cffff'; }else{ - element.style.borderColor = 'black'; + element.style.borderColor = _isDark ? '#30363d' : 'black'; } } - element.style.borderWidth = '2px'; + element.style.borderWidth = _isDark ? '1px' : '2px'; element.style.borderStyle = 'solid'; element.style.borderRadius = '10%'; if(hasProblem){ element.style.boxShadow = ''; element.style.animation = '_jrs_glow 3s linear infinite'; }else{ - element.style.boxShadow = '0 0 4px rgba(0, 0, 0, 0.2)'; + element.style.boxShadow = _isDark ? '0 2px 8px rgba(0,0,0,0.6)' : '0 0 4px rgba(0,0,0,0.2)'; element.style.animation = ''; } element.style.margin = '2px'; @@ -1099,6 +1176,13 @@ const setStrings = () =>{ if(i.style.backgroundColor != 'rgb(247, 247, 247)'){ i.childNodes.forEach(m=> m.style.width = '20vw') } + if (_isDark && i.nodeType === 1) { + var _cbg = getComputedStyle(i).backgroundColor; + var _cm = _cbg && _cbg.match(/rgba?\\((\\d+),\\s*(\\d+),\\s*(\\d+)/); + if (_cm && (+_cm[1] + +_cm[2] + +_cm[3]) > 500) { + i.style.backgroundColor = '#161b22'; + } + } }) } @@ -1112,10 +1196,12 @@ const setStrings = () =>{ document.querySelector('#pMENU_2').style.borderColor='#00d3e8'; document.querySelector('#pMENU_2').style.borderWidth='2px'; document.querySelector('#pMENU_2').style.borderRadius='10%'; + document.querySelector('#pMENU_2').style.backgroundColor=_t.bgSec; document.querySelector('#pMENU_3').style.borderStyle='solid'; document.querySelector('#pMENU_3').style.borderColor='#00d3e8'; document.querySelector('#pMENU_3').style.borderWidth='2px'; document.querySelector('#pMENU_3').style.borderRadius='10%'; + document.querySelector('#pMENU_3').style.backgroundColor=_t.bgSec; document.querySelectorAll('#pMENU_2 div').forEach((d)=>d.style.padding = '10px'); document.querySelectorAll('#pMENU_3 div').forEach((d)=>d.style.padding = '10px'); document.querySelectorAll('#topHeader div').forEach((d)=>d.style.width = '100vw'); @@ -1123,6 +1209,22 @@ const setStrings = () =>{ document.getElementById('disp').style.width = '100vw'; document.getElementById('disp').style.overflowX = 'hidden'; + // ダークモード: 左右ゾーンの縦グラデーション + if (_isDark) { + document.querySelectorAll('[id^="stationBlock"]').forEach(function(block) { + // 左右ゾーン:元の縦グラデーションを継承する微妙な明暗 + if (block.children[0]) block.children[0].style.backgroundImage = 'linear-gradient(to bottom, rgba(200,210,230,0.04), transparent 30%, transparent 70%, rgba(200,210,230,0.04))'; + if (block.children[2]) block.children[2].style.backgroundImage = 'linear-gradient(to bottom, rgba(200,210,230,0.04), transparent 30%, transparent 70%, rgba(200,210,230,0.04))'; + }); + // ~区間の左右にもグラデーション + document.querySelectorAll('#disp > [id*="~"]').forEach(function(row) { + var up = row.querySelector(':scope > [id^="Up"]'); + var down = row.querySelector(':scope > [id^="Down"]'); + if (up) up.style.backgroundImage = 'linear-gradient(to bottom, rgba(200,210,230,0.04), transparent 30%, transparent 70%, rgba(200,210,230,0.04))'; + if (down) down.style.backgroundImage = 'linear-gradient(to bottom, rgba(200,210,230,0.04), transparent 30%, transparent 70%, rgba(200,210,230,0.04))'; + }); + } + }catch(e){ alert("本家サーバーからのデータ取得に失敗しました。"); window.ReactNativeWebView.postMessage(JSON.stringify({type:"LoadError"})); diff --git a/stateBox/useTrainMenu.tsx b/stateBox/useTrainMenu.tsx index 9179a9b..249b630 100644 --- a/stateBox/useTrainMenu.tsx +++ b/stateBox/useTrainMenu.tsx @@ -6,6 +6,7 @@ import React, { FC, } from "react"; +import { useColorScheme } from "react-native"; import { ASCore } from "../storageControl"; import { getStationList2 } from "../lib/getStationList"; @@ -118,6 +119,7 @@ export const TrainMenuProvider: FC = ({ children }) => { uiSetting, useUnyohub: useUnyohubSetting, useElesite: useEleSiteSetting, + isDark: useColorScheme() === "dark", }); useEffect(() => {