6 Commits
v2.1 ... v2.3

Author SHA1 Message Date
366d0dd454 Релиз 2.3: versionCode 5, versionName 2.3 (расчёт прибытия в Кандалакшу) 2026-09-28 06:45:40 +00:00
80b1b2b0cc Считается прибытие в Кандалакшу
Для направления Полярные Зори → Кандалакша время прибытия в таблице
расписания отсутствует, поэтому оно теперь рассчитывается: отправление
из Полярных Зорь плюс 50 минут. Время в пути взято из самой таблицы —
в 12 строках из 13 Кандалакша → Полярные Зори занимает ровно 50 минут.

Заодно исправлена ошибка из v2.2: в карточке Зашейка колонка 3 подписана
как «прибытие Кандалакша», хотя это время в Полярных Зорях (20–30 минут
после прибытия в Зашеек, а не час до Кандалакши). Теперь в карточке
Зашейка показываются оба прибытия: Полярные Зори, затем Кандалакша.

Модель направлений переведена на список прибытий arrs, где время берётся
либо из колонки таблицы (col), либо считается как offset от отправления.
2026-09-28 06:44:57 +00:00
269a651325 Считается прибытие в Кандалакшу
Для направления Полярные Зори → Кандалакша время прибытия в таблице
расписания отсутствует, поэтому оно теперь рассчитывается: отправление
из Полярных Зорь плюс 50 минут. Время в пути взято из самой таблицы —
в 12 строках из 13 Кандалакша → Полярные Зори занимает ровно 50 минут.

Заодно исправлена ошибка из v2.2: в карточке Зашейка колонка 3 подписана
как «прибытие Кандалакша», хотя это время в Полярных Зорях (20–30 минут
после прибытия в Зашеек, а не час до Кандалакши). Теперь в карточке
Зашейка показываются оба прибытия: Полярные Зори, затем Кандалакша.

Модель направлений переведена на список прибытий arrs, где время берётся
либо из колонки таблицы (col), либо считается как offset от отправления.
2026-09-28 06:44:56 +00:00
6386a3420e Релиз 2.2: versionCode 4, versionName 2.2 (карточки Кандалакша/Зашеек, 5 рейсов) 2026-09-28 06:36:40 +00:00
eecb0ed95b Остановки Кандалакша и Зашеек: одна карточка направления, 5 ближайших рейсов
Раньше для крайних остановок показывались две карточки, хотя все рейсы
отправляются в одно время (Кандалакша — по отправлению, Зашеек — по
отправлению) и различаются только конечной остановкой. Карточки объединены:
- Кандалакша: «Полярные Зори, Зашеек», отправление 05:20 → Зашеек 06:30;
- Зашеек: «Полярные Зори, Кандалакша», отправление 06:30 → Кандалакша 06:50.

Число рейсов в карточке увеличено с 3 до 5 (TRIPS_SHOWN). Одна карточка
занимает всю ширину. Время прибытия подписано остановкой, так как в
объединённой карточке время прибытия относится к конечной остановке.
2026-09-28 06:34:53 +00:00
99bb3b2981 Остановки Кандалакша и Зашеек: одна карточка направления, 5 ближайших рейсов
Раньше для крайних остановок показывались две карточки, хотя все рейсы
отправляются в одно время (Кандалакша — по отправлению, Зашеек — по
отправлению) и различаются только конечной остановкой. Карточки объединены:
- Кандалакша: «Полярные Зори, Зашеек», отправление 05:20 → Зашеек 06:30;
- Зашеек: «Полярные Зори, Кандалакша», отправление 06:30 → Кандалакша 06:50.

Число рейсов в карточке увеличено с 3 до 5 (TRIPS_SHOWN). Одна карточка
занимает всю ширину. Время прибытия подписано остановкой, так как в
объединённой карточке время прибытия относится к конечной остановке.
2026-09-28 06:34:51 +00:00
3 changed files with 76 additions and 20 deletions

View File

@@ -19,8 +19,8 @@ android {
applicationId "ru.ua1zbe.bus127" applicationId "ru.ua1zbe.bus127"
minSdk 26 minSdk 26
targetSdk 34 targetSdk 34
versionCode 3 versionCode 5
versionName "2.1" versionName "2.3"
} }
buildTypes { buildTypes {

View File

@@ -35,6 +35,7 @@
.where{text-align:center;color:var(--muted);font-size:13px;margin:2px 0 12px} .where{text-align:center;color:var(--muted);font-size:13px;margin:2px 0 12px}
.dir{display:grid;grid-template-columns:1fr 1fr;gap:12px} .dir{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.dir .card:only-child{grid-column:1 / -1} /* одна карточка — во всю ширину */
.card{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:14px 16px} .card{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:14px 16px}
.card h3{margin:0 0 4px;font-size:15px;font-weight:700} .card h3{margin:0 0 4px;font-size:15px;font-weight:700}
.card h3 .arr{color:var(--accent)} .card h3 .arr{color:var(--accent)}
@@ -123,18 +124,31 @@ const SCHEDULE = [
["18:20","19:10","19:40","20:05"], ["18:20","19:10","19:40","20:05"],
["20:20","21:10","21:40","22:05"] ["20:20","21:10","21:40","22:05"]
]; ];
const TRIPS_SHOWN = 5; // сколько ближайших рейсов показывать в карточке
// Время в пути Полярные Зори ↔ Кандалакша. В таблице расписания его нет:
// в 12 строках из 13 Кандалакша → ПЗ занимает ровно 50 минут
// (в первой строке 60 — по всей видимости опечатка).
const RIDE_PZ_KAND = 50;
// arrs — список прибытий: {label, col} — время из колонки таблицы,
// {label, offset} — «время в пути» от отправления (если col не задан).
const DIRECTIONS = { const DIRECTIONS = {
// От Кандалакши и Зашейка все рейсы идут через Полярные Зори — одна карточка
"Кандалакша": [ "Кандалакша": [
{to:"Полярные Зори", dep:0, arr:1, via:"через 1 час"}, {to:"Полярные Зори, Зашеек", dep:0, via:"через Полярные Зори",
{to:"Зашеек", dep:0, arr:2, via:"через Полярные Зори"} arrs:[{label:"Зашеек", col:2}]}
], ],
"Полярные Зори": [ "Полярные Зори": [
{to:"Зашеек", dep:1, arr:2, via:"≈ 30 мин в пути"}, {to:"Зашеек", dep:1, via:"≈ 30 мин в пути",
{to:"Кандалакша", dep:3, arr:null, via:"≈ 1 час в пути"} arrs:[{label:"Зашеек", col:2}]},
{to:"Кандалакша", dep:3, via:"≈ 50 мин в пути",
arrs:[{label:"Кандалакша", offset:RIDE_PZ_KAND}]}
], ],
"Зашеек": [ "Зашеек": [
{to:"Полярные Зори", dep:2, arr:3, via:"≈ 20 мин в пути"}, {to:"Полярные Зори, Кандалакша", dep:2, via:"через Полярные Зори",
{to:"Кандалакша", dep:2, arr:null, via:"через Полярные Зори"} arrs:[{label:"Полярные Зори", col:3},
{label:"Кандалакша", col:3, offset:RIDE_PZ_KAND}]}
] ]
}; };
const DAYS = ["воскресенье","понедельник","вторник","среда","четверг","пятница","суббота"]; const DAYS = ["воскресенье","понедельник","вторник","среда","четверг","пятница","суббота"];
@@ -173,6 +187,12 @@ function nowMs(){
second: d.getUTCSeconds(), day: d.getUTCDay() }; second: d.getUTCSeconds(), day: d.getUTCDay() };
} }
function fmtHM(m){
const p = n=>String(n).padStart(2,"0");
m = ((m % 1440) + 1440) % 1440; // на случай перехода через полночь
return `${p(Math.floor(m/60))}:${p(m%60)}`;
}
function fmtWait(m){ function fmtWait(m){
if(m <= 0) return "отправление сейчас"; if(m <= 0) return "отправление сейчас";
const h = Math.floor(m/60), mm = m%60; const h = Math.floor(m/60), mm = m%60;
@@ -189,13 +209,21 @@ function render(){
const dirs = DIRECTIONS[currentStop]; const dirs = DIRECTIONS[currentStop];
document.getElementById("dirBox").innerHTML = dirs.map(d=>{ document.getElementById("dirBox").innerHTML = dirs.map(d=>{
const upcoming = SCHEDULE const upcoming = SCHEDULE
.map((r,ri)=>({dep:toMin(r[d.dep]), arr:d.arr!==null?r[d.arr]:null, ri})) .map((r,ri)=>{
const dep = toMin(r[d.dep]);
const arrs = d.arrs.map(a=>{
const base = a.col!==undefined ? toMin(r[a.col]) : dep;
return {label:a.label, t:fmtHM(base + (a.offset||0))};
});
return {dep, arrs, ri};
})
.filter(x=>x.dep >= curMin) .filter(x=>x.dep >= curMin)
.sort((a,b)=>a.dep-b.dep) .sort((a,b)=>a.dep-b.dep)
.slice(0,3) .slice(0,TRIPS_SHOWN)
.map(x=>{ .map(x=>{
const w = x.dep - curMin; const w = x.dep - curMin;
const arr = x.arr ? `<small>прибытие ${x.arr}</small>` : ""; // .trip .t small имеет display:block, поэтому каждый <small> — своя строка
const arr = x.arrs.map(a=>`<small>прибытие ${a.label} ${a.t}</small>`).join("");
return `<div class="trip ${w<15?'soon':''}"> return `<div class="trip ${w<15?'soon':''}">
<span class="t">${SCHEDULE[x.ri][d.dep]}${arr}</span> <span class="t">${SCHEDULE[x.ri][d.dep]}${arr}</span>
<span class="w">${fmtWait(w)}</span> <span class="w">${fmtWait(w)}</span>

View File

@@ -35,6 +35,7 @@
.where{text-align:center;color:var(--muted);font-size:13px;margin:2px 0 12px} .where{text-align:center;color:var(--muted);font-size:13px;margin:2px 0 12px}
.dir{display:grid;grid-template-columns:1fr 1fr;gap:12px} .dir{display:grid;grid-template-columns:1fr 1fr;gap:12px}
.dir .card:only-child{grid-column:1 / -1} /* одна карточка — во всю ширину */
.card{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:14px 16px} .card{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:14px 16px}
.card h3{margin:0 0 4px;font-size:15px;font-weight:700} .card h3{margin:0 0 4px;font-size:15px;font-weight:700}
.card h3 .arr{color:var(--accent)} .card h3 .arr{color:var(--accent)}
@@ -123,18 +124,31 @@ const SCHEDULE = [
["18:20","19:10","19:40","20:05"], ["18:20","19:10","19:40","20:05"],
["20:20","21:10","21:40","22:05"] ["20:20","21:10","21:40","22:05"]
]; ];
const TRIPS_SHOWN = 5; // сколько ближайших рейсов показывать в карточке
// Время в пути Полярные Зори ↔ Кандалакша. В таблице расписания его нет:
// в 12 строках из 13 Кандалакша → ПЗ занимает ровно 50 минут
// (в первой строке 60 — по всей видимости опечатка).
const RIDE_PZ_KAND = 50;
// arrs — список прибытий: {label, col} — время из колонки таблицы,
// {label, offset} — «время в пути» от отправления (если col не задан).
const DIRECTIONS = { const DIRECTIONS = {
// От Кандалакши и Зашейка все рейсы идут через Полярные Зори — одна карточка
"Кандалакша": [ "Кандалакша": [
{to:"Полярные Зори", dep:0, arr:1, via:"через 1 час"}, {to:"Полярные Зори, Зашеек", dep:0, via:"через Полярные Зори",
{to:"Зашеек", dep:0, arr:2, via:"через Полярные Зори"} arrs:[{label:"Зашеек", col:2}]}
], ],
"Полярные Зори": [ "Полярные Зори": [
{to:"Зашеек", dep:1, arr:2, via:"≈ 30 мин в пути"}, {to:"Зашеек", dep:1, via:"≈ 30 мин в пути",
{to:"Кандалакша", dep:3, arr:null, via:"≈ 1 час в пути"} arrs:[{label:"Зашеек", col:2}]},
{to:"Кандалакша", dep:3, via:"≈ 50 мин в пути",
arrs:[{label:"Кандалакша", offset:RIDE_PZ_KAND}]}
], ],
"Зашеек": [ "Зашеек": [
{to:"Полярные Зори", dep:2, arr:3, via:"≈ 20 мин в пути"}, {to:"Полярные Зори, Кандалакша", dep:2, via:"через Полярные Зори",
{to:"Кандалакша", dep:2, arr:null, via:"через Полярные Зори"} arrs:[{label:"Полярные Зори", col:3},
{label:"Кандалакша", col:3, offset:RIDE_PZ_KAND}]}
] ]
}; };
const DAYS = ["воскресенье","понедельник","вторник","среда","четверг","пятница","суббота"]; const DAYS = ["воскресенье","понедельник","вторник","среда","четверг","пятница","суббота"];
@@ -173,6 +187,12 @@ function nowMs(){
second: d.getUTCSeconds(), day: d.getUTCDay() }; second: d.getUTCSeconds(), day: d.getUTCDay() };
} }
function fmtHM(m){
const p = n=>String(n).padStart(2,"0");
m = ((m % 1440) + 1440) % 1440; // на случай перехода через полночь
return `${p(Math.floor(m/60))}:${p(m%60)}`;
}
function fmtWait(m){ function fmtWait(m){
if(m <= 0) return "отправление сейчас"; if(m <= 0) return "отправление сейчас";
const h = Math.floor(m/60), mm = m%60; const h = Math.floor(m/60), mm = m%60;
@@ -189,13 +209,21 @@ function render(){
const dirs = DIRECTIONS[currentStop]; const dirs = DIRECTIONS[currentStop];
document.getElementById("dirBox").innerHTML = dirs.map(d=>{ document.getElementById("dirBox").innerHTML = dirs.map(d=>{
const upcoming = SCHEDULE const upcoming = SCHEDULE
.map((r,ri)=>({dep:toMin(r[d.dep]), arr:d.arr!==null?r[d.arr]:null, ri})) .map((r,ri)=>{
const dep = toMin(r[d.dep]);
const arrs = d.arrs.map(a=>{
const base = a.col!==undefined ? toMin(r[a.col]) : dep;
return {label:a.label, t:fmtHM(base + (a.offset||0))};
});
return {dep, arrs, ri};
})
.filter(x=>x.dep >= curMin) .filter(x=>x.dep >= curMin)
.sort((a,b)=>a.dep-b.dep) .sort((a,b)=>a.dep-b.dep)
.slice(0,3) .slice(0,TRIPS_SHOWN)
.map(x=>{ .map(x=>{
const w = x.dep - curMin; const w = x.dep - curMin;
const arr = x.arr ? `<small>прибытие ${x.arr}</small>` : ""; // .trip .t small имеет display:block, поэтому каждый <small> — своя строка
const arr = x.arrs.map(a=>`<small>прибытие ${a.label} ${a.t}</small>`).join("");
return `<div class="trip ${w<15?'soon':''}"> return `<div class="trip ${w<15?'soon':''}">
<span class="t">${SCHEDULE[x.ri][d.dep]}${arr}</span> <span class="t">${SCHEDULE[x.ri][d.dep]}${arr}</span>
<span class="w">${fmtWait(w)}</span> <span class="w">${fmtWait(w)}</span>