diff --git a/.github/workflows/fastlane.action.yaml b/.github/workflows/fastlane.action.yaml
index 3d0b6cf6..93accf78 100644
--- a/.github/workflows/fastlane.action.yaml
+++ b/.github/workflows/fastlane.action.yaml
@@ -34,7 +34,7 @@ jobs:
- name: 'Generate changelog'
run: ./.github/scripts/generate_changelog.sh
- name: 'Setup Ruby'
- uses: ruby/setup-ruby@v1.146.0
+ uses: ruby/setup-ruby@v1.148.0
with:
ruby-version: '3.0'
bundler-cache: true
diff --git a/.github/workflows/flutter.analyze-test.action.yaml b/.github/workflows/flutter.analyze-test.action.yaml
index f644b952..1832900e 100644
--- a/.github/workflows/flutter.analyze-test.action.yaml
+++ b/.github/workflows/flutter.analyze-test.action.yaml
@@ -25,7 +25,7 @@ jobs:
with:
flutter-version: ${{ inputs.flutter_version }}
- name: 'Flutter analyze'
- run: flutter analyze
+ run: flutter analyze lib
format:
name: 'Format'
runs-on: ubuntu-latest
@@ -38,6 +38,24 @@ jobs:
flutter-version: ${{ inputs.flutter_version }}
- name: 'Flutter format'
run: dart format lib --set-exit-if-changed
+ code-metrics:
+ name: 'Code metrics'
+ runs-on: ubuntu-latest
+ steps:
+ - name: 'Checkout source code'
+ uses: actions/checkout@v3
+ - name: 'Setup flutter action'
+ uses: subosito/flutter-action@v2.10.0
+ with:
+ flutter-version: ${{ inputs.flutter_version }}
+ - name: 'Get dependencies'
+ run: flutter pub get
+ - name: '[Global] Code metrics'
+ run: flutter pub run dart_code_metrics:metrics analyze --fatal-style --fatal-warnings --fatal-performance --reporter=github lib
+ - name: '[Unused files] Code metrics'
+ run: flutter pub run dart_code_metrics:metrics check-unused-files lib
+ - name: '[Unused code] Code metrics'
+ run: flutter pub run dart_code_metrics:metrics check-unused-code lib
test:
name: 'Test'
runs-on: ubuntu-latest
diff --git a/.github/workflows/flutter.build.action.yaml b/.github/workflows/flutter.build.action.yaml
index 7bbda5a6..e1f4be2f 100644
--- a/.github/workflows/flutter.build.action.yaml
+++ b/.github/workflows/flutter.build.action.yaml
@@ -1,99 +1,99 @@
-name: Flutter - Build
-
-on:
- workflow_call:
- inputs:
- flutter_version:
- description: 'The Flutter used (ex: 2.5.1)'
- required: true
- type: string
- android_output:
- description: 'Android build file type output (apk or abb)'
- required: true
- type: string
- secrets:
- passphrase:
- description: 'The passphrase to decrypt the configuration'
- required: true
-
-
-jobs:
- build_android:
- name: 'Android (${{ inputs.android_output }})'
- runs-on: ubuntu-latest
- steps:
- - name: 'Checkout source code'
- uses: actions/checkout@v3.5.2
- with:
- fetch-depth: 0
- - name: 'Decrypt secret configuration'
- run: ./.github/scripts/decrypt_secret.sh
- env:
- PASSPHRASE: ${{ secrets.passphrase }}
- - name: 'Check secret configuration'
- run: ./.github/scripts/check_secrets_decryption.sh
- - name: 'Set up JAVA'
- uses: actions/setup-java@v3
- with:
- distribution: 'zulu'
- java-version: '11.x'
- - name: 'Setup Flutter'
- uses: subosito/flutter-action@v2.10.0
- with:
- flutter-version: ${{ inputs.flutter_version }}
- - name: 'Build Android APK'
- if: ${{ inputs.android_output == 'apk' }}
- # Build APK version of the app
- run: flutter build apk --split-per-abi
- - name: 'Save APK'
- if: ${{ inputs.android_output == 'apk' }}
- uses: actions/upload-artifact@v3
- with:
- name: 'apk-build'
- path: build/app/outputs/apk/release/app-arm64-v8a-release.apk
- - name: 'Generate build number'
- if: ${{ inputs.android_output == 'aab' }}
- # Build App Bundle version of the app
- run: |
- BUILD_NUMBER=$(git rev-list --all --count)
- echo "BUILD_NUMBER=$BUILD_NUMBER" >> $GITHUB_ENV
- echo "This build is tagged as $BUILD_NUMBER on $GITHUB_REF"
- - name: 'Build Android App Bundle'
- if: ${{ inputs.android_output == 'aab' }}
- run: flutter build appbundle --dart-define=FLAVOR=prod --build-number="$BUILD_NUMBER"
- env:
- BUILD_NUMBER: ${{ env.BUILD_NUMBER }}
- - name: 'Save AAB'
- if: ${{ inputs.android_output == 'aab' }}
- uses: actions/upload-artifact@v3
- with:
- name: 'aab-build'
- path: build/app/outputs/bundle/release/app-release.aab
- build_ios:
- name: 'iOS'
- runs-on: ubuntu-latest
- steps:
- - name: 'Checkout source code'
- uses: actions/checkout@v3
- - name: 'Decrypt secret configuration'
- run: ./.github/scripts/decrypt_secret.sh
- env:
- PASSPHRASE: ${{ secrets.passphrase }}
- - name: 'Check secret configuration'
- run: ./.github/scripts/check_secrets_decryption.sh
- - name: '🥺'
- run: echo 'WIP'
- build_web:
- name: 'WEB'
- runs-on: ubuntu-latest
- steps:
- - name: 'Checkout source code'
- uses: actions/checkout@v3
- - name: 'Decrypt secret configuration'
- run: ./.github/scripts/decrypt_secret.sh
- env:
- PASSPHRASE: ${{ secrets.passphrase }}
- - name: 'Check secret configuration'
- run: ./.github/scripts/check_secrets_decryption.sh
- - name: '🥺'
+name: Flutter - Build
+
+on:
+ workflow_call:
+ inputs:
+ flutter_version:
+ description: 'The Flutter used (ex: 2.5.1)'
+ required: true
+ type: string
+ android_output:
+ description: 'Android build file type output (apk or abb)'
+ required: true
+ type: string
+ secrets:
+ passphrase:
+ description: 'The passphrase to decrypt the configuration'
+ required: true
+
+
+jobs:
+ build_android:
+ name: 'Android (${{ inputs.android_output }})'
+ runs-on: ubuntu-latest
+ steps:
+ - name: 'Checkout source code'
+ uses: actions/checkout@v3.5.2
+ with:
+ fetch-depth: 0
+ - name: 'Decrypt secret configuration'
+ run: ./.github/scripts/decrypt_secret.sh
+ env:
+ PASSPHRASE: ${{ secrets.passphrase }}
+ - name: 'Check secret configuration'
+ run: ./.github/scripts/check_secrets_decryption.sh
+ - name: 'Set up JAVA'
+ uses: actions/setup-java@v3
+ with:
+ distribution: 'zulu'
+ java-version: '11.x'
+ - name: 'Setup Flutter'
+ uses: subosito/flutter-action@v2.10.0
+ with:
+ flutter-version: ${{ inputs.flutter_version }}
+ - name: 'Build Android APK'
+ if: ${{ inputs.android_output == 'apk' }}
+ # Build APK version of the app
+ run: flutter build apk --split-per-abi
+ - name: 'Save APK'
+ if: ${{ inputs.android_output == 'apk' }}
+ uses: actions/upload-artifact@v3
+ with:
+ name: 'apk-build'
+ path: build/app/outputs/apk/release/app-arm64-v8a-release.apk
+ - name: 'Generate build number'
+ if: ${{ inputs.android_output == 'aab' }}
+ # Build App Bundle version of the app
+ run: |
+ BUILD_NUMBER=$(git rev-list --all --count)
+ echo "BUILD_NUMBER=$BUILD_NUMBER" >> $GITHUB_ENV
+ echo "This build is tagged as $BUILD_NUMBER on $GITHUB_REF"
+ - name: 'Build Android App Bundle'
+ if: ${{ inputs.android_output == 'aab' }}
+ run: flutter build appbundle --build-number="$BUILD_NUMBER"
+ env:
+ BUILD_NUMBER: ${{ env.BUILD_NUMBER }}
+ - name: 'Save AAB'
+ if: ${{ inputs.android_output == 'aab' }}
+ uses: actions/upload-artifact@v3
+ with:
+ name: 'aab-build'
+ path: build/app/outputs/bundle/release/app-release.aab
+ build_ios:
+ name: 'iOS'
+ runs-on: ubuntu-latest
+ steps:
+ - name: 'Checkout source code'
+ uses: actions/checkout@v3
+ - name: 'Decrypt secret configuration'
+ run: ./.github/scripts/decrypt_secret.sh
+ env:
+ PASSPHRASE: ${{ secrets.passphrase }}
+ - name: 'Check secret configuration'
+ run: ./.github/scripts/check_secrets_decryption.sh
+ - name: '🥺'
+ run: echo 'WIP'
+ build_web:
+ name: 'WEB'
+ runs-on: ubuntu-latest
+ steps:
+ - name: 'Checkout source code'
+ uses: actions/checkout@v3
+ - name: 'Decrypt secret configuration'
+ run: ./.github/scripts/decrypt_secret.sh
+ env:
+ PASSPHRASE: ${{ secrets.passphrase }}
+ - name: 'Check secret configuration'
+ run: ./.github/scripts/check_secrets_decryption.sh
+ - name: '🥺'
run: echo 'WIP'
\ No newline at end of file
diff --git a/CHANGELOG_en.md b/CHANGELOG_en.md
index e280649e..c22a47a6 100644
--- a/CHANGELOG_en.md
+++ b/CHANGELOG_en.md
@@ -1,3 +1,20 @@
+# **v1.6.1** :
+
+- *Fix*:
+ - The SnackBar warning of activation of notifications on slots is now correctly in place
+- *Interface*:
+ - Adding a padding to display the last item in the closures list
+ - Only one ad is now displayed
+***
+# **v1.6.0** :
+
+- *Features*:
+ - It is now possible to add two favorite slots to be notified only of impacting events
+- *Interface*:
+ - Revised list of closures to make it easier to read.
+ - Highlighting impacting events with an orange border.
+ - Adding a message if notifications are disabled
+***
# **v1.4.0** :
- *Features*:
diff --git a/CHANGELOG_es.md b/CHANGELOG_es.md
index a1c663b6..fe6d1750 100644
--- a/CHANGELOG_es.md
+++ b/CHANGELOG_es.md
@@ -1,3 +1,20 @@
+# **v1.6.1** :
+
+- *Fix*:
+ - El snackbar de advertencia de la activación de las notificaciones en las ranuras está ahora correctamente en su lugar
+- *Interfaz*:
+ - Añadir un relleno para mostrar el último elemento de la lista de cierres
+ - Ahora solo se muestra un anuncio
+***
+# **v1.6.0** :
+
+- *Funcionalidades*:
+ - Ahora es posible añadir dos franjas horarias favoritas para ser notificado solo de los eventos impactantes
+- *Interfaz*:
+ - Reordenar la lista de cierres para que sea más fácil de leer.
+ - Resalta eventos impactantes con un borde naranja.
+ - Añadido un mensaje si las notificaciones están desactivadas
+***
# **v1.4.0** :
- *Características*:
diff --git a/CHANGELOG_fr.md b/CHANGELOG_fr.md
index f07c095f..2fd68345 100644
--- a/CHANGELOG_fr.md
+++ b/CHANGELOG_fr.md
@@ -1,3 +1,20 @@
+# **v1.6.1** :
+
+- *Fix*:
+ - La SnackBar d'avertissement de l'activation des notifications sur les créneaux est maintenant correctement en place
+- *Interface*:
+ - Ajout d'un padding pour afficher le dernier élément de la liste des fermetures
+ - Seulement une seule publicité est maintenant affichée
+***
+# **v1.6.0** :
+
+- *Fonctionnalités*:
+ - Il est maintenant possible d'ajouter deux créneaux favoris pour n'être averti que des évènements impactant
+- *Interface*:
+ - Remaniement de la liste des fermetures afin de la rendre plus facile à lire.
+ - Mise en valeur d'événements impactants grâce à une bordure orange.
+ - Ajout d'un message si les notifications sont désactivées
+***
# **v1.4.0** :
- *Fonctionnalités*:
diff --git a/analysis_options.yaml b/analysis_options.yaml
index bac0015c..6847264e 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -3,7 +3,34 @@ include: package:flutter_lints/flutter.yaml
linter:
rules:
prefer_single_quotes: true
- use_build_context_synchronously: false
-# Additional information about this file can be found at
-# https://dart.dev/guides/language/analysis-options
+analyzer:
+ plugins:
+ - dart_code_metrics
+
+dart_code_metrics:
+ metrics:
+ cyclomatic-complexity: 20
+ number-of-parameters: 5
+ maximum-nesting-level: 5
+ metrics-exclude:
+ - test/**
+ rules:
+ - avoid-dynamic
+ - avoid-passing-async-when-sync-expected
+ - avoid-redundant-async
+ - avoid-unnecessary-type-assertions
+ - avoid-unnecessary-type-casts
+ - avoid-unrelated-type-assertions
+ - avoid-nested-conditional-expressions:
+ acceptable-level: 2
+ - newline-before-return
+ - no-boolean-literal-compare
+ - no-empty-block
+ - prefer-trailing-comma
+ - prefer-conditional-expressions:
+ ignore-nested: true
+ - no-equal-then-else
+ - prefer-moving-to-variable:
+ allowed-duplicated-chains: 3
+ - prefer-match-file-name
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index c71fca2b..d060281a 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -3,6 +3,7 @@
+
diff --git a/android/build.gradle b/android/build.gradle
index 6b10a38f..13b3050e 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -1,5 +1,5 @@
buildscript {
- ext.kotlin_version = '1.8.20'
+ ext.kotlin_version = '1.8.21'
repositories {
google()
mavenCentral()
diff --git a/lib/app_theme.dart b/lib/app_theme.dart
index e9d67c61..48c61cc9 100644
--- a/lib/app_theme.dart
+++ b/lib/app_theme.dart
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
-class AppThemes {
+class AppTheme {
static final lightTheme = ThemeData.light(
useMaterial3: true,
).copyWith(
diff --git a/lib/bloc/chaban_bridge_forecast/chaban_bridge_forecast_bloc.dart b/lib/bloc/chaban_bridge_forecast/chaban_bridge_forecast_bloc.dart
deleted file mode 100644
index 4570e5ad..00000000
--- a/lib/bloc/chaban_bridge_forecast/chaban_bridge_forecast_bloc.dart
+++ /dev/null
@@ -1,166 +0,0 @@
-// ignore_for_file: invalid_use_of_visible_for_testing_member
-
-import 'dart:async';
-import 'dart:convert';
-
-import 'package:chabo/bloc/chabo_event.dart';
-import 'package:chabo/const.dart';
-import 'package:chabo/models/abstract_chaban_bridge_forecast.dart';
-import 'package:chabo/models/chaban_bridge_boat_forecast.dart';
-import 'package:chabo/models/chaban_bridge_maintenance_forecast.dart';
-import 'package:equatable/equatable.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:http/http.dart' as http;
-
-part 'chaban_bridge_forecast_event.dart';
-part 'chaban_bridge_forecast_state.dart';
-
-class ChabanBridgeForecastBloc
- extends Bloc {
- final http.Client httpClient;
-
- ChabanBridgeForecastBloc({required this.httpClient})
- : super(const ChabanBridgeForecastState()) {
- Timer.periodic(const Duration(seconds: 1), _onRefreshCurrentStatus);
- on(
- _onChabanBridgeForecastFetched,
- );
- }
-
- void _onRefreshCurrentStatus(Timer timer) {
- try {
- if (state.status == ChabanBridgeForecastStatus.success) {
- final currentStatus = _getCurrentStatus(state.chabanBridgeForecasts);
- final previousStatus =
- _getPreviousStatus(state.chabanBridgeForecasts, currentStatus);
- if (currentStatus != state.currentChabanBridgeForecast &&
- currentStatus != previousStatus) {
- emit(
- state.copyWith(
- currentChabanBridgeForecast: currentStatus,
- previousChabanBridgeForecast: previousStatus,
- ),
- );
- }
- }
- } catch (_) {
- emit(state.copyWith(
- status: ChabanBridgeForecastStatus.failure, message: _.toString()));
- }
- }
-
- Future> _fetchChabanBridgeForecasts(
- int offset) async {
- var uri = Uri.https(
- 'opendata.bordeaux-metropole.fr',
- '/api/records/1.0/search',
- {
- 'dataset': 'previsions_pont_chaban',
- 'rows': '${Const.chabanBridgeForecastLimit}',
- 'sort': '-date_passage',
- 'start': '$offset',
- 'timezone': 'Europe/Paris'
- },
- );
- final response = await httpClient.get(uri);
- if (response.statusCode == 200) {
- final body = json.decode(response.body);
- return (body['records'] as List).map((dynamic json) {
- if (json['fields']['bateau'].toString().toLowerCase() ==
- 'maintenance') {
- final maintenanceForecast =
- ChabanBridgeMaintenanceForecast.fromJSON(json);
- return maintenanceForecast;
- }
- final boatForecast = ChabanBridgeBoatForecast.fromJSON(json);
- return boatForecast;
- }).toList()
- ..sort((a, b) =>
- a.circulationClosingDate.compareTo(b.circulationClosingDate));
- }
- return [];
- }
-
- AbstractChabanBridgeForecast _getCurrentStatus(
- List chabanBridgeForecast) {
- int middle = chabanBridgeForecast.length ~/ 2;
- if ((chabanBridgeForecast[middle]
- .circulationClosingDate
- .isBefore(DateTime.now()) &&
- chabanBridgeForecast[middle]
- .circulationReOpeningDate
- .isAfter(DateTime.now()))) {
- return chabanBridgeForecast[middle];
- }
- if (chabanBridgeForecast.length == 2) {
- return chabanBridgeForecast[1]
- .circulationClosingDate
- .isAfter(DateTime.now()) &&
- chabanBridgeForecast[0]
- .circulationReOpeningDate
- .isBefore(DateTime.now())
- ? chabanBridgeForecast[1]
- : chabanBridgeForecast[0];
- } else if (chabanBridgeForecast[middle]
- .circulationClosingDate
- .isAfter(DateTime.now())) {
- return _getCurrentStatus(chabanBridgeForecast.sublist(0, middle + 1));
- } else {
- return _getCurrentStatus(chabanBridgeForecast.sublist(middle));
- }
- }
-
- AbstractChabanBridgeForecast? _getPreviousStatus(
- List chabanBridgeForecasts,
- AbstractChabanBridgeForecast currentStatus) {
- if (chabanBridgeForecasts.indexOf(currentStatus) == 0) {
- return null;
- } else {
- return chabanBridgeForecasts
- .elementAt(chabanBridgeForecasts.indexOf(currentStatus) - 1);
- }
- }
-
- Future _onChabanBridgeForecastFetched(ChabanBridgeForecastFetched event,
- Emitter emit) async {
- if (state.hasReachedMax) return;
- try {
- if (state.status == ChabanBridgeForecastStatus.initial) {
- final chabanBridgeForecasts =
- await _fetchChabanBridgeForecasts(state.offset);
- final currentStatus = _getCurrentStatus(chabanBridgeForecasts);
- emit(state.copyWith(
- status: ChabanBridgeForecastStatus.success,
- chabanBridgeForecasts: chabanBridgeForecasts,
- currentChabanBridgeForecast: currentStatus,
- previousChabanBridgeForecast:
- _getPreviousStatus(chabanBridgeForecasts, currentStatus),
- hasReachedMax: false,
- offset: state.offset + Const.chabanBridgeForecastLimit));
- }
- final chabanBridgeForecasts =
- await _fetchChabanBridgeForecasts(state.chabanBridgeForecasts.length);
- emit(
- chabanBridgeForecasts.isEmpty
- ? state.copyWith(hasReachedMax: true)
- : state.copyWith(
- currentChabanBridgeForecast:
- state.currentChabanBridgeForecast ??
- _getCurrentStatus(chabanBridgeForecasts),
- previousChabanBridgeForecast:
- state.previousChabanBridgeForecast ??
- _getPreviousStatus(chabanBridgeForecasts,
- _getCurrentStatus(chabanBridgeForecasts)),
- status: ChabanBridgeForecastStatus.success,
- chabanBridgeForecasts: List.of(state.chabanBridgeForecasts)
- ..addAll(chabanBridgeForecasts),
- hasReachedMax: false,
- offset: state.offset + Const.chabanBridgeForecastLimit,
- ),
- );
- } catch (_) {
- emit(state.copyWith(
- status: ChabanBridgeForecastStatus.failure, message: _.toString()));
- }
- }
-}
diff --git a/lib/bloc/chaban_bridge_forecast/chaban_bridge_forecast_event.dart b/lib/bloc/chaban_bridge_forecast/chaban_bridge_forecast_event.dart
deleted file mode 100644
index 020cc3d3..00000000
--- a/lib/bloc/chaban_bridge_forecast/chaban_bridge_forecast_event.dart
+++ /dev/null
@@ -1,8 +0,0 @@
-part of 'chaban_bridge_forecast_bloc.dart';
-
-abstract class ChabanBridgeForecastEvent extends ChaboEvent {}
-
-class ChabanBridgeForecastFetched extends ChabanBridgeForecastEvent {}
-
-class ChabanBridgeForecastRefreshCurrentStatus
- extends ChabanBridgeForecastEvent {}
diff --git a/lib/bloc/chaban_bridge_forecast/chaban_bridge_forecast_state.dart b/lib/bloc/chaban_bridge_forecast/chaban_bridge_forecast_state.dart
deleted file mode 100644
index 67feb542..00000000
--- a/lib/bloc/chaban_bridge_forecast/chaban_bridge_forecast_state.dart
+++ /dev/null
@@ -1,59 +0,0 @@
-part of 'chaban_bridge_forecast_bloc.dart';
-
-enum ChabanBridgeForecastStatus { initial, success, failure }
-
-class ChabanBridgeForecastState extends Equatable {
- final ChabanBridgeForecastStatus status;
- final List chabanBridgeForecasts;
- final AbstractChabanBridgeForecast? currentChabanBridgeForecast;
- final AbstractChabanBridgeForecast? previousChabanBridgeForecast;
- final bool hasReachedMax;
- final int offset;
- final String message;
-
- const ChabanBridgeForecastState(
- {this.status = ChabanBridgeForecastStatus.initial,
- this.chabanBridgeForecasts = const [],
- this.currentChabanBridgeForecast,
- this.previousChabanBridgeForecast,
- this.hasReachedMax = false,
- this.offset = 0,
- this.message = 'OK'});
-
- ChabanBridgeForecastState copyWith(
- {ChabanBridgeForecastStatus? status,
- List? chabanBridgeForecasts,
- AbstractChabanBridgeForecast? currentChabanBridgeForecast,
- AbstractChabanBridgeForecast? previousChabanBridgeForecast,
- bool? hasReachedMax,
- int? offset,
- String? message}) {
- return ChabanBridgeForecastState(
- status: status ?? this.status,
- chabanBridgeForecasts:
- chabanBridgeForecasts ?? this.chabanBridgeForecasts,
- currentChabanBridgeForecast:
- currentChabanBridgeForecast ?? this.currentChabanBridgeForecast,
- previousChabanBridgeForecast:
- previousChabanBridgeForecast ?? this.previousChabanBridgeForecast,
- hasReachedMax: hasReachedMax ?? this.hasReachedMax,
- offset: offset ?? this.offset,
- message: message ?? this.message);
- }
-
- @override
- String toString() {
- return 'ChabanBridgeForecastState{status: $status, chabanBridgeForecasts: $chabanBridgeForecasts, currentChabanBridgeForecast: $currentChabanBridgeForecast, hasReachedMax: $hasReachedMax, offset: $offset, message: $message}';
- }
-
- @override
- List