diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Enmarcha.Backend/Services/Processors/NextStopsProcessor.cs | 51 | ||||
| -rw-r--r-- | src/Enmarcha.Backend/Types/Arrivals/Arrival.cs | 5 | ||||
| -rw-r--r-- | src/frontend/app/api/schema.ts | 1 | ||||
| -rw-r--r-- | src/frontend/app/components/arrivals/ArrivalCard.tsx | 16 |
4 files changed, 71 insertions, 2 deletions
diff --git a/src/Enmarcha.Backend/Services/Processors/NextStopsProcessor.cs b/src/Enmarcha.Backend/Services/Processors/NextStopsProcessor.cs index 4c0b8ac..0f86be5 100644 --- a/src/Enmarcha.Backend/Services/Processors/NextStopsProcessor.cs +++ b/src/Enmarcha.Backend/Services/Processors/NextStopsProcessor.cs @@ -43,7 +43,31 @@ public class NextStopsProcessor : IArrivalsProcessor .ToList(); } + // Remove last stop since it doesn't make sense to show "via" for the terminus + arrival.NextStops = arrival.NextStops.Take(arrival.NextStops.Count - 1).ToList(); + + if (feedId == "xunta") + { + arrival.OriginStops = otpArrival.Trip.Stoptimes + .Where(s => s.ScheduledDeparture < currentStopDeparture) + .OrderBy(s => s.ScheduledDeparture) + .Take(1) + .Select(s => $"{s.Stop.Name} -- {s.Stop.Description}") + .Distinct() + .ToList(); + } + else if (feedId == "renfe") + { + arrival.OriginStops = otpArrival.Trip.Stoptimes + .Where(s => s.ScheduledDeparture < currentStopDeparture) + .OrderBy(s => s.ScheduledDeparture) + .Take(1) + .Select(s => FeedService.NormalizeStopName(feedId, s.Stop.Name)) + .ToList(); + } + arrival.Headsign.Marquee = GenerateMarquee(feedId, arrival.NextStops); + arrival.Headsign.Origin = GenerateOrigin(feedId, arrival.OriginStops); } return Task.CompletedTask; @@ -122,6 +146,33 @@ public class NextStopsProcessor : IArrivalsProcessor }; } + private static string? GenerateOrigin(string feedId, List<string> originStops) + { + if (originStops.Count == 0) return null; + + if (feedId == "xunta") + { + var points = originStops.Select(SplitXuntaStopDescription).ToList(); + var concellos = points + .Select(p => p.concello) + .Where(c => !string.IsNullOrWhiteSpace(c)) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); + + return concellos.Count > 0 ? string.Join(" > ", concellos) : null; + } + + if (feedId == "renfe") + { + // For trains just show the origin terminus + return originStops.First(); + } + + // For local bus feeds, origin is generally not very informative, + // but return the first stop for completeness + return originStops.First(); + } + private static (string nombre, string parroquia, string concello) SplitXuntaStopDescription(string stopName) { var parts = stopName.Split(" -- ", 3); diff --git a/src/Enmarcha.Backend/Types/Arrivals/Arrival.cs b/src/Enmarcha.Backend/Types/Arrivals/Arrival.cs index 0e74a44..3272a05 100644 --- a/src/Enmarcha.Backend/Types/Arrivals/Arrival.cs +++ b/src/Enmarcha.Backend/Types/Arrivals/Arrival.cs @@ -34,6 +34,9 @@ public class Arrival public List<string> NextStops { get; set; } = []; [JsonIgnore] + public List<string> OriginStops { get; set; } = []; + + [JsonIgnore] public ArrivalsAtStopResponse.Arrival? RawOtpTrip { get; set; } [JsonIgnore] public bool Delete { get; set; } @@ -73,6 +76,8 @@ public class HeadsignInfo [JsonPropertyName("destination")] public required string Destination { get; set; } [JsonPropertyName("marquee")] public string? Marquee { get; set; } + + [JsonPropertyName("origin")] public string? Origin { get; set; } } public class ArrivalDetails diff --git a/src/frontend/app/api/schema.ts b/src/frontend/app/api/schema.ts index 864ea57..d900055 100644 --- a/src/frontend/app/api/schema.ts +++ b/src/frontend/app/api/schema.ts @@ -11,6 +11,7 @@ export const HeadsignInfoSchema = z.object({ badge: z.string().optional().nullable(), destination: z.string().nullable(), marquee: z.string().optional().nullable(), + origin: z.string().optional().nullable(), }); export const ArrivalPrecisionSchema = z.enum([ diff --git a/src/frontend/app/components/arrivals/ArrivalCard.tsx b/src/frontend/app/components/arrivals/ArrivalCard.tsx index 51e0803..3aae65e 100644 --- a/src/frontend/app/components/arrivals/ArrivalCard.tsx +++ b/src/frontend/app/components/arrivals/ArrivalCard.tsx @@ -34,7 +34,7 @@ const AutoMarquee = ({ text }: { text: string }) => { if (!el) return; const checkScroll = () => { - const charWidth = 8; + const charWidth = 12; const availableWidth = el.offsetWidth; const textWidth = text.length * charWidth; setShouldScroll(textWidth > availableWidth); @@ -252,10 +252,22 @@ export const ArrivalCard: React.FC<ArrivalCardProps> = ({ {operator && ( <span className="text-xs font-mono text-slate-700 dark:text-slate-200 font-medium shrink-0"> {operator} + {headsign.destination || headsign.origin ? ( + <> · </> + ) : ( + "" + )} + </span> + )} + {headsign.origin && ( + <span className="text-xs font-mono text-slate-500 dark:text-slate-400 shrink-0"> + Proc: {headsign.origin}{" "} {headsign.marquee && <> · </>} </span> )} - {headsign.marquee && <AutoMarquee text={headsign.marquee} />} + {headsign.marquee && ( + <AutoMarquee text={"Via: " + headsign.marquee} /> + )} </div> </div> </div> |
