new pipeline
Some checks failed
Release / meta (push) Successful in 2s
Release / android-build (push) Failing after 15s
Release / linux-build (push) Failing after 34s
Release / windows-build (push) Has been cancelled
Release / release-dev (push) Has been cancelled
Release / release-master (push) Has been cancelled

This commit is contained in:
2025-12-11 01:24:44 +00:00
parent 40ee16d2d5
commit e34c689ed9
4 changed files with 232 additions and 155 deletions

View File

@@ -10,10 +10,10 @@ env:
JAVA_VERSION: "17" JAVA_VERSION: "17"
ANDROID_SDK_ROOT: "${{ github.workspace }}/android-sdk" ANDROID_SDK_ROOT: "${{ github.workspace }}/android-sdk"
FLUTTER_CHANNEL: "stable" FLUTTER_CHANNEL: "stable"
BUILD_WINDOWS: "false" # set to "true" when you have a Windows runner available BUILD_WINDOWS: "false" # set to "true" when you actually want Windows builds
jobs: jobs:
build: meta:
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs: outputs:
base_version: ${{ steps.meta.outputs.base }} base_version: ${{ steps.meta.outputs.base }}
@@ -21,7 +21,21 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install OS deps (Android + Linux desktop) - name: Determine version
id: meta
run: |
RAW_VERSION=$(awk '/^version:/{print $2}' pubspec.yaml)
BASE_VERSION=${RAW_VERSION%%+*}
echo "base=${BASE_VERSION}" >> "$GITHUB_OUTPUT"
android-build:
runs-on: ubuntu-latest
needs: meta
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install OS deps (Android)
run: | run: |
if command -v sudo >/dev/null 2>&1; then if command -v sudo >/dev/null 2>&1; then
SUDO="sudo" SUDO="sudo"
@@ -29,7 +43,7 @@ jobs:
SUDO="" SUDO=""
fi fi
$SUDO apt-get update $SUDO apt-get update
$SUDO apt-get install -y unzip xz-utils zip libstdc++6 libglu1-mesa clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev curl $SUDO apt-get install -y unzip xz-utils zip libstdc++6 liblzma-dev curl
- name: Setup Java - name: Setup Java
uses: actions/setup-java@v4 uses: actions/setup-java@v4
@@ -43,8 +57,11 @@ jobs:
curl -fsSL https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip -o /tmp/cli-tools.zip curl -fsSL https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip -o /tmp/cli-tools.zip
unzip -q /tmp/cli-tools.zip -d "$ANDROID_SDK_ROOT"/cmdline-tools unzip -q /tmp/cli-tools.zip -d "$ANDROID_SDK_ROOT"/cmdline-tools
mv "$ANDROID_SDK_ROOT"/cmdline-tools/cmdline-tools "$ANDROID_SDK_ROOT"/cmdline-tools/latest mv "$ANDROID_SDK_ROOT"/cmdline-tools/cmdline-tools "$ANDROID_SDK_ROOT"/cmdline-tools/latest
yes | "$ANDROID_SDK_ROOT"/cmdline-tools/latest/bin/sdkmanager --licenses
yes | "$ANDROID_SDK_ROOT"/cmdline-tools/latest/bin/sdkmanager "platform-tools" "platforms;android-33" "build-tools;33.0.2" yes | "$ANDROID_SDK_ROOT"/cmdline-tools/latest/bin/sdkmanager --sdk_root="$ANDROID_SDK_ROOT" --licenses
yes | "$ANDROID_SDK_ROOT"/cmdline-tools/latest/bin/sdkmanager --sdk_root="$ANDROID_SDK_ROOT" \
"platform-tools" "platforms;android-33" "build-tools;33.0.2"
echo "ANDROID_SDK_ROOT=$ANDROID_SDK_ROOT" >> "$GITHUB_ENV" echo "ANDROID_SDK_ROOT=$ANDROID_SDK_ROOT" >> "$GITHUB_ENV"
echo "$ANDROID_SDK_ROOT/platform-tools" >> "$GITHUB_PATH" echo "$ANDROID_SDK_ROOT/platform-tools" >> "$GITHUB_PATH"
echo "$ANDROID_SDK_ROOT/build-tools/33.0.2" >> "$GITHUB_PATH" echo "$ANDROID_SDK_ROOT/build-tools/33.0.2" >> "$GITHUB_PATH"
@@ -57,46 +74,34 @@ jobs:
- name: Flutter dependencies - name: Flutter dependencies
run: flutter pub get run: flutter pub get
- name: Enable Linux desktop
run: flutter config --enable-linux-desktop
- name: Determine version
id: meta
run: |
RAW_VERSION=$(awk '/^version:/{print $2}' pubspec.yaml)
BASE_VERSION=${RAW_VERSION%%+*}
echo "base=${BASE_VERSION}" >> "$GITHUB_OUTPUT"
- name: Build APK (release) - name: Build APK (release)
run: | run: |
flutter build apk --release flutter build apk --release
cp build/app/outputs/flutter-apk/app-release.apk app-release.apk cp build/app/outputs/flutter-apk/app-release.apk app-release.apk
- name: Build Linux binary (release) - name: Upload Android APK artifact
run: |
flutter build linux --release
tar -C build/linux/x64/release/bundle -czf app-linux-x64.tar.gz .
- name: Upload APK artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: apk name: android-apk
path: app-release.apk path: app-release.apk
- name: Upload Linux artifact linux-build:
uses: actions/upload-artifact@v4 runs-on: ubuntu-latest
with: needs: meta
name: linux
path: app-linux-x64.tar.gz
windows-build:
if: env.BUILD_WINDOWS == 'true'
runs-on: windows-latest
needs: build
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install OS deps (Linux desktop)
run: |
if command -v sudo >/dev/null 2>&1; then
SUDO="sudo"
else
SUDO=""
fi
$SUDO apt-get update
$SUDO apt-get install -y unzip xz-utils zip libstdc++6 libglu1-mesa clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev curl
- name: Setup Flutter - name: Setup Flutter
uses: subosito/flutter-action@v2 uses: subosito/flutter-action@v2
with: with:
@@ -105,48 +110,88 @@ jobs:
- name: Flutter dependencies - name: Flutter dependencies
run: flutter pub get run: flutter pub get
- name: Enable Linux desktop
run: flutter config --enable-linux-desktop
- name: Build Linux binary (release)
run: |
flutter build linux --release
tar -C build/linux/x64/release/bundle -czf app-linux-x64.tar.gz .
- name: Upload Linux artifact
uses: actions/upload-artifact@v4
with:
name: linux-bundle
path: app-linux-x64.tar.gz
windows-build:
runs-on: windows-latest
needs: meta
# Job always runs; individual steps are gated so release jobs can still depend on it.
steps:
- name: Checkout
if: env.BUILD_WINDOWS == 'true'
uses: actions/checkout@v4
- name: Setup Flutter
if: env.BUILD_WINDOWS == 'true'
uses: subosito/flutter-action@v2
with:
channel: ${{ env.FLUTTER_CHANNEL }}
- name: Flutter dependencies
if: env.BUILD_WINDOWS == 'true'
run: flutter pub get
- name: Enable Windows desktop - name: Enable Windows desktop
if: env.BUILD_WINDOWS == 'true'
run: flutter config --enable-windows-desktop run: flutter config --enable-windows-desktop
- name: Build Windows binary (release) - name: Build Windows binary (release)
if: env.BUILD_WINDOWS == 'true'
run: | run: |
flutter build windows --release flutter build windows --release
powershell -Command "Compress-Archive -Path build/windows/x64/runner/Release/* -DestinationPath app-windows-x64.zip" powershell -Command "Compress-Archive -Path build/windows/x64/runner/Release/* -DestinationPath app-windows-x64.zip"
- name: Upload Windows artifact - name: Upload Windows artifact
if: env.BUILD_WINDOWS == 'true'
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: windows name: windows-zip
path: app-windows-x64.zip path: app-windows-x64.zip
release-dev: release-dev:
if: github.ref_name == 'dev' if: github.ref_name == 'dev'
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [build] needs:
- meta
- android-build
- linux-build
- windows-build
steps: steps:
- name: Download APK - name: Download Android APK
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
with: with:
name: apk name: android-apk
path: artifacts path: artifacts
- name: Download Linux bundle - name: Download Linux bundle
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
with: with:
name: linux name: linux-bundle
path: artifacts path: artifacts
- name: Download Windows bundle (optional) - name: Download Windows bundle (optional)
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
with: with:
name: windows name: windows-zip
path: artifacts path: artifacts
if-no-files-found: ignore if-no-files-found: ignore
- name: Prepare artifacts and tag - name: Prepare artefacts and tag
id: bundle id: bundle
run: | run: |
BASE="${{ needs.build.outputs.base_version }}" BASE="${{ needs.meta.outputs.base_version }}"
TAG="v${BASE}-dev" TAG="v${BASE}-dev"
mv artifacts/app-release.apk "artifacts/app-${BASE}-dev.apk" mv artifacts/app-release.apk "artifacts/app-${BASE}-dev.apk"
@@ -180,31 +225,35 @@ jobs:
release-master: release-master:
if: github.ref_name == 'master' if: github.ref_name == 'master'
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [build] needs:
- meta
- android-build
- linux-build
- windows-build
steps: steps:
- name: Download APK - name: Download Android APK
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
with: with:
name: apk name: android-apk
path: artifacts path: artifacts
- name: Download Linux bundle - name: Download Linux bundle
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
with: with:
name: linux name: linux-bundle
path: artifacts path: artifacts
- name: Download Windows bundle (optional) - name: Download Windows bundle (optional)
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
with: with:
name: windows name: windows-zip
path: artifacts path: artifacts
if-no-files-found: ignore if-no-files-found: ignore
- name: Prepare artifacts and tag - name: Prepare artefacts and tag
id: bundle id: bundle
run: | run: |
BASE="${{ needs.build.outputs.base_version }}" BASE="${{ needs.meta.outputs.base_version }}"
TAG="v${BASE}" TAG="v${BASE}"
mv artifacts/app-release.apk "artifacts/app-${BASE}.apk" mv artifacts/app-release.apk "artifacts/app-${BASE}.apk"

View File

@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:mileograph_flutter/components/calculator/calculator.dart'; import 'package:mileograph_flutter/components/calculator/calculator.dart';
@@ -25,21 +27,19 @@ class _NewEntryPageState extends State<NewEntryPage> {
final _mileageController = TextEditingController(); final _mileageController = TextEditingController();
final _networkController = TextEditingController(); final _networkController = TextEditingController();
bool _submitting = false; bool _submitting = false;
bool _initialised = false;
bool _useManualMileage = false; bool _useManualMileage = false;
RouteResult? _routeResult; RouteResult? _routeResult;
final List<_TractionItem> _tractionItems = [_TractionItem.marker()]; final List<_TractionItem> _tractionItems = [_TractionItem.marker()];
int? _selectedTripId; int? _selectedTripId;
bool _tripsRequested = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) { Future.microtask(() {
if (!_tripsRequested) { if (!mounted) return;
_tripsRequested = true; final data = context.read<DataService>();
context.read<DataService>().fetchTrips(); data.fetchClassList();
} data.fetchTrips();
}); });
} }
@@ -140,21 +140,6 @@ class _NewEntryPageState extends State<NewEntryPage> {
} }
} }
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (!_initialised) {
_initialised = true;
WidgetsBinding.instance.addPostFrameCallback((_) {
context.read<DataService>().fetchClassList();
if (!_tripsRequested) {
_tripsRequested = true;
context.read<DataService>().fetchTrips();
}
});
}
}
Future<void> _openCalculator() async { Future<void> _openCalculator() async {
final result = await Navigator.of(context).push<RouteResult>( final result = await Navigator.of(context).push<RouteResult>(
MaterialPageRoute( MaterialPageRoute(
@@ -304,6 +289,9 @@ class _NewEntryPageState extends State<NewEntryPage> {
}; };
await api.post('/add', body); await api.post('/add', body);
} }
if (mounted) {
context.read<DataService>().refreshLegs();
}
try { try {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(

View File

@@ -150,11 +150,12 @@ class _TractionPageState extends State<TractionPage> {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text('Fleet', Text('Fleet', style: Theme.of(context).textTheme.labelMedium),
style: Theme.of(context).textTheme.labelMedium),
const SizedBox(height: 2), const SizedBox(height: 2),
Text('Traction', Text(
style: Theme.of(context).textTheme.headlineSmall), 'Traction',
style: Theme.of(context).textTheme.headlineSmall,
),
], ],
), ),
IconButton( IconButton(
@@ -174,8 +175,10 @@ class _TractionPageState extends State<TractionPage> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text('Filters', Text(
style: Theme.of(context).textTheme.titleMedium), 'Filters',
style: Theme.of(context).textTheme.titleMedium,
),
TextButton( TextButton(
onPressed: _clearFilters, onPressed: _clearFilters,
child: const Text('Clear'), child: const Text('Clear'),
@@ -199,24 +202,30 @@ class _TractionPageState extends State<TractionPage> {
(c) => c.toLowerCase().contains(query), (c) => c.toLowerCase().contains(query),
); );
}, },
initialValue: initialValue: TextEditingValue(
TextEditingValue(text: _classController.text), text: _classController.text,
fieldViewBuilder: (context, controller, focusNode, ),
onFieldSubmitted) { fieldViewBuilder:
controller.value = _classController.value; (
return TextField( context,
controller: controller, controller,
focusNode: focusNode, focusNode,
decoration: const InputDecoration( onFieldSubmitted,
labelText: 'Class', ) {
border: OutlineInputBorder(), controller.value = _classController.value;
), return TextField(
onChanged: (val) { controller: controller,
_classController.text = val; focusNode: focusNode,
decoration: const InputDecoration(
labelText: 'Class',
border: OutlineInputBorder(),
),
onChanged: (val) {
_classController.text = val;
},
onSubmitted: (_) => _refreshTraction(),
);
}, },
onSubmitted: (_) => _refreshTraction(),
);
},
onSelected: (String selection) { onSelected: (String selection) {
setState(() { setState(() {
_selectedClass = selection; _selectedClass = selection;
@@ -249,7 +258,7 @@ class _TractionPageState extends State<TractionPage> {
), ),
), ),
FilterChip( FilterChip(
label: const Text('Had only'), label: const Text('Had first'),
selected: _hadOnly, selected: _hadOnly,
onSelected: (v) { onSelected: (v) {
setState(() => _hadOnly = v); setState(() => _hadOnly = v);
@@ -258,13 +267,18 @@ class _TractionPageState extends State<TractionPage> {
), ),
TextButton.icon( TextButton.icon(
onPressed: () => setState( onPressed: () => setState(
() => _showAdvancedFilters = !_showAdvancedFilters), () => _showAdvancedFilters = !_showAdvancedFilters,
icon: Icon(_showAdvancedFilters ),
? Icons.expand_less icon: Icon(
: Icons.expand_more), _showAdvancedFilters
label: Text(_showAdvancedFilters ? Icons.expand_less
? 'Hide filters' : Icons.expand_more,
: 'More filters'), ),
label: Text(
_showAdvancedFilters
? 'Hide filters'
: 'More filters',
),
), ),
ElevatedButton.icon( ElevatedButton.icon(
onPressed: _refreshTraction, onPressed: _refreshTraction,
@@ -398,10 +412,9 @@ class _TractionPageState extends State<TractionPage> {
children: [ children: [
Text( Text(
'No traction found', 'No traction found',
style: Theme.of(context) style: Theme.of(context).textTheme.titleMedium?.copyWith(
.textTheme fontWeight: FontWeight.w700,
.titleMedium ),
?.copyWith(fontWeight: FontWeight.w700),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
const Text('Try relaxing the filters or sync again.'), const Text('Try relaxing the filters or sync again.'),
@@ -449,8 +462,10 @@ class _TractionPageState extends State<TractionPage> {
icon: const Icon(Icons.arrow_back), icon: const Icon(Icons.arrow_back),
label: const Text('Back'), label: const Text('Back'),
style: TextButton.styleFrom( style: TextButton.styleFrom(
padding: padding: const EdgeInsets.symmetric(
const EdgeInsets.symmetric(horizontal: 12, vertical: 10), horizontal: 12,
vertical: 10,
),
foregroundColor: Theme.of(context).colorScheme.onSurface, foregroundColor: Theme.of(context).colorScheme.onSurface,
), ),
), ),
@@ -484,25 +499,24 @@ class _TractionPageState extends State<TractionPage> {
children: [ children: [
Text( Text(
'${loco.locoClass} ${loco.number}', '${loco.locoClass} ${loco.number}',
style: Theme.of(context) style: Theme.of(context).textTheme.titleMedium?.copyWith(
.textTheme fontWeight: FontWeight.w700,
.titleMedium ),
?.copyWith(fontWeight: FontWeight.w700),
), ),
if ((loco.name ?? '').isNotEmpty) if ((loco.name ?? '').isNotEmpty)
Text( Text(
loco.name ?? '', loco.name ?? '',
style: Theme.of(context) style: Theme.of(context).textTheme.bodyMedium?.copyWith(
.textTheme fontStyle: FontStyle.italic,
.bodyMedium ),
?.copyWith(fontStyle: FontStyle.italic),
), ),
], ],
), ),
Chip( Chip(
label: Text(status), label: Text(status),
backgroundColor: backgroundColor: Theme.of(
Theme.of(context).colorScheme.surfaceContainerHighest, context,
).colorScheme.surfaceContainerHighest,
), ),
], ],
), ),
@@ -529,9 +543,11 @@ class _TractionPageState extends State<TractionPage> {
} }
}); });
}, },
icon: Icon(isSelected icon: Icon(
? Icons.remove_circle_outline isSelected
: Icons.add_circle_outline), ? Icons.remove_circle_outline
: Icons.add_circle_outline,
),
label: Text(isSelected ? 'Remove' : 'Add to entry'), label: Text(isSelected ? 'Remove' : 'Add to entry'),
), ),
], ],
@@ -551,17 +567,9 @@ class _TractionPageState extends State<TractionPage> {
value: (loco.trips ?? loco.journeys ?? 0).toString(), value: (loco.trips ?? loco.journeys ?? 0).toString(),
), ),
if (operatorName.isNotEmpty) if (operatorName.isNotEmpty)
_statPill( _statPill(context, label: 'Operator', value: operatorName),
context,
label: 'Operator',
value: operatorName,
),
if (domain.isNotEmpty) if (domain.isNotEmpty)
_statPill( _statPill(context, label: 'Domain', value: domain),
context,
label: 'Domain',
value: domain,
),
], ],
), ),
], ],
@@ -570,8 +578,11 @@ class _TractionPageState extends State<TractionPage> {
); );
} }
Widget _statPill(BuildContext context, Widget _statPill(
{required String label, required String value}) { BuildContext context, {
required String label,
required String value,
}) {
return Container( return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
decoration: BoxDecoration( decoration: BoxDecoration(
@@ -581,16 +592,12 @@ class _TractionPageState extends State<TractionPage> {
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Text( Text('$label: ', style: Theme.of(context).textTheme.labelSmall),
'$label: ',
style: Theme.of(context).textTheme.labelSmall,
),
Text( Text(
value, value,
style: Theme.of(context) style: Theme.of(
.textTheme context,
.labelSmall ).textTheme.labelSmall?.copyWith(fontWeight: FontWeight.w700),
?.copyWith(fontWeight: FontWeight.w700),
), ),
], ],
), ),
@@ -621,10 +628,9 @@ class _TractionPageState extends State<TractionPage> {
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Text(
'${loco.locoClass} ${loco.number}', '${loco.locoClass} ${loco.number}',
style: Theme.of(context) style: Theme.of(context).textTheme.titleLarge?.copyWith(
.textTheme fontWeight: FontWeight.w700,
.titleLarge ),
?.copyWith(fontWeight: FontWeight.w700),
), ),
], ],
), ),
@@ -647,10 +653,11 @@ class _TractionPageState extends State<TractionPage> {
_detailRow('Owner', loco.owner ?? ''), _detailRow('Owner', loco.owner ?? ''),
_detailRow('Livery', loco.livery ?? ''), _detailRow('Livery', loco.livery ?? ''),
_detailRow('Location', loco.location ?? ''), _detailRow('Location', loco.location ?? ''),
_detailRow('Mileage', _formatNumber(loco.mileage ?? 0)),
_detailRow( _detailRow(
'Mileage', _formatNumber(loco.mileage ?? 0)), 'Trips',
_detailRow('Trips', (loco.trips ?? loco.journeys ?? 0).toString(),
(loco.trips ?? loco.journeys ?? 0).toString()), ),
_detailRow('EVN', loco.evn ?? ''), _detailRow('EVN', loco.evn ?? ''),
if (loco.notes != null && loco.notes!.isNotEmpty) if (loco.notes != null && loco.notes!.isNotEmpty)
_detailRow('Notes', loco.notes!), _detailRow('Notes', loco.notes!),
@@ -676,17 +683,13 @@ class _TractionPageState extends State<TractionPage> {
width: 110, width: 110,
child: Text( child: Text(
label, label,
style: Theme.of(context) style: Theme.of(
.textTheme context,
.labelMedium ).textTheme.labelMedium?.copyWith(fontWeight: FontWeight.w600),
?.copyWith(fontWeight: FontWeight.w600),
), ),
), ),
Expanded( Expanded(
child: Text( child: Text(value, style: Theme.of(context).textTheme.bodyMedium),
value,
style: Theme.of(context).textTheme.bodyMedium,
),
), ),
], ],
), ),

View File

@@ -5,11 +5,29 @@ import 'package:flutter/scheduler.dart';
import 'package:mileograph_flutter/objects/objects.dart'; import 'package:mileograph_flutter/objects/objects.dart';
import 'package:mileograph_flutter/services/apiService.dart'; // assumes you've moved HomepageStats + submodels to a separate file import 'package:mileograph_flutter/services/apiService.dart'; // assumes you've moved HomepageStats + submodels to a separate file
class _LegFetchOptions {
final int limit;
final String sortBy;
final int sortDirection;
final String? dateRangeStart;
final String? dateRangeEnd;
const _LegFetchOptions({
this.limit = 100,
this.sortBy = 'date',
this.sortDirection = 0,
this.dateRangeStart,
this.dateRangeEnd,
});
}
class DataService extends ChangeNotifier { class DataService extends ChangeNotifier {
final ApiService api; final ApiService api;
DataService({required this.api}); DataService({required this.api});
_LegFetchOptions _lastLegsFetch = const _LegFetchOptions();
// Homepage Data // Homepage Data
HomepageStats? _homepageStats; HomepageStats? _homepageStats;
HomepageStats? get homepageStats => _homepageStats; HomepageStats? get homepageStats => _homepageStats;
@@ -89,6 +107,15 @@ class DataService extends ChangeNotifier {
bool append = false, bool append = false,
}) async { }) async {
_isLegsLoading = true; _isLegsLoading = true;
if (!append) {
_lastLegsFetch = _LegFetchOptions(
limit: limit,
sortBy: sortBy,
sortDirection: sortDirection,
dateRangeStart: dateRangeStart,
dateRangeEnd: dateRangeEnd,
);
}
final buffer = StringBuffer( final buffer = StringBuffer(
'?sort_direction=$sortDirection&sort_by=$sortBy&offset=$offset&limit=$limit'); '?sort_direction=$sortDirection&sort_by=$sortBy&offset=$offset&limit=$limit');
if (dateRangeStart != null && dateRangeStart.isNotEmpty) { if (dateRangeStart != null && dateRangeStart.isNotEmpty) {
@@ -117,6 +144,16 @@ class DataService extends ChangeNotifier {
} }
} }
Future<void> refreshLegs() {
return fetchLegs(
limit: _lastLegsFetch.limit,
sortBy: _lastLegsFetch.sortBy,
sortDirection: _lastLegsFetch.sortDirection,
dateRangeStart: _lastLegsFetch.dateRangeStart,
dateRangeEnd: _lastLegsFetch.dateRangeEnd,
);
}
Future<void> fetchHadTraction({int offset = 0, int limit = 100}) async { Future<void> fetchHadTraction({int offset = 0, int limit = 100}) async {
await fetchTraction( await fetchTraction(
hadOnly: true, hadOnly: true,