Tabs for different providers and better BC route names (#12)

* Made new Sidebar widget with OCCT

* Separate tabs for providers

* Fixed BC data

* Proper sorting for BC
This commit is contained in:
Levi Lesches 2025-05-02 02:46:03 -04:00 committed by GitHub
parent c878d08c23
commit 575c4f9a16
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 1173 additions and 1502 deletions

View file

@ -17,44 +17,8 @@ class HomePage extends ReactiveWidget<HomeModel> {
children: [
AnimatedContainer(
duration: Durations.short4,
width: model.shouldShowMarkers ? 250 : 0,
child: Card(
clipBehavior: Clip.hardEdge,
elevation: 8,
child: Column(
children: [
Text(
"Select routes",
maxLines: 1,
style: context.textTheme.titleLarge,
textAlign: TextAlign.center,
),
Text(
"Or click anywhere on the map",
maxLines: 1,
style: context.textTheme.bodyMedium,
textAlign: TextAlign.center,
),
const Divider(),
Expanded(
child: ListView(
children: [
for (final route in model.routeNames) ListTile(
title: Text(route, maxLines: 1),
subtitle: const Text("BC Transit", maxLines: 1),
// Can't use CheckboxListTile, since we must remove the
// checkboxes manually to prevent layour errors
trailing: !model.shouldShowMarkers ? null : Checkbox(
value: model.routesToShow.contains(route),
onChanged: (value) => model.showRoute(route, shouldShow: value!),
),
),
],
),
),
],
),
),
width: model.shouldShowMarkers ? 300 : 0,
child: Sidebar(model),
),
Expanded(
child: Column(

View file

@ -36,8 +36,24 @@ mixin HomeMarkers on ChangeNotifier {
if (_endMarker != null) _endMarker!,
};
Set<String> routeNames = {};
Set<String> bcRouteNames = {};
Set<String> occtRouteNames = {};
Set<String> routesToShow = {};
Map<String, int> stopCounts = {};
Iterable<(String, List<String>)> get providers => [
("OCCT", occtRouteNames.toList()..sort()),
("BC Transit", bcRouteNames.toList()..sort(compareNums)),
];
int parseBcNumber(String routeName) {
// eg, "53)" --> 53
final first = routeName.split(" ").first;
final withoutParen = first.substring(0, first.length - 1);
return int.parse(withoutParen);
}
int compareNums(String a, String b) =>
parseBcNumber(a).compareTo(parseBcNumber(b));
MarkerId get markerId => switch (markerState!) {
MarkerState.start => const MarkerId("start"),
@ -58,7 +74,16 @@ mixin HomeMarkers on ChangeNotifier {
stops = await services.api.getStops();
if (stops == null) return;
for (final stop in stops!) {
routeNames.addAll(stop.routes);
for (final route in stop.routes) {
stopCounts[route] ??= 0;
stopCounts[route] = stopCounts[route]! + 1;
}
if (stop.provider.contains("OCCT")) {
occtRouteNames.addAll(stop.routes);
} else {
bcRouteNames.addAll(stop.routes);
}
}
notifyListeners();
}

View file

@ -0,0 +1,63 @@
import "package:client/view_models.dart";
import "package:client/widgets.dart";
import "package:flutter/material.dart";
class Sidebar extends ReusableReactiveWidget<HomeModel> {
const Sidebar(super.model);
@override
Widget build(BuildContext context, HomeModel model) => DefaultTabController(
length: 2,
child: Card(
clipBehavior: Clip.hardEdge,
elevation: 8,
child: Column(
children: [
Text(
"Select routes",
maxLines: 1,
style: context.textTheme.titleLarge,
textAlign: TextAlign.center,
),
Text(
"Or click anywhere on the map",
maxLines: 1,
style: context.textTheme.bodyMedium,
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
TabBar(
tabs: [
for (final (provider, _) in model.providers)
Tab(text: provider),
],
),
Expanded(
child: TabBarView(
children: [
for (final (_, routesList) in model.providers)
ListView(
children: [
for (final route in routesList) ListTile(
title: Text(route, maxLines: 1),
subtitle: Text(
"${model.stopCounts[route] ?? 0} stops",
maxLines: 1,
),
// Can't use CheckboxListTile, since we must remove the
// checkboxes manually to prevent layour errors
trailing: !model.shouldShowMarkers ? null : Checkbox(
value: model.routesToShow.contains(route),
onChanged: (value) => model.showRoute(route, shouldShow: value!),
),
),
],
),
],
),
),
],
),
),
);
}

View file

@ -4,6 +4,7 @@ export "package:go_router/go_router.dart";
export "src/widgets/lat_long_editor.dart";
export "src/widgets/reactive_widget.dart";
export "src/widgets/sidebar.dart";
/// Helpful methods on [BuildContext].
extension ContextUtils on BuildContext {

File diff suppressed because it is too large Load diff

View file

@ -27,7 +27,8 @@ class BcStopParser extends Parser<Stop> {
Future<Map<RouteID, String>> getRouteNames() async => {
for (final row in await readCsv(routeNamesFile))
RouteID(row[0]): row[3],
if (row[2].isNotEmpty)
RouteID(row[0]): "${row[2]}) ${row[3]}",
};
Future<Map<StopID, Stop>> getStops() async {
@ -60,7 +61,8 @@ class BcStopParser extends Parser<Stop> {
for (final (tripID, trip) in trips.records) {
if (!trip.contains(stop.id)) continue;
final routeID = routes[tripID]!;
final routeName = routeNames[routeID]!;
final routeName = routeNames[routeID];
if (routeName == null) continue; // old route
stop.routes.add(routeName);
}
}