Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(no-forecasts): add the edge case when all forecasts has passed #121

Merged
merged 1 commit into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions lib/bloc/forecast/forecast_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class ForecastBloc extends Bloc<ForecastEvent, ForecastState> {
return [];
}

AbstractForecast _getCurrentStatus(
AbstractForecast? _getCurrentStatus(
List<AbstractForecast> forecast,
) {
int middle = forecast.length ~/ 2;
Expand All @@ -94,10 +94,16 @@ class ForecastBloc extends Bloc<ForecastEvent, ForecastState> {
return forecast[middle];
}
if (forecast.length == 2) {
return forecast[1].circulationClosingDate.isAfter(DateTime.now()) &&
forecast[0].circulationReOpeningDate.isBefore(DateTime.now())
? forecast[1]
: forecast[0];
if (forecast[1].circulationClosingDate.isAfter(DateTime.now()) &&
forecast[0].circulationReOpeningDate.isBefore(DateTime.now())) {
return forecast[1];
} else {
if (!forecast[0].circulationReOpeningDate.isBefore(DateTime.now())) {
return forecast[0];
} else {
return null;
}
}
} else if (forecast[middle]
.circulationClosingDate
.isAfter(DateTime.now())) {
Expand All @@ -109,8 +115,11 @@ class ForecastBloc extends Bloc<ForecastEvent, ForecastState> {

AbstractForecast? _getPreviousStatus(
List<AbstractForecast> forecasts,
AbstractForecast currentStatus,
AbstractForecast? currentStatus,
) {
if (currentStatus == null) {
return null;
}
return forecasts.indexOf(currentStatus) == 0
? null
: forecasts.elementAt(forecasts.indexOf(currentStatus) - 1);
Expand All @@ -125,11 +134,13 @@ class ForecastBloc extends Bloc<ForecastEvent, ForecastState> {
if (state.status == ForecastStatus.initial) {
final forecasts = await _fetchForecasts(state.offset);
final currentStatus = _getCurrentStatus(forecasts);
final noMoreForecasts = currentStatus == null;
emit(state.copyWith(
status: ForecastStatus.success,
forecasts: forecasts,
currentForecast: currentStatus,
previousForecast: _getPreviousStatus(forecasts, currentStatus),
noMoreForecasts: noMoreForecasts,
hasReachedMax: false,
offset: state.offset + Const.forecastLimit,
));
Expand Down
4 changes: 4 additions & 0 deletions lib/bloc/forecast/forecast_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class ForecastState extends Equatable {
final List<AbstractForecast> forecasts;
final AbstractForecast? currentForecast;
final AbstractForecast? previousForecast;
final bool noMoreForecasts;
final bool hasReachedMax;
final int offset;
final String message;
Expand All @@ -17,13 +18,15 @@ class ForecastState extends Equatable {
this.hasReachedMax = false,
this.offset = 0,
this.message = 'OK',
this.noMoreForecasts = false,
});

ForecastState copyWith({
ForecastStatus? status,
List<AbstractForecast>? forecasts,
AbstractForecast? currentForecast,
AbstractForecast? previousForecast,
bool? noMoreForecasts,
bool? hasReachedMax,
int? offset,
String? message,
Expand All @@ -33,6 +36,7 @@ class ForecastState extends Equatable {
forecasts: forecasts ?? this.forecasts,
currentForecast: currentForecast ?? this.currentForecast,
previousForecast: previousForecast ?? this.previousForecast,
noMoreForecasts: noMoreForecasts ?? this.noMoreForecasts,
hasReachedMax: hasReachedMax ?? this.hasReachedMax,
offset: offset ?? this.offset,
message: message ?? this.message,
Expand Down
8 changes: 4 additions & 4 deletions lib/bloc/status/status_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ class StatusBloc extends Bloc<StatusEvent, StatusState> {
return Theme.of(context).colorScheme.error;
}
} else {
return state.backgroundColor;
return Theme.of(context).colorScheme.okColor;
}
}

Expand All @@ -129,7 +129,7 @@ class StatusBloc extends Bloc<StatusEvent, StatusState> {
? colorScheme.background
: colorScheme.onError;
} else {
return state.foregroundColor;
return Theme.of(context).colorScheme.background;
}
}

Expand All @@ -140,7 +140,7 @@ class StatusBloc extends Bloc<StatusEvent, StatusState> {
? '${AppLocalizations.of(context)!.scheduledToOpen.capitalize()} '
: '${AppLocalizations.of(context)!.nextClosingScheduled.capitalize()} ';
} else {
return 'NO_TIME';
return '';
}
}

Expand All @@ -162,7 +162,7 @@ class StatusBloc extends Bloc<StatusEvent, StatusState> {
: AppLocalizations.of(context)!.willSoonClose.capitalize();
} else {
return state.statusWidgetDimension == StatusWidgetDimension.large
? '${_getGreetings(context)}, ${AppLocalizations.of(context)!.theChabanBridgeIsClosed}'
? '${_getGreetings(context)}, ${AppLocalizations.of(context)!.theChabanBridgeIsOpen}'
: AppLocalizations.of(context)!.isClosed.capitalize();
}
}
Expand Down
4 changes: 2 additions & 2 deletions lib/bloc/status/status_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class StatusStateInitial extends StatusState {
durationUntilNextEvent: Duration.zero,
durationBetweenPreviousAndNextEvent: null,
durationForCloseClosing: Const.notificationDurationValueDefaultValue,
statusLifecycle: StatusLifecycle.empty,
statusLifecycle: StatusLifecycle.loading,
completionPercentage: 0,
mainMessageStatus: '',
timeMessagePrefix: '',
Expand All @@ -99,6 +99,6 @@ class StatusStateInitial extends StatusState {
);
}

enum StatusLifecycle { empty, populated }
enum StatusLifecycle { empty, populated, loading }

enum StatusWidgetDimension { small, large }
4 changes: 3 additions & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -223,5 +223,7 @@
"wineFestivalSailBoats": "Wine Festival Sailboats",
"externalLinks": "External links",
"rate": "Rate",
"timeFormatSubTitle": "Time format"
"timeFormatSubTitle": "Time format",
"noMoreForecastsTitle": "No upcoming events",
"noMoreForecastsMessage": "Bordeaux Métropole has not communicated any closure of the Chaban-Delmas Bridge for the coming weeks.\nStay informed of upcoming closures by returning here regularly"
}
4 changes: 3 additions & 1 deletion lib/l10n/app_es.arb
Original file line number Diff line number Diff line change
Expand Up @@ -223,5 +223,7 @@
"wineFestivalSailBoats": "Veleros de la Fiesta del Vino",
"externalLinks": "Enlaces externos",
"rate": "Califica",
"timeFormatSubTitle": "Formato de hora"
"timeFormatSubTitle": "Formato de hora",
"noMoreForecastsTitle": "Ningún acontecimiento por venir",
"noMoreForecastsMessage": "Bordeaux Métropole no ha comunicado ningún cierre del Puente Chaban-Delmas para las próximas semanas.\nManténgase informado de los próximos cierres volviendo aquí regularmente"
}
4 changes: 3 additions & 1 deletion lib/l10n/app_fr.arb
Original file line number Diff line number Diff line change
Expand Up @@ -223,5 +223,7 @@
"wineFestivalSailBoats": "Voiliers de la fête du vin",
"externalLinks": "Liens externes",
"rate": "Noter",
"timeFormatSubTitle": "Format d'affichage des heures"
"timeFormatSubTitle": "Format d'affichage des heures",
"noMoreForecastsTitle": "Aucun évènement à venir",
"noMoreForecastsMessage": "Bordeaux Métropole n'a communiqué aucune fermeture du Pont Chaban-Delmas pour les prochaines semaines.\nTenez-vous informer des prochaines fermetures en revenant ici régulièrement"
}
4 changes: 4 additions & 0 deletions lib/models/boat.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
isWineFestivalSailBoats = name == Const.specialWineFestivalBoatsEvent;
}

factory Boat.fake() {
return Boat(name: 'fake', isLeaving: true);

Check warning on line 22 in lib/models/boat.dart

View check run for this annotation

Codecov / codecov/patch

lib/models/boat.dart#L21-L22

Added lines #L21 - L22 were not covered by tests
}

void _launchURL(String url) async {
await launchUrlString(url, mode: LaunchMode.externalApplication);
}
Expand Down
9 changes: 9 additions & 0 deletions lib/models/boat_forecast.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@
closingReason: ForecastClosingReason.boat,
);

factory BoatForecast.fake() {
return BoatForecast(

Check warning on line 33 in lib/models/boat_forecast.dart

View check run for this annotation

Codecov / codecov/patch

lib/models/boat_forecast.dart#L32-L33

Added lines #L32 - L33 were not covered by tests
totalClosing: true,
circulationClosingDate: DateTime.now(),
circulationReOpeningDate: DateTime.now(),
boats: [Boat.fake()],

Check warning on line 37 in lib/models/boat_forecast.dart

View check run for this annotation

Codecov / codecov/patch

lib/models/boat_forecast.dart#L35-L37

Added lines #L35 - L37 were not covered by tests
closingType: ForecastClosingType.complete);
}

factory BoatForecast.fromJSON(Map<String, dynamic> json) {
var apiTimezone = AbstractForecast.getApiTimeZone(json['record_timestamp']);
var closingDate = AbstractForecast.parseFieldDate(
Expand Down
8 changes: 8 additions & 0 deletions lib/models/maintenance_forecast.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@
closingReason: ForecastClosingReason.maintenance,
);

factory MaintenanceForecast.fake() {
return MaintenanceForecast(

Check warning on line 24 in lib/models/maintenance_forecast.dart

View check run for this annotation

Codecov / codecov/patch

lib/models/maintenance_forecast.dart#L23-L24

Added lines #L23 - L24 were not covered by tests
totalClosing: true,
circulationClosingDate: DateTime.now(),
circulationReOpeningDate: DateTime.now(),

Check warning on line 27 in lib/models/maintenance_forecast.dart

View check run for this annotation

Codecov / codecov/patch

lib/models/maintenance_forecast.dart#L26-L27

Added lines #L26 - L27 were not covered by tests
closingType: ForecastClosingType.complete);
}

factory MaintenanceForecast.fromJSON(Map<String, dynamic> json) {
var apiTimezone = AbstractForecast.getApiTimeZone(json['record_timestamp']);
var closingDate = AbstractForecast.parseFieldDate(
Expand Down
5 changes: 5 additions & 0 deletions lib/widgets/forecast/forecast_list_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:chabo_app/bloc/scroll_status/scroll_status_bloc.dart';
import 'package:chabo_app/bloc/time_slots/time_slots_bloc.dart';
import 'package:chabo_app/models/abstract_forecast.dart';
import 'package:chabo_app/widgets/forecast/forecast_widget/forecast_widget.dart';
import 'package:chabo_app/widgets/forecast/no_more_forecasts_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart';
Expand All @@ -24,6 +25,10 @@ class _ForecastListWidgetState extends State<ForecastListWidget> {
return SliverToBoxAdapter(
child: BlocBuilder<TimeSlotsBloc, TimeSlotsState>(
builder: (context, timeSlotState) {
// Check if the last forecast is before today
if (forecastState.noMoreForecasts) {
return const NoMoreForecastsWidget();
}
return ListView.separated(
shrinkWrap: true,
cacheExtent: 5000,
Expand Down
51 changes: 51 additions & 0 deletions lib/widgets/forecast/no_more_forecasts_widget.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import 'package:chabo_app/models/boat_forecast.dart';
import 'package:chabo_app/models/maintenance_forecast.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

class NoMoreForecastsWidget extends StatelessWidget {
const NoMoreForecastsWidget({super.key});

@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 100.0),
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Center(
child: Text(
AppLocalizations.of(context)!.noMoreForecastsTitle,
style: Theme.of(context)
.textTheme
.titleLarge!
.copyWith(fontWeight: FontWeight.bold),
),
),
Padding(
padding: const EdgeInsets.all(15.0),
child: Center(
child: Text(
AppLocalizations.of(context)!.noMoreForecastsMessage,
textAlign: TextAlign.center,
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
BoatForecast.fake().getIconWidget(context, false, 40, true),
const Text(' • '),
MaintenanceForecast.fake()
.getIconWidget(context, false, 40, true),
],
),
)
],
),
);
}
}
7 changes: 4 additions & 3 deletions lib/widgets/forecast/status_widget/layout_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ class _LayoutWidget extends StatelessWidget {
statusState: statusState,
),
),
_ProgressIndicatorWidget(
statusState: statusState,
),
if (statusState.currentForecast != null)
_ProgressIndicatorWidget(
statusState: statusState,
),
const SizedBox(
height: 10,
),
Expand Down
2 changes: 1 addition & 1 deletion lib/widgets/forecast/status_widget/status_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class StatusWidgetState extends CustomWidgetState<StatusWidget> {
child: child,
);
},
child: state.statusLifecycle == StatusLifecycle.empty
child: state.statusLifecycle == StatusLifecycle.loading
? CustomCircularProgressIndicator(
message: AppLocalizations.of(context)!.statusLoadMessage,
)
Expand Down
Loading