From e584f19b47c1c3ef3e14ad37d6b5987f63d3593b Mon Sep 17 00:00:00 2001 From: Serdar Coskun Date: Mon, 1 Jun 2026 23:39:32 +0300 Subject: [PATCH 1/2] Improve pub.dev score to 160/160 - LICENSE: replace one-line prose with standard OSI MIT text so the license is recognized (+10). - Apply `dart format .` across the package and example (+10); add a formatting gate to CI. - pubspec: richer description, plus repository / issue_tracker / documentation / topics metadata. - Bump to 2.3.1; CHANGELOG updated. No API changes. pana now reports 160/160; analyze (fatal-infos) and tests green. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/ci.yml | 3 + CHANGELOG.md | 5 + LICENSE | 22 +- example/lib/api/api_models.dart | 2 +- example/lib/api/api_registry.dart | 396 +++++++++++------- example/lib/api/result_format.dart | 3 +- example/lib/examples/authentication.dart | 15 +- example/lib/main.dart | 81 ++-- example/lib/screens/call_screen.dart | 50 ++- example/test/widget_test.dart | 5 +- lib/src/common/local_storage.dart | 10 +- lib/src/common/session_manager.dart | 39 +- lib/src/data/repository/client.dart | 79 ++-- .../repository/repository_activity_impl.dart | 138 +++--- .../repository/repository_athlete_impl.dart | 49 ++- .../repository_authentication_impl.dart | 164 +++++--- .../data/repository/repository_club_impl.dart | 102 +++-- .../data/repository/repository_gear_impl.dart | 6 +- .../repository/repository_route_impl.dart | 28 +- .../repository_running_race_impl.dart | 26 +- .../repository_segment_efforts_impl.dart | 41 +- .../repository/repository_segment_impl.dart | 71 ++-- .../repository/repository_stream_impl.dart | 78 ++-- .../repository/repository_upload_impl.dart | 16 +- .../model/model_activity_request_create.dart | 35 +- .../model/model_activity_request_update.dart | 16 +- .../domain/model/model_activity_stats.dart | 13 +- .../model/model_activity_type_enum.dart | 7 +- lib/src/domain/model/model_activity_zone.dart | 17 +- .../model/model_authentication_response.dart | 17 +- .../model/model_authentication_scopes.dart | 7 +- lib/src/domain/model/model_club.dart | 51 +-- lib/src/domain/model/model_comment.dart | 19 +- .../domain/model/model_detailed_activity.dart | 256 +++++------ .../domain/model/model_detailed_athlete.dart | 71 ++-- .../domain/model/model_detailed_segment.dart | 64 +-- lib/src/domain/model/model_fault.dart | 11 +- lib/src/domain/model/model_gear.dart | 19 +- lib/src/domain/model/model_lap.dart | 45 +- lib/src/domain/model/model_route.dart | 54 +-- lib/src/domain/model/model_running_race.dart | 27 +- .../model/model_segment_leaderboard.dart | 23 +- .../model_segment_leaderboard_request.dart | 54 +-- .../domain/model/model_segments_explore.dart | 27 +- lib/src/domain/model/model_stream_set.dart | 19 +- .../domain/model/model_summary_activity.dart | 113 ++--- .../domain/model/model_summary_segment.dart | 62 +-- lib/src/domain/model/model_upload.dart | 15 +- .../domain/model/model_upload_request.dart | 17 +- .../domain/repository/repository_athlete.dart | 8 +- .../domain/repository/repository_gear.dart | 6 +- .../domain/repository/repository_stream.dart | 16 +- pubspec.yaml | 15 +- test/model_serialization_test.dart | 9 +- test/spec_example_roundtrip_test.dart | 13 +- 55 files changed, 1441 insertions(+), 1114 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b17e34..4ab150e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,6 +30,9 @@ jobs: exit 1 fi + - name: Verify formatting + run: dart format --output=none --set-exit-if-changed lib test + - name: Analyze run: dart analyze --fatal-infos --fatal-warnings diff --git a/CHANGELOG.md b/CHANGELOG.md index db6605a..941aeaa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## [2.3.1] +- Recognized OSI MIT `LICENSE`, `dart format` applied across the package, and + added `repository` / `issue_tracker` / `documentation` / `topics` metadata + (improves pub.dev score). No API changes. + ## [2.3.0] Backwards compatible (additions + deprecations only). diff --git a/LICENSE b/LICENSE index 3007fe5..5b2c3b6 100644 --- a/LICENSE +++ b/LICENSE @@ -1 +1,21 @@ -strava-flutter is provided under a MIT License. Copyright (c) 2019 Patrick FINKELSTEIN \ No newline at end of file +MIT License + +Copyright (c) 2019 Patrick FINKELSTEIN and the strava_flutter contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/example/lib/api/api_models.dart b/example/lib/api/api_models.dart index 73067ea..c889c05 100644 --- a/example/lib/api/api_models.dart +++ b/example/lib/api/api_models.dart @@ -39,7 +39,7 @@ class ApiCall { final bool isWrite; final List params; final Future Function(StravaClient client, Map args) - run; + run; const ApiCall({ required this.group, diff --git a/example/lib/api/api_registry.dart b/example/lib/api/api_registry.dart index 023e2ac..6a8dbc3 100644 --- a/example/lib/api/api_registry.dart +++ b/example/lib/api/api_registry.dart @@ -3,9 +3,17 @@ import 'package:strava_client/strava_client.dart'; import 'api_models.dart'; const _page = ApiParam( - key: "page", label: "Page", type: ParamType.int, defaultValue: 1); + key: "page", + label: "Page", + type: ParamType.int, + defaultValue: 1, +); const _perPage = ApiParam( - key: "perPage", label: "Per page", type: ParamType.int, defaultValue: 30); + key: "perPage", + label: "Per page", + type: ParamType.int, + defaultValue: 30, +); ApiParam _id(String key, String label) => ApiParam(key: key, label: label, type: ParamType.int); @@ -56,23 +64,26 @@ final List kApiCalls = [ description: "Your activities within a date window.", params: [ ApiParam( - key: "before", - label: "Before (ISO date)", - type: ParamType.dateTime, - defaultValue: DateTime.now()), + key: "before", + label: "Before (ISO date)", + type: ParamType.dateTime, + defaultValue: DateTime.now(), + ), ApiParam( - key: "after", - label: "After (ISO date)", - type: ParamType.dateTime, - defaultValue: DateTime.now().subtract(const Duration(days: 30))), + key: "after", + label: "After (ISO date)", + type: ParamType.dateTime, + defaultValue: DateTime.now().subtract(const Duration(days: 30)), + ), _page, _perPage, ], run: (c, a) => c.activities.listLoggedInAthleteActivities( - a["before"] as DateTime, - a["after"] as DateTime, - a["page"] as int, - a["perPage"] as int), + a["before"] as DateTime, + a["after"] as DateTime, + a["page"] as int, + a["perPage"] as int, + ), ), ApiCall( group: "Activity", @@ -111,37 +122,44 @@ final List kApiCalls = [ ApiParam(key: "name", label: "Name", type: ParamType.string), _activityTypeParam, ApiParam( - key: "startDateLocal", - label: "Start date local (ISO)", - type: ParamType.dateTime, - defaultValue: DateTime.now()), + key: "startDateLocal", + label: "Start date local (ISO)", + type: ParamType.dateTime, + defaultValue: DateTime.now(), + ), ApiParam( - key: "elapsedTime", - label: "Elapsed time (s)", - type: ParamType.int, - defaultValue: 3600), + key: "elapsedTime", + label: "Elapsed time (s)", + type: ParamType.int, + defaultValue: 3600, + ), ApiParam( - key: "description", - label: "Description", - type: ParamType.string, - defaultValue: ""), + key: "description", + label: "Description", + type: ParamType.string, + defaultValue: "", + ), ApiParam( - key: "distance", - label: "Distance (m)", - type: ParamType.double, - defaultValue: 0.0), + key: "distance", + label: "Distance (m)", + type: ParamType.double, + defaultValue: 0.0, + ), ApiParam( - key: "trainer", - label: "Trainer", - type: ParamType.bool, - defaultValue: false), + key: "trainer", + label: "Trainer", + type: ParamType.bool, + defaultValue: false, + ), ApiParam( - key: "commute", - label: "Commute", - type: ParamType.bool, - defaultValue: false), + key: "commute", + label: "Commute", + type: ParamType.bool, + defaultValue: false, + ), ], - run: (c, a) => c.activities.createActivity(CreateActivityRequest( + run: (c, a) => c.activities.createActivity( + CreateActivityRequest( a["name"] as String, a["type"] as ActivityTypeEnum, a["startDateLocal"] as DateTime, @@ -149,7 +167,9 @@ final List kApiCalls = [ a["description"] as String, a["distance"] as double, a["trainer"] as bool, - a["commute"] as bool)), + a["commute"] as bool, + ), + ), ), ApiCall( group: "Activity", @@ -158,28 +178,36 @@ final List kApiCalls = [ isWrite: true, params: [ _id("activityId", "Activity id"), - ApiParam(key: "name", label: "Name (blank = skip)", type: ParamType.string), ApiParam( - key: "description", - label: "Description (blank = skip)", - type: ParamType.string), + key: "name", + label: "Name (blank = skip)", + type: ParamType.string, + ), ApiParam( - key: "gearId", label: "Gear id (blank = skip)", type: ParamType.string), + key: "description", + label: "Description (blank = skip)", + type: ParamType.string, + ), + ApiParam( + key: "gearId", + label: "Gear id (blank = skip)", + type: ParamType.string, + ), ], run: (c, a) => c.activities.updateActivity( - a["activityId"] as int, - UpdateActivityRequest( - name: (a["name"] as String?)?.isEmpty ?? true - ? null - : a["name"] as String, - description: (a["description"] as String?)?.isEmpty ?? true - ? null - : a["description"] as String, - gearId: (a["gearId"] as String?)?.isEmpty ?? true - ? null - : a["gearId"] as String, - ), - ), + a["activityId"] as int, + UpdateActivityRequest( + name: (a["name"] as String?)?.isEmpty ?? true + ? null + : a["name"] as String, + description: (a["description"] as String?)?.isEmpty ?? true + ? null + : a["description"] as String, + gearId: (a["gearId"] as String?)?.isEmpty ?? true + ? null + : a["gearId"] as String, + ), + ), ), // ------------------------------------------------------------------- Club @@ -204,7 +232,10 @@ final List kApiCalls = [ description: "Recent activities of a club.", params: [_id("clubId", "Club id"), _page, _perPage], run: (c, a) => c.clubs.listClubActivities( - a["clubId"] as int, a["page"] as int, a["perPage"] as int), + a["clubId"] as int, + a["page"] as int, + a["perPage"] as int, + ), ), ApiCall( group: "Club", @@ -212,7 +243,10 @@ final List kApiCalls = [ description: "Administrators of a club.", params: [_id("clubId", "Club id"), _page, _perPage], run: (c, a) => c.clubs.listClubAdministrators( - a["clubId"] as int, a["page"] as int, a["perPage"] as int), + a["clubId"] as int, + a["page"] as int, + a["perPage"] as int, + ), ), ApiCall( group: "Club", @@ -220,7 +254,10 @@ final List kApiCalls = [ description: "Members of a club.", params: [_id("clubId", "Club id"), _page, _perPage], run: (c, a) => c.clubs.listClubMembers( - a["clubId"] as int, a["page"] as int, a["perPage"] as int), + a["clubId"] as int, + a["page"] as int, + a["perPage"] as int, + ), ), // ------------------------------------------------------------------- Gear @@ -230,9 +267,10 @@ final List kApiCalls = [ description: "Gear (bike/shoe) by id.", params: [ ApiParam( - key: "gearId", - label: "Gear id (e.g. b1234567)", - type: ParamType.string) + key: "gearId", + label: "Gear id (e.g. b1234567)", + type: ParamType.string, + ), ], run: (c, a) => c.gears.getGearById(a["gearId"] as String), ), @@ -251,7 +289,10 @@ final List kApiCalls = [ description: "Routes created by an athlete.", params: [_id("athleteId", "Athlete id"), _page, _perPage], run: (c, a) => c.routes.listAthleteRoutes( - a["athleteId"] as int, a["page"] as int, a["perPage"] as int), + a["athleteId"] as int, + a["page"] as int, + a["perPage"] as int, + ), ), ApiCall( group: "Route", @@ -282,10 +323,11 @@ final List kApiCalls = [ description: "Running races in a given year.", params: [ ApiParam( - key: "year", - label: "Year", - type: ParamType.int, - defaultValue: DateTime.now().year) + key: "year", + label: "Year", + type: ParamType.int, + defaultValue: DateTime.now().year, + ), ], run: (c, a) => c.runningRaces.listRunningRaces(a["year"] as int), ), @@ -305,22 +347,25 @@ final List kApiCalls = [ params: [ _id("segmentId", "Segment id"), ApiParam( - key: "startDate", - label: "Start date (ISO)", - type: ParamType.dateTime, - defaultValue: DateTime.now().subtract(const Duration(days: 365))), + key: "startDate", + label: "Start date (ISO)", + type: ParamType.dateTime, + defaultValue: DateTime.now().subtract(const Duration(days: 365)), + ), ApiParam( - key: "endDate", - label: "End date (ISO)", - type: ParamType.dateTime, - defaultValue: DateTime.now()), + key: "endDate", + label: "End date (ISO)", + type: ParamType.dateTime, + defaultValue: DateTime.now(), + ), _perPage, ], run: (c, a) => c.segmentEfforts.listSegmentEfforts( - a["segmentId"] as int, - a["startDate"] as DateTime, - a["endDate"] as DateTime, - a["perPage"] as int), + a["segmentId"] as int, + a["startDate"] as DateTime, + a["endDate"] as DateTime, + a["perPage"] as int, + ), ), // ---------------------------------------------------------------- Segment @@ -347,10 +392,11 @@ final List kApiCalls = [ params: [ _id("segmentId", "Segment id"), ApiParam( - key: "starred", - label: "Starred", - type: ParamType.bool, - defaultValue: true), + key: "starred", + label: "Starred", + type: ParamType.bool, + defaultValue: true, + ), ], run: (c, a) => c.segments.starSegment(a["segmentId"] as int, a["starred"] as bool), @@ -361,43 +407,50 @@ final List kApiCalls = [ description: "Explore segments within a bounding box.", params: [ ApiParam( - key: "swLat", - label: "SW latitude", - type: ParamType.double, - defaultValue: 37.821), + key: "swLat", + label: "SW latitude", + type: ParamType.double, + defaultValue: 37.821, + ), ApiParam( - key: "swLon", - label: "SW longitude", - type: ParamType.double, - defaultValue: -122.505), + key: "swLon", + label: "SW longitude", + type: ParamType.double, + defaultValue: -122.505, + ), ApiParam( - key: "neLat", - label: "NE latitude", - type: ParamType.double, - defaultValue: 37.842), + key: "neLat", + label: "NE latitude", + type: ParamType.double, + defaultValue: 37.842, + ), ApiParam( - key: "neLon", - label: "NE longitude", - type: ParamType.double, - defaultValue: -122.465), + key: "neLon", + label: "NE longitude", + type: ParamType.double, + defaultValue: -122.465, + ), _activityTypeParam, ApiParam( - key: "minCat", - label: "Min climb category", - type: ParamType.int, - defaultValue: 0), + key: "minCat", + label: "Min climb category", + type: ParamType.int, + defaultValue: 0, + ), ApiParam( - key: "maxCat", - label: "Max climb category", - type: ParamType.int, - defaultValue: 5), + key: "maxCat", + label: "Max climb category", + type: ParamType.int, + defaultValue: 5, + ), ], run: (c, a) => c.segments.exploreSegments( - GeoPoint(a["swLat"] as double, a["swLon"] as double), - GeoPoint(a["neLat"] as double, a["neLon"] as double), - a["type"] as ActivityTypeEnum, - a["minCat"] as int, - a["maxCat"] as int), + GeoPoint(a["swLat"] as double, a["swLon"] as double), + GeoPoint(a["neLat"] as double, a["neLon"] as double), + a["type"] as ActivityTypeEnum, + a["minCat"] as int, + a["maxCat"] as int, + ), ), ApiCall( group: "Segment", @@ -406,50 +459,60 @@ final List kApiCalls = [ params: [ _id("segmentId", "Segment id"), ApiParam( - key: "gender", - label: "Gender", - type: ParamType.enumValue, - defaultValue: SegmentGender.male, - enumValues: SegmentGender.values, - enumLabel: (v) => (v as SegmentGender).name), + key: "gender", + label: "Gender", + type: ParamType.enumValue, + defaultValue: SegmentGender.male, + enumValues: SegmentGender.values, + enumLabel: (v) => (v as SegmentGender).name, + ), ApiParam( - key: "ageGroup", - label: "Age group", - type: ParamType.enumValue, - defaultValue: SegmentAgeGroup.values.first, - enumValues: SegmentAgeGroup.values, - enumLabel: (v) => (v as SegmentAgeGroup).name), + key: "ageGroup", + label: "Age group", + type: ParamType.enumValue, + defaultValue: SegmentAgeGroup.values.first, + enumValues: SegmentAgeGroup.values, + enumLabel: (v) => (v as SegmentAgeGroup).name, + ), ApiParam( - key: "weightClass", - label: "Weight class", - type: ParamType.enumValue, - defaultValue: SegmentWeightClass.values.first, - enumValues: SegmentWeightClass.values, - enumLabel: (v) => (v as SegmentWeightClass).name), + key: "weightClass", + label: "Weight class", + type: ParamType.enumValue, + defaultValue: SegmentWeightClass.values.first, + enumValues: SegmentWeightClass.values, + enumLabel: (v) => (v as SegmentWeightClass).name, + ), ApiParam( - key: "dateRange", - label: "Date range", - type: ParamType.enumValue, - defaultValue: SegmentDateRange.this_year, - enumValues: SegmentDateRange.values, - enumLabel: (v) => (v as SegmentDateRange).name), + key: "dateRange", + label: "Date range", + type: ParamType.enumValue, + defaultValue: SegmentDateRange.this_year, + enumValues: SegmentDateRange.values, + enumLabel: (v) => (v as SegmentDateRange).name, + ), ApiParam( - key: "clubId", label: "Club id (0 = none)", type: ParamType.int, - defaultValue: 0), + key: "clubId", + label: "Club id (0 = none)", + type: ParamType.int, + defaultValue: 0, + ), ApiParam( - key: "maxEntries", - label: "Max entries", - type: ParamType.int, - defaultValue: 10), + key: "maxEntries", + label: "Max entries", + type: ParamType.int, + defaultValue: 10, + ), ApiParam( - key: "following", - label: "Following only", - type: ParamType.bool, - defaultValue: false), + key: "following", + label: "Following only", + type: ParamType.bool, + defaultValue: false, + ), _page, _perPage, ], - run: (c, a) => c.segments.getLeaderBoard(SegmentLeaderboardRequest( + run: (c, a) => c.segments.getLeaderBoard( + SegmentLeaderboardRequest( a["dateRange"] as SegmentDateRange, a["gender"] as SegmentGender, a["ageGroup"] as SegmentAgeGroup, @@ -459,7 +522,9 @@ final List kApiCalls = [ a["maxEntries"] as int, a["following"] as bool, a["page"] as int, - a["perPage"] as int)), + a["perPage"] as int, + ), + ), ), // ----------------------------------------------------------------- Stream @@ -468,8 +533,10 @@ final List kApiCalls = [ name: "getActivityStreams", description: "Activity streams as a list (array form).", params: [_id("activityId", "Activity id"), _streamKeys], - run: (c, a) => c.streams - .getActivityStreams(a["activityId"] as int, a["keys"] as List), + run: (c, a) => c.streams.getActivityStreams( + a["activityId"] as int, + a["keys"] as List, + ), ), ApiCall( group: "Stream", @@ -477,7 +544,9 @@ final List kApiCalls = [ description: "Activity streams keyed by type (StreamCollection).", params: [_id("activityId", "Activity id"), _streamKeys], run: (c, a) => c.streams.getActivityStreamsByType( - a["activityId"] as int, a["keys"] as List), + a["activityId"] as int, + a["keys"] as List, + ), ), ApiCall( group: "Stream", @@ -499,7 +568,9 @@ final List kApiCalls = [ description: "Segment-effort streams as a list.", params: [_id("effortId", "Segment effort id"), _streamKeys], run: (c, a) => c.streams.getSegmentEffortStreams( - a["effortId"] as int, a["keys"] as List), + a["effortId"] as int, + a["keys"] as List, + ), ), ApiCall( group: "Stream", @@ -507,15 +578,19 @@ final List kApiCalls = [ description: "Segment-effort streams keyed by type.", params: [_id("effortId", "Segment effort id"), _streamKeys], run: (c, a) => c.streams.getSegmentEffortStreamsByType( - a["effortId"] as int, a["keys"] as List), + a["effortId"] as int, + a["keys"] as List, + ), ), ApiCall( group: "Stream", name: "getSegmentStreams", description: "Segment streams as a list.", params: [_id("segmentId", "Segment id"), _streamKeys], - run: (c, a) => c.streams - .getSegmentStreams(a["segmentId"] as int, a["keys"] as List), + run: (c, a) => c.streams.getSegmentStreams( + a["segmentId"] as int, + a["keys"] as List, + ), ), ApiCall( group: "Stream", @@ -523,7 +598,9 @@ final List kApiCalls = [ description: "Segment streams keyed by type.", params: [_id("segmentId", "Segment id"), _streamKeys], run: (c, a) => c.streams.getSegmentStreamsByType( - a["segmentId"] as int, a["keys"] as List), + a["segmentId"] as int, + a["keys"] as List, + ), ), // ----------------------------------------------------------------- Upload @@ -541,7 +618,8 @@ const _streamKeys = ApiParam( label: "Keys (comma separated)", type: ParamType.stringList, defaultValue: "distance,heartrate,watts", - hint: "time, distance, latlng, altitude, velocity_smooth, heartrate, " + hint: + "time, distance, latlng, altitude, velocity_smooth, heartrate, " "cadence, watts, temp, moving, grade_smooth", ); diff --git a/example/lib/api/result_format.dart b/example/lib/api/result_format.dart index f439d3d..6e6a30d 100644 --- a/example/lib/api/result_format.dart +++ b/example/lib/api/result_format.dart @@ -13,8 +13,7 @@ dynamic _toEncodable(dynamic value) { if (value is num || value is String || value is bool) return value; if (value is List) return value.map(_toEncodable).toList(); if (value is Map) { - return value - .map((k, v) => MapEntry(k.toString(), _toEncodable(v))); + return value.map((k, v) => MapEntry(k.toString(), _toEncodable(v))); } try { // Strava models expose toJson(); explicit_to_json makes it fully expanded. diff --git a/example/lib/examples/authentication.dart b/example/lib/examples/authentication.dart index 279daeb..56e1b0c 100644 --- a/example/lib/examples/authentication.dart +++ b/example/lib/examples/authentication.dart @@ -5,13 +5,16 @@ class ExampleAuthentication { ExampleAuthentication(this.stravaClient); Future testAuthentication( - List scopes, String redirectUrl) { + List scopes, + String redirectUrl, + ) { return stravaClient.authentication.authenticate( - scopes: scopes, - redirectUrl: redirectUrl, - forceShowingApproval: false, - callbackUrlScheme: "stravaflutter", - preferEphemeral: true); + scopes: scopes, + redirectUrl: redirectUrl, + forceShowingApproval: false, + callbackUrlScheme: "stravaflutter", + preferEphemeral: true, + ); } Future testDeauthorize() { diff --git a/example/lib/main.dart b/example/lib/main.dart index 6f6259d..8562899 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -57,7 +57,7 @@ class _StravaExplorerPageState extends State { FutureOr _showError(dynamic error, dynamic stackTrace) { final message = error is Fault ? 'Fault: ${error.message}\n' - '${(error.errors ?? []).map((e) => "• ${e.code} (${e.field})").join("\n")}' + '${(error.errors ?? []).map((e) => "• ${e.code} (${e.field})").join("\n")}' : error.toString(); if (!mounted) return null; showDialog( @@ -67,8 +67,9 @@ class _StravaExplorerPageState extends State { content: SingleChildScrollView(child: Text(message)), actions: [ TextButton( - onPressed: () => Navigator.pop(context), - child: const Text('OK')) + onPressed: () => Navigator.pop(context), + child: const Text('OK'), + ), ], ), ); @@ -76,32 +77,35 @@ class _StravaExplorerPageState extends State { } void _login() { - ExampleAuthentication(stravaClient).testAuthentication( - const [ - AuthenticationScope.profile_read_all, - AuthenticationScope.read_all, - AuthenticationScope.activity_read_all, - AuthenticationScope.activity_write, - AuthenticationScope.profile_write, - ], - "stravaflutter://redirect", - ).then((token) { - setState(() { - isLoggedIn = true; - this.token = token; - _tokenController.text = token.accessToken; - }); - }).catchError(_showError); + ExampleAuthentication(stravaClient) + .testAuthentication(const [ + AuthenticationScope.profile_read_all, + AuthenticationScope.read_all, + AuthenticationScope.activity_read_all, + AuthenticationScope.activity_write, + AuthenticationScope.profile_write, + ], "stravaflutter://redirect") + .then((token) { + setState(() { + isLoggedIn = true; + this.token = token; + _tokenController.text = token.accessToken; + }); + }) + .catchError(_showError); } void _logout() { - ExampleAuthentication(stravaClient).testDeauthorize().then((_) { - setState(() { - isLoggedIn = false; - token = null; - _tokenController.clear(); - }); - }).catchError(_showError); + ExampleAuthentication(stravaClient) + .testDeauthorize() + .then((_) { + setState(() { + isLoggedIn = false; + token = null; + _tokenController.clear(); + }); + }) + .catchError(_showError); } @override @@ -164,10 +168,10 @@ class _StravaExplorerPageState extends State { suffixIcon: IconButton( icon: const Icon(Icons.copy), onPressed: () { - Clipboard.setData( - ClipboardData(text: _tokenController.text)); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Copied'))); + Clipboard.setData(ClipboardData(text: _tokenController.text)); + ScaffoldMessenger.of( + context, + ).showSnackBar(const SnackBar(content: Text('Copied'))); }, ), ), @@ -182,8 +186,7 @@ class _StravaExplorerPageState extends State { return const Center( child: Padding( padding: EdgeInsets.all(24), - child: Text('Login to run API calls.', - textAlign: TextAlign.center), + child: Text('Login to run API calls.', textAlign: TextAlign.center), ), ); } @@ -191,8 +194,10 @@ class _StravaExplorerPageState extends State { return ListView( children: grouped.entries.map((entry) { return ExpansionTile( - title: Text(entry.key, - style: const TextStyle(fontWeight: FontWeight.w600)), + title: Text( + entry.key, + style: const TextStyle(fontWeight: FontWeight.w600), + ), children: entry.value.map(_callTile).toList(), ); }).toList(), @@ -208,9 +213,11 @@ class _StravaExplorerPageState extends State { ? const Icon(Icons.edit, color: Colors.orange, size: 20) : const Icon(Icons.download, color: Colors.blueGrey, size: 20), trailing: const Icon(Icons.chevron_right), - onTap: () => Navigator.of(context).push(MaterialPageRoute( - builder: (_) => CallScreen(client: stravaClient, call: call), - )), + onTap: () => Navigator.of(context).push( + MaterialPageRoute( + builder: (_) => CallScreen(client: stravaClient, call: call), + ), + ), ); } } diff --git a/example/lib/screens/call_screen.dart b/example/lib/screens/call_screen.dart index 8186281..a4496e9 100644 --- a/example/lib/screens/call_screen.dart +++ b/example/lib/screens/call_screen.dart @@ -38,8 +38,7 @@ class _CallScreenState extends State { (p.defaultValue as Object?) ?? p.enumValues!.first; break; default: - _controllers[p.key] = - TextEditingController(text: _initialText(p)); + _controllers[p.key] = TextEditingController(text: _initialText(p)); } } } @@ -73,8 +72,7 @@ class _CallScreenState extends State { args[p.key] = DateTime.parse(_controllers[p.key]!.text.trim()); break; case ParamType.stringList: - args[p.key] = _controllers[p.key]! - .text + args[p.key] = _controllers[p.key]!.text .split(',') .map((e) => e.trim()) .where((e) => e.isNotEmpty) @@ -121,8 +119,11 @@ class _CallScreenState extends State { String _formatFault(Fault fault) { final errors = (fault.errors ?? []) - .map((e) => ' - code: ${e.code}, field: ${e.field}, ' - 'resource: ${e.resource}') + .map( + (e) => + ' - code: ${e.code}, field: ${e.field}, ' + 'resource: ${e.resource}', + ) .join('\n'); return 'Strava Fault\nmessage: ${fault.message}\nerrors:\n$errors'; } @@ -148,8 +149,7 @@ class _CallScreenState extends State { body: ListView( padding: const EdgeInsets.all(16), children: [ - Text(call.description, - style: Theme.of(context).textTheme.bodyMedium), + Text(call.description, style: Theme.of(context).textTheme.bodyMedium), if (call.isWrite) Padding( padding: const EdgeInsets.only(top: 8), @@ -167,7 +167,8 @@ class _CallScreenState extends State { ? const SizedBox( width: 16, height: 16, - child: CircularProgressIndicator(strokeWidth: 2)) + child: CircularProgressIndicator(strokeWidth: 2), + ) : const Icon(Icons.play_arrow), label: Text(_loading ? 'Running…' : 'Run'), ), @@ -192,16 +193,20 @@ class _CallScreenState extends State { padding: const EdgeInsets.symmetric(vertical: 8), child: InputDecorator( decoration: InputDecoration( - labelText: p.label, border: const OutlineInputBorder()), + labelText: p.label, + border: const OutlineInputBorder(), + ), child: DropdownButtonHideUnderline( child: DropdownButton( isExpanded: true, value: _enumValues[p.key], items: p.enumValues! - .map((v) => DropdownMenuItem( - value: v, - child: Text(p.enumLabel?.call(v) ?? v.toString()), - )) + .map( + (v) => DropdownMenuItem( + value: v, + child: Text(p.enumLabel?.call(v) ?? v.toString()), + ), + ) .toList(), onChanged: (v) => setState(() => _enumValues[p.key] = v!), ), @@ -209,8 +214,7 @@ class _CallScreenState extends State { ), ); } - final isNumber = - p.type == ParamType.int || p.type == ParamType.double; + final isNumber = p.type == ParamType.int || p.type == ParamType.double; return Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: TextField( @@ -234,17 +238,21 @@ class _CallScreenState extends State { children: [ Row( children: [ - Text(_isError ? 'Error' : 'Response', - style: Theme.of(context).textTheme.titleMedium?.copyWith( - color: _isError ? Colors.red : Colors.green.shade800)), + Text( + _isError ? 'Error' : 'Response', + style: Theme.of(context).textTheme.titleMedium?.copyWith( + color: _isError ? Colors.red : Colors.green.shade800, + ), + ), const Spacer(), TextButton.icon( icon: const Icon(Icons.copy, size: 16), label: const Text('Copy'), onPressed: () { Clipboard.setData(ClipboardData(text: _result ?? '')); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Copied'))); + ScaffoldMessenger.of( + context, + ).showSnackBar(const SnackBar(content: Text('Copied'))); }, ), ], diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart index 6911bc9..8b1d4ed 100644 --- a/example/test/widget_test.dart +++ b/example/test/widget_test.dart @@ -5,8 +5,9 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:example/main.dart'; void main() { - testWidgets('shows login button and empty-state on launch', - (WidgetTester tester) async { + testWidgets('shows login button and empty-state on launch', ( + WidgetTester tester, + ) async { await tester.pumpWidget(const MyApp()); expect(find.text('Login with Strava'), findsOneWidget); diff --git a/lib/src/common/local_storage.dart b/lib/src/common/local_storage.dart index 925dfaf..4439405 100644 --- a/lib/src/common/local_storage.dart +++ b/lib/src/common/local_storage.dart @@ -9,12 +9,16 @@ class LocalStorageManager { // static String _kScopesKey = "strava_scopes"; static Future saveToken( - TokenResponse token, List scopes, - {String applicationName = ""}) async { + TokenResponse token, + List scopes, { + String applicationName = "", + }) async { var sharedPrefs = await SharedPreferences.getInstance(); token.scopes = AuthenticationScopeHelper.buildScopeString(scopes); return sharedPrefs.setString( - "$_kTokenKey+_$applicationName", token.toRawJson()); + "$_kTokenKey+_$applicationName", + token.toRawJson(), + ); } static Future deleteToken({String applicationName = ""}) async { diff --git a/lib/src/common/session_manager.dart b/lib/src/common/session_manager.dart index 01e99c2..b6378fe 100644 --- a/lib/src/common/session_manager.dart +++ b/lib/src/common/session_manager.dart @@ -14,10 +14,11 @@ class SessionManager { // ignore: unused_field List? _scopes; - void initialize( - {required String secret, - required String clientId, - String applicationName = ""}) { + void initialize({ + required String secret, + required String clientId, + String applicationName = "", + }) { this.secret = secret; this.clientId = clientId; this.applicationName = applicationName; @@ -28,8 +29,9 @@ class SessionManager { if (_currentToken != null) { completer.complete(_currentToken); } else { - LocalStorageManager.getToken(applicationName: applicationName) - .then((storedValue) { + LocalStorageManager.getToken(applicationName: applicationName).then(( + storedValue, + ) { if (storedValue != null) { _currentToken = storedValue; } @@ -39,19 +41,23 @@ class SessionManager { return completer.future; } - Future setToken( - {required TokenResponse token, - required List scopes}) { + Future setToken({ + required TokenResponse token, + required List scopes, + }) { _currentToken = token; _scopes = scopes; - return LocalStorageManager.saveToken(token, scopes, - applicationName: applicationName) - .then((value) => _currentToken = token); + return LocalStorageManager.saveToken( + token, + scopes, + applicationName: applicationName, + ).then((value) => _currentToken = token); } bool isTokenExpired(TokenResponse token) { - DateTime expiresAt = - DateTime.fromMillisecondsSinceEpoch(token.expiresAt * 1000); + DateTime expiresAt = DateTime.fromMillisecondsSinceEpoch( + token.expiresAt * 1000, + ); return DateTime.now().isAfter(expiresAt); } @@ -66,8 +72,9 @@ class SessionManager { final token = await getToken(); if (token == null) return null; if (!isTokenExpired(token)) return token; - return _refreshInFlight ??= _refreshToken(token) - .whenComplete(() => _refreshInFlight = null); + return _refreshInFlight ??= _refreshToken( + token, + ).whenComplete(() => _refreshInFlight = null); } Future _refreshToken(TokenResponse token) async { diff --git a/lib/src/data/repository/client.dart b/lib/src/data/repository/client.dart index ce5dfa2..d5622c5 100644 --- a/lib/src/data/repository/client.dart +++ b/lib/src/data/repository/client.dart @@ -15,7 +15,9 @@ class ApiClient { var headers = {}; if (token != null) { headers.putIfAbsent( - "Authorization", () => "Bearer ${token.accessToken}"); + "Authorization", + () => "Bearer ${token.accessToken}", + ); } dio.options = BaseOptions(headers: headers); @@ -24,67 +26,86 @@ class ApiClient { return Future.value(dio); } - static Future getRequest( - {required String endPoint, - Map? queryParameters, - required T Function(dynamic) dataConstructor}) async { + static Future getRequest({ + required String endPoint, + Map? queryParameters, + required T Function(dynamic) dataConstructor, + }) async { var completer = Completer(); _getDioClient().then((client) { client .get("$_baseUrl$endPoint", queryParameters: queryParameters) .then( - (response) => completer.complete(dataConstructor(response.data))) + (response) => completer.complete(dataConstructor(response.data)), + ) .catchError( - (error, stackTrace) => handleError(completer, error, stackTrace)); + (error, stackTrace) => handleError(completer, error, stackTrace), + ); }); return completer.future; } - static Future postRequest( - {required String endPoint, - String? baseUrl, - Map? queryParameters, - dynamic postBody, - required T Function(dynamic) dataConstructor}) async { + static Future postRequest({ + required String endPoint, + String? baseUrl, + Map? queryParameters, + dynamic postBody, + required T Function(dynamic) dataConstructor, + }) async { var completer = Completer(); _getDioClient().then((client) { client - .post("${baseUrl ?? _baseUrl}$endPoint", - queryParameters: queryParameters, data: postBody) + .post( + "${baseUrl ?? _baseUrl}$endPoint", + queryParameters: queryParameters, + data: postBody, + ) .then( - (response) => completer.complete(dataConstructor(response.data))) + (response) => completer.complete(dataConstructor(response.data)), + ) .catchError( - (error, stackTrace) => handleError(completer, error, stackTrace)); + (error, stackTrace) => handleError(completer, error, stackTrace), + ); }); return completer.future; } - static Future putRequest( - {required String endPoint, - Map? queryParameters, - dynamic postBody, - required T Function(dynamic) dataConstructor}) async { + static Future putRequest({ + required String endPoint, + Map? queryParameters, + dynamic postBody, + required T Function(dynamic) dataConstructor, + }) async { var completer = Completer(); _getDioClient().then((client) { client - .put("$_baseUrl$endPoint", - queryParameters: queryParameters, data: postBody) + .put( + "$_baseUrl$endPoint", + queryParameters: queryParameters, + data: postBody, + ) .then( - (response) => completer.complete(dataConstructor(response.data))) + (response) => completer.complete(dataConstructor(response.data)), + ) .catchError( - (error, stackTrace) => handleError(completer, error, stackTrace)); + (error, stackTrace) => handleError(completer, error, stackTrace), + ); }); return completer.future; } static void handleError( - Completer completer, dynamic error, StackTrace stackTrace) { + Completer completer, + dynamic error, + StackTrace stackTrace, + ) { if (error is DioException) { if (error.response != null && error.response?.data != null && error.response?.data is Map) { - var stravaFault = - Fault.fromJson(Map.from(error.response?.data)); + var stravaFault = Fault.fromJson( + Map.from(error.response?.data), + ); completer.completeError(stravaFault); } else { completer.completeError(error, stackTrace); diff --git a/lib/src/data/repository/repository_activity_impl.dart b/lib/src/data/repository/repository_activity_impl.dart index cb51de2..736eb59 100644 --- a/lib/src/data/repository/repository_activity_impl.dart +++ b/lib/src/data/repository/repository_activity_impl.dart @@ -7,107 +7,121 @@ class RepositoryActivityImpl extends RepositoryActivity { @override Future getActivity(int activityId) { return ApiClient.getRequest( - endPoint: "/v3/activities/$activityId", - dataConstructor: (data) => - DetailedActivity.fromJson(Map.from(data))); + endPoint: "/v3/activities/$activityId", + dataConstructor: (data) => + DetailedActivity.fromJson(Map.from(data)), + ); } @override Future> listActivityComments(int activityId) { return ApiClient.getRequest( - endPoint: "/v3/activities/$activityId/comments", - dataConstructor: (data) { - if (data is List) { - return data - .map((d) => Comment.fromJson(Map.from(d))) - .toList(); - } - return []; - }); + endPoint: "/v3/activities/$activityId/comments", + dataConstructor: (data) { + if (data is List) { + return data + .map((d) => Comment.fromJson(Map.from(d))) + .toList(); + } + return []; + }, + ); } @override Future> listActivityKudoers(int activityId) { return ApiClient.getRequest( - endPoint: "/v3/activities/$activityId/comments", - dataConstructor: (data) { - if (data is List) { - return data - .map((d) => - SummaryAthlete.fromJson(Map.from(d))) - .toList(); - } - return []; - }); + endPoint: "/v3/activities/$activityId/comments", + dataConstructor: (data) { + if (data is List) { + return data + .map((d) => SummaryAthlete.fromJson(Map.from(d))) + .toList(); + } + return []; + }, + ); } @override Future> getLapsByActivityId(int activityId) { return ApiClient.getRequest( - endPoint: "/v3/activities/$activityId/laps", - dataConstructor: (data) { - if (data is List) { - return data - .map((d) => Lap.fromJson(Map.from(d))) - .toList(); - } - return []; - }); + endPoint: "/v3/activities/$activityId/laps", + dataConstructor: (data) { + if (data is List) { + return data + .map((d) => Lap.fromJson(Map.from(d))) + .toList(); + } + return []; + }, + ); } @override Future> listLoggedInAthleteActivities( - DateTime before, DateTime after, int page, int perPage) { + DateTime before, + DateTime after, + int page, + int perPage, + ) { var queryParams = { "before": before.millisecondsSinceEpoch / 1000, "after": after.millisecondsSinceEpoch / 1000, "page": page, - "per_page": perPage + "per_page": perPage, }; return ApiClient.getRequest( - endPoint: "/v3/athlete/activities", - queryParameters: queryParams, - dataConstructor: (data) { - if (data is List) { - return data - .map((d) => - SummaryActivity.fromJson(Map.from(d))) - .toList(); - } - return []; - }); + endPoint: "/v3/athlete/activities", + queryParameters: queryParams, + dataConstructor: (data) { + if (data is List) { + return data + .map( + (d) => SummaryActivity.fromJson(Map.from(d)), + ) + .toList(); + } + return []; + }, + ); } @override Future> getActivityZones(int activityId) { return ApiClient.getRequest( - endPoint: "/v3/activities/$activityId/zones", - dataConstructor: (data) { - if (data is List) { - return data - .map((d) => ActivityZone.fromJson(Map.from(d))) - .toList(); - } - return []; - }); + endPoint: "/v3/activities/$activityId/zones", + dataConstructor: (data) { + if (data is List) { + return data + .map((d) => ActivityZone.fromJson(Map.from(d))) + .toList(); + } + return []; + }, + ); } @override Future createActivity(CreateActivityRequest request) { return ApiClient.postRequest( - endPoint: "/v3/activities", - postBody: request.toJson(), - dataConstructor: (data) => - DetailedActivity.fromJson(Map.from(data))); + endPoint: "/v3/activities", + postBody: request.toJson(), + dataConstructor: (data) => + DetailedActivity.fromJson(Map.from(data)), + ); } @override Future updateActivity( - int activityId, UpdateActivityRequest request) { + int activityId, + UpdateActivityRequest request, + ) { return ApiClient.putRequest( - endPoint: "/v3/activities/$activityId", - postBody: request.toJson(), - dataConstructor: (data) => - DetailedActivity.fromJson(Map.from(data))); + endPoint: "/v3/activities/$activityId", + postBody: request.toJson(), + dataConstructor: (data) => + DetailedActivity.fromJson(Map.from(data)), + ); } } diff --git a/lib/src/data/repository/repository_athlete_impl.dart b/lib/src/data/repository/repository_athlete_impl.dart index 78884b0..83a022d 100644 --- a/lib/src/data/repository/repository_athlete_impl.dart +++ b/lib/src/data/repository/repository_athlete_impl.dart @@ -6,48 +6,53 @@ class RepositoryAthleteImpl extends RepositoryAthlete { @override Future getAthleteStats(int athleteId) { return ApiClient.getRequest( - endPoint: "/v3/athletes/$athleteId/stats", - dataConstructor: (data) => ActivityStats.fromJson(data)); + endPoint: "/v3/athletes/$athleteId/stats", + dataConstructor: (data) => ActivityStats.fromJson(data), + ); } @override Future getAuthenticatedAthlete() { return ApiClient.getRequest( - endPoint: "/v3/athlete", - dataConstructor: (data) { - return DetailedAthlete.fromJson(data); - }); + endPoint: "/v3/athlete", + dataConstructor: (data) { + return DetailedAthlete.fromJson(data); + }, + ); } @Deprecated('Mismodels GET /athlete/zones. Use getAthleteZones() instead.') @override Future> getZones() { return ApiClient.getRequest>( - endPoint: "/v3/athlete/zones", - dataConstructor: (data) { - if (data is List) { - return data - .map((e) => Zones.fromJson(Map.from(e))) - .toList(); - } else { - return []; - } - }); + endPoint: "/v3/athlete/zones", + dataConstructor: (data) { + if (data is List) { + return data + .map((e) => Zones.fromJson(Map.from(e))) + .toList(); + } else { + return []; + } + }, + ); } @override Future getAthleteZones() { return ApiClient.getRequest( - endPoint: "/v3/athlete/zones", - dataConstructor: (data) => - AthleteZones.fromJson(Map.from(data))); + endPoint: "/v3/athlete/zones", + dataConstructor: (data) => + AthleteZones.fromJson(Map.from(data)), + ); } @override Future updateAthlete(double weight) { return ApiClient.putRequest( - endPoint: "/v3/athlete", - queryParameters: {"weight": weight}, - dataConstructor: (data) => DetailedAthlete.fromJson(data)); + endPoint: "/v3/athlete", + queryParameters: {"weight": weight}, + dataConstructor: (data) => DetailedAthlete.fromJson(data), + ); } } diff --git a/lib/src/data/repository/repository_authentication_impl.dart b/lib/src/data/repository/repository_authentication_impl.dart index 2afe244..8b8dd86 100644 --- a/lib/src/data/repository/repository_authentication_impl.dart +++ b/lib/src/data/repository/repository_authentication_impl.dart @@ -14,75 +14,91 @@ class RepositoryAuthenticationImpl extends RepositoryAuthentication { StreamSubscription? _uriLinkStream; @override - Future authenticate( - {required List scopes, - required String redirectUrl, - required String callbackUrlScheme, - bool forceShowingApproval = false, - bool? preferEphemeral}) async { + Future authenticate({ + required List scopes, + required String redirectUrl, + required String callbackUrlScheme, + bool forceShowingApproval = false, + bool? preferEphemeral, + }) async { var completer = Completer(); var token = await sl().getToken(); if (token == null) { - completer.complete(_completeAuthentication( + completer.complete( + _completeAuthentication( scopes: scopes, forceShowingApproval: forceShowingApproval, redirectUrl: redirectUrl, callbackUrlScheme: callbackUrlScheme, - preferEphemeral: preferEphemeral)); + preferEphemeral: preferEphemeral, + ), + ); } else { List oldScopes = AuthenticationScopeHelper.generateScopes(token.scopes ?? ""); var isScopesAreSame = _compareScopes(oldScopes, scopes); if (isScopesAreSame) { if (sl().isTokenExpired(token)) { - _refreshAccessToken(sl().clientId, - sl().secret, token.refreshToken) + _refreshAccessToken( + sl().clientId, + sl().secret, + token.refreshToken, + ) .then((refreshResult) { - sl() - .setToken( - scopes: scopes, - token: TokenResponse( + sl() + .setToken( + scopes: scopes, + token: TokenResponse( tokenType: token.tokenType, expiresAt: refreshResult.expiresAt, expiresIn: refreshResult.expiresIn, refreshToken: refreshResult.refreshToken, - accessToken: refreshResult.accessToken)) - .then((value) => completer.complete(refreshResult)); - }).onError((error, stackTrace) { - completer.completeError(error!, stackTrace); - }); + accessToken: refreshResult.accessToken, + ), + ) + .then((value) => completer.complete(refreshResult)); + }) + .onError((error, stackTrace) { + completer.completeError(error!, stackTrace); + }); } else { completer.complete(token); } } else { // Scopes have changed. we need a new token. - completer.complete(_completeAuthentication( + completer.complete( + _completeAuthentication( scopes: scopes, forceShowingApproval: forceShowingApproval, redirectUrl: redirectUrl, callbackUrlScheme: callbackUrlScheme, - preferEphemeral: preferEphemeral)); + preferEphemeral: preferEphemeral, + ), + ); } } return completer.future; } - Future _completeAuthentication( - {required List scopes, - required bool forceShowingApproval, - required String redirectUrl, - required String callbackUrlScheme, - bool? preferEphemeral}) { + Future _completeAuthentication({ + required List scopes, + required bool forceShowingApproval, + required String redirectUrl, + required String callbackUrlScheme, + bool? preferEphemeral, + }) { return _getStravaCode( - redirectUrl: redirectUrl, - scopes: scopes, - forceShowingApproval: forceShowingApproval, - callbackUrlScheme: callbackUrlScheme, - preferEphemeral: preferEphemeral) - .then((code) { + redirectUrl: redirectUrl, + scopes: scopes, + forceShowingApproval: forceShowingApproval, + callbackUrlScheme: callbackUrlScheme, + preferEphemeral: preferEphemeral, + ).then((code) { return _requestNewAccessToken( - sl().clientId, sl().secret, code) - .then((token) async { + sl().clientId, + sl().secret, + code, + ).then((token) async { await sl().setToken(token: token, scopes: scopes); return token; }); @@ -92,12 +108,13 @@ class RepositoryAuthenticationImpl extends RepositoryAuthentication { /// RedirectUrl works best when it is a custom scheme. For example: strava://auth /// /// If your redirectUrl is, for example, strava://auth then your callbackUrlScheme should be strava - Future _getStravaCode( - {required String redirectUrl, - required List scopes, - required bool forceShowingApproval, - required String callbackUrlScheme, - bool? preferEphemeral}) async { + Future _getStravaCode({ + required String redirectUrl, + required List scopes, + required bool forceShowingApproval, + required String callbackUrlScheme, + bool? preferEphemeral, + }) async { final Completer completer = Completer(); final params = '?client_id=${sl().clientId}&redirect_uri=$redirectUrl&response_type=code&approval_prompt=${forceShowingApproval ? "force" : "auto"}&scope=${AuthenticationScopeHelper.buildScopeString(scopes)}'; @@ -131,9 +148,10 @@ class RepositoryAuthenticationImpl extends RepositoryAuthentication { final reqAuth = host + authorizationEndpoint + params; try { final result = await FlutterWebAuth2.authenticate( - url: reqAuth, - callbackUrlScheme: callbackUrlScheme, - options: FlutterWebAuth2Options(preferEphemeral: preferEphemeral)); + url: reqAuth, + callbackUrlScheme: callbackUrlScheme, + options: FlutterWebAuth2Options(preferEphemeral: preferEphemeral), + ); final parsed = Uri.parse(result); @@ -152,33 +170,43 @@ class RepositoryAuthenticationImpl extends RepositoryAuthentication { } Future _requestNewAccessToken( - String clientID, String secret, String code) { + String clientID, + String secret, + String code, + ) { return ApiClient.postRequest( - endPoint: "/v3/oauth/token", - queryParameters: { - "client_id": clientID, - "client_secret": secret, - "code": code, - "grant_type": "authorization_code", - }, - dataConstructor: (data) => TokenResponse.fromJson(data)); + endPoint: "/v3/oauth/token", + queryParameters: { + "client_id": clientID, + "client_secret": secret, + "code": code, + "grant_type": "authorization_code", + }, + dataConstructor: (data) => TokenResponse.fromJson(data), + ); } Future _refreshAccessToken( - String clientID, String secret, String refreshToken) { + String clientID, + String secret, + String refreshToken, + ) { return ApiClient.postRequest( - endPoint: "/v3/oauth/token", - queryParameters: { - "client_id": clientID, - "client_secret": secret, - "grant_type": "refresh_token", - "refresh_token": refreshToken - }, - dataConstructor: (data) => TokenResponse.fromJson(data)); + endPoint: "/v3/oauth/token", + queryParameters: { + "client_id": clientID, + "client_secret": secret, + "grant_type": "refresh_token", + "refresh_token": refreshToken, + }, + dataConstructor: (data) => TokenResponse.fromJson(data), + ); } bool _compareScopes( - List left, List right) { + List left, + List right, + ) { if (left.length != right.length) { return false; } @@ -191,10 +219,10 @@ class RepositoryAuthenticationImpl extends RepositoryAuthentication { var params = {"access_token": token?.accessToken ?? ""}; return ApiClient.postRequest( - baseUrl: "https://www.strava.com/", - endPoint: "/oauth/deauthorize", - queryParameters: params, - dataConstructor: (data) => null) - .whenComplete(() => sl().logout()); + baseUrl: "https://www.strava.com/", + endPoint: "/oauth/deauthorize", + queryParameters: params, + dataConstructor: (data) => null, + ).whenComplete(() => sl().logout()); } } diff --git a/lib/src/data/repository/repository_club_impl.dart b/lib/src/data/repository/repository_club_impl.dart index 4ae5eb3..43704f9 100644 --- a/lib/src/data/repository/repository_club_impl.dart +++ b/lib/src/data/repository/repository_club_impl.dart @@ -6,70 +6,82 @@ class RepositoryClubImpl extends RepositoryClub { @override Future getClub(int clubId) { return ApiClient.getRequest( - endPoint: "/v3/clubs/$clubId", - dataConstructor: (data) => - Club.fromJson(Map.from(data))); + endPoint: "/v3/clubs/$clubId", + dataConstructor: (data) => Club.fromJson(Map.from(data)), + ); } @override Future> getLoggedInAthleteClubs(int page, int perPage) { return ApiClient.getRequest( - endPoint: "v3/athlete/clubs", - dataConstructor: (data) { - if (data is List) { - return data - .map((e) => SummaryClub.fromJson(Map.from(e))) - .toList(); - } - return []; - }); + endPoint: "v3/athlete/clubs", + dataConstructor: (data) { + if (data is List) { + return data + .map((e) => SummaryClub.fromJson(Map.from(e))) + .toList(); + } + return []; + }, + ); } @override Future> listClubActivities( - int clubId, int page, int perPage) { + int clubId, + int page, + int perPage, + ) { return ApiClient.getRequest( - endPoint: "v3/clubs/$clubId/activities", - dataConstructor: (data) { - if (data is List) { - return data - .map((e) => - SummaryActivity.fromJson(Map.from(e))) - .toList(); - } - return []; - }); + endPoint: "v3/clubs/$clubId/activities", + dataConstructor: (data) { + if (data is List) { + return data + .map( + (e) => SummaryActivity.fromJson(Map.from(e)), + ) + .toList(); + } + return []; + }, + ); } @override Future> listClubAdministrators( - int clubId, int page, int perPage) { + int clubId, + int page, + int perPage, + ) { return ApiClient.getRequest( - endPoint: "v3/clubs/$clubId/admins", - dataConstructor: (data) { - if (data is List) { - return data - .map((e) => - SummaryAthlete.fromJson(Map.from(e))) - .toList(); - } - return []; - }); + endPoint: "v3/clubs/$clubId/admins", + dataConstructor: (data) { + if (data is List) { + return data + .map((e) => SummaryAthlete.fromJson(Map.from(e))) + .toList(); + } + return []; + }, + ); } @override Future> listClubMembers( - int clubId, int page, int perPage) { + int clubId, + int page, + int perPage, + ) { return ApiClient.getRequest( - endPoint: "v3/clubs/$clubId/members", - dataConstructor: (data) { - if (data is List) { - return data - .map((e) => - SummaryAthlete.fromJson(Map.from(e))) - .toList(); - } - return []; - }); + endPoint: "v3/clubs/$clubId/members", + dataConstructor: (data) { + if (data is List) { + return data + .map((e) => SummaryAthlete.fromJson(Map.from(e))) + .toList(); + } + return []; + }, + ); } } diff --git a/lib/src/data/repository/repository_gear_impl.dart b/lib/src/data/repository/repository_gear_impl.dart index f15f95c..4507def 100644 --- a/lib/src/data/repository/repository_gear_impl.dart +++ b/lib/src/data/repository/repository_gear_impl.dart @@ -10,8 +10,8 @@ class RepositoryGearImpl extends RepositoryGear { @override Future getGearById(String gearId) { return ApiClient.getRequest( - endPoint: "/v3/gear/$gearId", - dataConstructor: (data) => - Gear.fromJson(Map.from(data))); + endPoint: "/v3/gear/$gearId", + dataConstructor: (data) => Gear.fromJson(Map.from(data)), + ); } } diff --git a/lib/src/data/repository/repository_route_impl.dart b/lib/src/data/repository/repository_route_impl.dart index a9210cc..da93338 100644 --- a/lib/src/data/repository/repository_route_impl.dart +++ b/lib/src/data/repository/repository_route_impl.dart @@ -8,24 +8,26 @@ class RepositoryRouteImpl extends RepositoryRoute { @override Future getRoute(int routeId) { return ApiClient.getRequest( - endPoint: "/v3/routes/$routeId", - dataConstructor: (data) => - Route.fromJson(Map.from(data))); + endPoint: "/v3/routes/$routeId", + dataConstructor: (data) => + Route.fromJson(Map.from(data)), + ); } @override Future> listAthleteRoutes(int athleteId, int page, int perPage) { return ApiClient.getRequest( - endPoint: "/v3/athletes/$athleteId/routes", - queryParameters: {"page": page, "per_page": perPage}, - dataConstructor: (data) { - if (data is List) { - return data - .map((e) => Route.fromJson(Map.from(e))) - .toList(); - } - return []; - }); + endPoint: "/v3/athletes/$athleteId/routes", + queryParameters: {"page": page, "per_page": perPage}, + dataConstructor: (data) { + if (data is List) { + return data + .map((e) => Route.fromJson(Map.from(e))) + .toList(); + } + return []; + }, + ); } @override diff --git a/lib/src/data/repository/repository_running_race_impl.dart b/lib/src/data/repository/repository_running_race_impl.dart index 44d6d43..14f995c 100644 --- a/lib/src/data/repository/repository_running_race_impl.dart +++ b/lib/src/data/repository/repository_running_race_impl.dart @@ -6,22 +6,24 @@ class RepositoryRunningRaceImpl extends RepositoryRunningRace { @override Future getRage(int raceId) { return ApiClient.getRequest( - endPoint: "/v3/running_races/$raceId", - dataConstructor: (data) => - RunningRace.fromJson(Map.from(data))); + endPoint: "/v3/running_races/$raceId", + dataConstructor: (data) => + RunningRace.fromJson(Map.from(data)), + ); } @override Future> listRunningRaces(int year) { return ApiClient.getRequest( - endPoint: "/v3/running_races", - dataConstructor: (data) { - if (data is List) { - return data - .map((e) => RunningRace.fromJson(Map.from(e))) - .toList(); - } - return []; - }); + endPoint: "/v3/running_races", + dataConstructor: (data) { + if (data is List) { + return data + .map((e) => RunningRace.fromJson(Map.from(e))) + .toList(); + } + return []; + }, + ); } } diff --git a/lib/src/data/repository/repository_segment_efforts_impl.dart b/lib/src/data/repository/repository_segment_efforts_impl.dart index c7cf803..7d8c265 100644 --- a/lib/src/data/repository/repository_segment_efforts_impl.dart +++ b/lib/src/data/repository/repository_segment_efforts_impl.dart @@ -6,31 +6,40 @@ class RepositorySegmentEffortImpl extends RepositorySegmentEffort { @override Future getSegmentEffort(int segmentId) { return ApiClient.getRequest( - endPoint: "/v3/segment_efforts/$segmentId", - dataConstructor: (data) => - DetailedSegmentEffort.fromJson(Map.from(data))); + endPoint: "/v3/segment_efforts/$segmentId", + dataConstructor: (data) => + DetailedSegmentEffort.fromJson(Map.from(data)), + ); } @override Future> listSegmentEfforts( - int segmentId, DateTime startDate, DateTime endDate, int perPage) { + int segmentId, + DateTime startDate, + DateTime endDate, + int perPage, + ) { var queryParams = { "segment_id": segmentId, "start_date_local": startDate.toIso8601String(), "end_date_local": endDate.toIso8601String(), - "per_page": perPage + "per_page": perPage, }; return ApiClient.getRequest( - endPoint: "/v3/segment_efforts", - queryParameters: queryParams, - dataConstructor: (data) { - if (data is List) { - return data - .map((e) => DetailedSegmentEffort.fromJson( - Map.from(e))) - .toList(); - } - return []; - }); + endPoint: "/v3/segment_efforts", + queryParameters: queryParams, + dataConstructor: (data) { + if (data is List) { + return data + .map( + (e) => DetailedSegmentEffort.fromJson( + Map.from(e), + ), + ) + .toList(); + } + return []; + }, + ); } } diff --git a/lib/src/data/repository/repository_segment_impl.dart b/lib/src/data/repository/repository_segment_impl.dart index 88cf727..5e811bd 100644 --- a/lib/src/data/repository/repository_segment_impl.dart +++ b/lib/src/data/repository/repository_segment_impl.dart @@ -6,69 +6,74 @@ import 'package:strava_client/src/domain/repository/repository_segment.dart'; class RepositorySegmentImpl extends RepositorySegment { @override Future exploreSegments( - GeoPoint southWestCorner, - GeoPoint northEastCorner, - ActivityTypeEnum typeEnum, - int minClimbingCategory, - int maxClimbingCategory) { + GeoPoint southWestCorner, + GeoPoint northEastCorner, + ActivityTypeEnum typeEnum, + int minClimbingCategory, + int maxClimbingCategory, + ) { var queryParams = { "bounds": [ southWestCorner.lat, southWestCorner.lon, northEastCorner.lat, - northEastCorner.lon + northEastCorner.lon, ], "activity_type": typeEnum.stringValue(), "min_cat": minClimbingCategory, - "max_cat": maxClimbingCategory + "max_cat": maxClimbingCategory, }; return ApiClient.getRequest( - endPoint: "/v3/segments/explore", - queryParameters: queryParams, - dataConstructor: (data) => - ExplorerResponse.fromJson(Map.from(data))); + endPoint: "/v3/segments/explore", + queryParameters: queryParams, + dataConstructor: (data) => + ExplorerResponse.fromJson(Map.from(data)), + ); } @override Future> listStarredSegments(int page, int perPage) { return ApiClient.getRequest( - endPoint: "/v3/segments/starred", - queryParameters: {"page": page, "per_page": perPage}, - dataConstructor: (data) { - if (data is List) { - return data - .map((e) => - SummarySegment.fromJson(Map.from(e))) - .toList(); - } - return []; - }); + endPoint: "/v3/segments/starred", + queryParameters: {"page": page, "per_page": perPage}, + dataConstructor: (data) { + if (data is List) { + return data + .map((e) => SummarySegment.fromJson(Map.from(e))) + .toList(); + } + return []; + }, + ); } @override Future getSegment(int segmentId) { return ApiClient.getRequest( - endPoint: "/v3/segments/$segmentId", - dataConstructor: (data) => - DetailedSegment.fromJson(Map.from(data))); + endPoint: "/v3/segments/$segmentId", + dataConstructor: (data) => + DetailedSegment.fromJson(Map.from(data)), + ); } @override Future starSegment(int segmentId, bool isStarred) { FormData formData = FormData.fromMap({"starred": isStarred}); return ApiClient.putRequest( - endPoint: "/v3/segments/$segmentId/starred", - postBody: formData, - dataConstructor: (data) => - DetailedSegment.fromJson(Map.from(data))); + endPoint: "/v3/segments/$segmentId/starred", + postBody: formData, + dataConstructor: (data) => + DetailedSegment.fromJson(Map.from(data)), + ); } @override Future getLeaderBoard(SegmentLeaderboardRequest request) { return ApiClient.getRequest( - endPoint: "/v3/segments/${request.segmentId}/leaderboard", - queryParameters: request.toJson(), - dataConstructor: (data) => - SegmentLeaderboard.fromJson(Map.from(data))); + endPoint: "/v3/segments/${request.segmentId}/leaderboard", + queryParameters: request.toJson(), + dataConstructor: (data) => + SegmentLeaderboard.fromJson(Map.from(data)), + ); } } diff --git a/lib/src/data/repository/repository_stream_impl.dart b/lib/src/data/repository/repository_stream_impl.dart index a41679d..7236ecf 100644 --- a/lib/src/data/repository/repository_stream_impl.dart +++ b/lib/src/data/repository/repository_stream_impl.dart @@ -22,70 +22,90 @@ class RepositoryStreamImpl extends RepositoryStream { @override Future> getActivityStreams( - int activityId, List keys) { + int activityId, + List keys, + ) { return ApiClient.getRequest( - endPoint: "/v3/activities/$activityId/streams", - queryParameters: {"keys": keys, "key_by_type": false}, - dataConstructor: _parseList); + endPoint: "/v3/activities/$activityId/streams", + queryParameters: {"keys": keys, "key_by_type": false}, + dataConstructor: _parseList, + ); } @override Future> getRouteStreams(int routeId) { return ApiClient.getRequest( - endPoint: "/v3/routes/$routeId/streams", - dataConstructor: _parseList); + endPoint: "/v3/routes/$routeId/streams", + dataConstructor: _parseList, + ); } @override Future> getSegmentEffortStreams( - int segmentEffortId, List keys) { + int segmentEffortId, + List keys, + ) { return ApiClient.getRequest( - endPoint: "/v3/segment_efforts/$segmentEffortId/streams", - queryParameters: {"keys": keys, "key_by_type": false}, - dataConstructor: _parseList); + endPoint: "/v3/segment_efforts/$segmentEffortId/streams", + queryParameters: {"keys": keys, "key_by_type": false}, + dataConstructor: _parseList, + ); } @override Future> getSegmentStreams( - int segmentId, List keys) { + int segmentId, + List keys, + ) { return ApiClient.getRequest( - endPoint: "/v3/segments/$segmentId/streams", - queryParameters: {"keys": keys, "key_by_type": false}, - dataConstructor: _parseList); + endPoint: "/v3/segments/$segmentId/streams", + queryParameters: {"keys": keys, "key_by_type": false}, + dataConstructor: _parseList, + ); } @override Future getActivityStreamsByType( - int activityId, List keys) { + int activityId, + List keys, + ) { return ApiClient.getRequest( - endPoint: "/v3/activities/$activityId/streams", - queryParameters: {"keys": keys, "key_by_type": true}, - dataConstructor: _parseCollection); + endPoint: "/v3/activities/$activityId/streams", + queryParameters: {"keys": keys, "key_by_type": true}, + dataConstructor: _parseCollection, + ); } @override Future getRouteStreamsByType(int routeId) { return ApiClient.getRequest( - endPoint: "/v3/routes/$routeId/streams", - queryParameters: {"key_by_type": true}, - dataConstructor: _parseCollection); + endPoint: "/v3/routes/$routeId/streams", + queryParameters: {"key_by_type": true}, + dataConstructor: _parseCollection, + ); } @override Future getSegmentEffortStreamsByType( - int segmentEffortId, List keys) { + int segmentEffortId, + List keys, + ) { return ApiClient.getRequest( - endPoint: "/v3/segment_efforts/$segmentEffortId/streams", - queryParameters: {"keys": keys, "key_by_type": true}, - dataConstructor: _parseCollection); + endPoint: "/v3/segment_efforts/$segmentEffortId/streams", + queryParameters: {"keys": keys, "key_by_type": true}, + dataConstructor: _parseCollection, + ); } @override Future getSegmentStreamsByType( - int segmentId, List keys) { + int segmentId, + List keys, + ) { return ApiClient.getRequest( - endPoint: "/v3/segments/$segmentId/streams", - queryParameters: {"keys": keys, "key_by_type": true}, - dataConstructor: _parseCollection); + endPoint: "/v3/segments/$segmentId/streams", + queryParameters: {"keys": keys, "key_by_type": true}, + dataConstructor: _parseCollection, + ); } } diff --git a/lib/src/data/repository/repository_upload_impl.dart b/lib/src/data/repository/repository_upload_impl.dart index cd21949..c8edbe1 100644 --- a/lib/src/data/repository/repository_upload_impl.dart +++ b/lib/src/data/repository/repository_upload_impl.dart @@ -7,9 +7,10 @@ class RepositoryUploadImpl extends RepositoryUpload { @override Future getUpload(int uploadId) { return ApiClient.getRequest( - endPoint: "/v3/uploads/$uploadId", - dataConstructor: (data) => - UploadResponse.fromJson(Map.from(data))); + endPoint: "/v3/uploads/$uploadId", + dataConstructor: (data) => + UploadResponse.fromJson(Map.from(data)), + ); } @override @@ -20,9 +21,10 @@ class RepositoryUploadImpl extends RepositoryUpload { formData.files.add(MapEntry("file", multipartFile)); } return ApiClient.postRequest( - endPoint: "/v3/uploads", - postBody: formData, - dataConstructor: (data) => - UploadResponse.fromJson(Map.from(data))); + endPoint: "/v3/uploads", + postBody: formData, + dataConstructor: (data) => + UploadResponse.fromJson(Map.from(data)), + ); } } diff --git a/lib/src/domain/model/model_activity_request_create.dart b/lib/src/domain/model/model_activity_request_create.dart index 1168af6..74f2822 100644 --- a/lib/src/domain/model/model_activity_request_create.dart +++ b/lib/src/domain/model/model_activity_request_create.dart @@ -12,23 +12,24 @@ class CreateActivityRequest { bool isCommuteActivity; CreateActivityRequest( - this.name, - this.type, - this.startDateLocal, - this.elapsedTimeInSeconds, - this.description, - this.distanceInMeters, - this.isTrainerActivity, - this.isCommuteActivity); + this.name, + this.type, + this.startDateLocal, + this.elapsedTimeInSeconds, + this.description, + this.distanceInMeters, + this.isTrainerActivity, + this.isCommuteActivity, + ); Map toJson() => { - "name": name, - "description": description, - "type": type.stringValue(), - "start_date_local": startDateLocal.toIso8601String(), - "elapsed_time": elapsedTimeInSeconds, - "distance": distanceInMeters, - "trainer": isTrainerActivity ? 1 : 0, - "commute": isCommuteActivity ? 1 : 0 - }; + "name": name, + "description": description, + "type": type.stringValue(), + "start_date_local": startDateLocal.toIso8601String(), + "elapsed_time": elapsedTimeInSeconds, + "distance": distanceInMeters, + "trainer": isTrainerActivity ? 1 : 0, + "commute": isCommuteActivity ? 1 : 0, + }; } diff --git a/lib/src/domain/model/model_activity_request_update.dart b/lib/src/domain/model/model_activity_request_update.dart index 6a6cc57..2be8643 100644 --- a/lib/src/domain/model/model_activity_request_update.dart +++ b/lib/src/domain/model/model_activity_request_update.dart @@ -21,12 +21,12 @@ class UpdateActivityRequest { }); Map toJson() => { - if (name != null) "name": name, - if (description != null) "description": description, - if (type != null) "type": type!.stringValue(), - if (isTrainerActivity != null) "trainer": isTrainerActivity! ? 1 : 0, - if (isCommuteActivity != null) "commute": isCommuteActivity! ? 1 : 0, - if (gearId != null) "gear_id": gearId, - if (hideFromHome != null) "hide_from_home": hideFromHome! ? 1 : 0, - }; + if (name != null) "name": name, + if (description != null) "description": description, + if (type != null) "type": type!.stringValue(), + if (isTrainerActivity != null) "trainer": isTrainerActivity! ? 1 : 0, + if (isCommuteActivity != null) "commute": isCommuteActivity! ? 1 : 0, + if (gearId != null) "gear_id": gearId, + if (hideFromHome != null) "hide_from_home": hideFromHome! ? 1 : 0, + }; } diff --git a/lib/src/domain/model/model_activity_stats.dart b/lib/src/domain/model/model_activity_stats.dart index e5f1e50..ad4ed2c 100644 --- a/lib/src/domain/model/model_activity_stats.dart +++ b/lib/src/domain/model/model_activity_stats.dart @@ -10,8 +10,7 @@ part 'model_activity_stats.g.dart'; /// Preserves the legacy behavior of defaulting to `0` when the /// biggest_ride_distance field is absent or null in the JSON payload. -double? _biggestRideDistanceFromJson(dynamic value) => - value?.toDouble() ?? 0; +double? _biggestRideDistanceFromJson(dynamic value) => value?.toDouble() ?? 0; /// Preserves the legacy behavior of defaulting to `0` when the /// biggest_climb_elevation_gain field is absent or null in the JSON payload. @@ -48,7 +47,10 @@ class ActivityStats { ActivityTotal? recentSwimTotals; /// The longest distance ridden by the athlete. - @JsonKey(name: "biggest_ride_distance", fromJson: _biggestRideDistanceFromJson) + @JsonKey( + name: "biggest_ride_distance", + fromJson: _biggestRideDistanceFromJson, + ) double? biggestRideDistance; /// The year to date swim stats for the athlete. @@ -65,8 +67,9 @@ class ActivityStats { /// The highest climb ridden by the athlete. @JsonKey( - name: "biggest_climb_elevation_gain", - fromJson: _biggestClimbElevationGainFromJson) + name: "biggest_climb_elevation_gain", + fromJson: _biggestClimbElevationGainFromJson, + ) double? biggestClimbElevationGain; /// The year to date ride stats for the athlete. diff --git a/lib/src/domain/model/model_activity_type_enum.dart b/lib/src/domain/model/model_activity_type_enum.dart index 1d5170f..8c14797 100644 --- a/lib/src/domain/model/model_activity_type_enum.dart +++ b/lib/src/domain/model/model_activity_type_enum.dart @@ -42,14 +42,15 @@ enum ActivityTypeEnum { Wheelchair, Windsurf, Workout, - Yoga + Yoga, } extension ActivityTypeEnumHelper on ActivityTypeEnum { static ActivityTypeEnum getType(String value) { return ActivityTypeEnum.values.firstWhere( - (element) => element.toString().endsWith(value), - orElse: () => ActivityTypeEnum.undefined); + (element) => element.toString().endsWith(value), + orElse: () => ActivityTypeEnum.undefined, + ); } String stringValue() { diff --git a/lib/src/domain/model/model_activity_zone.dart b/lib/src/domain/model/model_activity_zone.dart index 05679bf..55c7fb5 100644 --- a/lib/src/domain/model/model_activity_zone.dart +++ b/lib/src/domain/model/model_activity_zone.dart @@ -38,14 +38,15 @@ class ActivityZone { @JsonKey(name: "points") int? points; - ActivityZone( - {this.score, - this.sensorBased, - this.customZones, - this.max, - this.distributionBuckets, - this.type, - this.points}); + ActivityZone({ + this.score, + this.sensorBased, + this.customZones, + this.max, + this.distributionBuckets, + this.type, + this.points, + }); factory ActivityZone.fromJson(Map json) => _$ActivityZoneFromJson(json); diff --git a/lib/src/domain/model/model_authentication_response.dart b/lib/src/domain/model/model_authentication_response.dart index a2b082b..312134c 100644 --- a/lib/src/domain/model/model_authentication_response.dart +++ b/lib/src/domain/model/model_authentication_response.dart @@ -10,14 +10,15 @@ part 'model_authentication_response.g.dart'; @JsonSerializable() class TokenResponse { - TokenResponse( - {required this.tokenType, - required this.expiresAt, - required this.expiresIn, - required this.refreshToken, - required this.accessToken, - this.athlete, - this.scopes}); + TokenResponse({ + required this.tokenType, + required this.expiresAt, + required this.expiresIn, + required this.refreshToken, + required this.accessToken, + this.athlete, + this.scopes, + }); @JsonKey(name: "token_type") String tokenType; diff --git a/lib/src/domain/model/model_authentication_scopes.dart b/lib/src/domain/model/model_authentication_scopes.dart index 5631d87..877c0f6 100644 --- a/lib/src/domain/model/model_authentication_scopes.dart +++ b/lib/src/domain/model/model_authentication_scopes.dart @@ -27,7 +27,7 @@ enum AuthenticationScope { /// activities that are visible to the app, based on activity read access /// level. activity_write, - undefined_scope + undefined_scope, } extension AuthenticationScopeHelper on AuthenticationScope { @@ -54,8 +54,9 @@ extension AuthenticationScopeHelper on AuthenticationScope { static AuthenticationScope getAuthScope(String value) { return AuthenticationScope.values.firstWhere( - (e) => e.toScopeString() == value, - orElse: () => AuthenticationScope.undefined_scope); + (e) => e.toScopeString() == value, + orElse: () => AuthenticationScope.undefined_scope, + ); } /// Builds comma separated scope string for strava. diff --git a/lib/src/domain/model/model_club.dart b/lib/src/domain/model/model_club.dart index e68d702..b920c18 100644 --- a/lib/src/domain/model/model_club.dart +++ b/lib/src/domain/model/model_club.dart @@ -116,31 +116,32 @@ class Club { @JsonKey(name: "following_count") int? followingCount; - Club( - {this.id, - this.resourceState, - this.name, - this.profileMedium, - this.profile, - this.coverPhoto, - this.coverPhotoSmall, - this.sportType, - this.city, - this.state, - this.country, - this.private, - this.memberCount, - this.featured, - this.verified, - this.url, - this.membership, - this.admin, - this.owner, - this.description, - this.clubType, - this.postCount, - this.ownerId, - this.followingCount}); + Club({ + this.id, + this.resourceState, + this.name, + this.profileMedium, + this.profile, + this.coverPhoto, + this.coverPhotoSmall, + this.sportType, + this.city, + this.state, + this.country, + this.private, + this.memberCount, + this.featured, + this.verified, + this.url, + this.membership, + this.admin, + this.owner, + this.description, + this.clubType, + this.postCount, + this.ownerId, + this.followingCount, + }); factory Club.fromJson(Map json) => _$ClubFromJson(json); diff --git a/lib/src/domain/model/model_comment.dart b/lib/src/domain/model/model_comment.dart index d75e948..0f76fc0 100644 --- a/lib/src/domain/model/model_comment.dart +++ b/lib/src/domain/model/model_comment.dart @@ -38,15 +38,16 @@ class Comment { @JsonKey(name: "athlete", includeIfNull: false) SummaryAthlete? athlete; - Comment( - {this.id, - this.activityId, - this.postId, - this.resourceState, - this.text, - this.mentionsMetadata, - this.createdAt, - this.athlete}); + Comment({ + this.id, + this.activityId, + this.postId, + this.resourceState, + this.text, + this.mentionsMetadata, + this.createdAt, + this.athlete, + }); factory Comment.fromJson(Map json) => _$CommentFromJson(json); diff --git a/lib/src/domain/model/model_detailed_activity.dart b/lib/src/domain/model/model_detailed_activity.dart index 8fe8176..0555720 100644 --- a/lib/src/domain/model/model_detailed_activity.dart +++ b/lib/src/domain/model/model_detailed_activity.dart @@ -218,72 +218,73 @@ class DetailedActivity { @JsonKey(name: "splits_standard", includeIfNull: false) List? splitsStandard; - DetailedActivity( - {this.id, - this.resourceState, - this.externalId, - this.uploadId, - this.athlete, - this.name, - this.distance, - this.movingTime, - this.elapsedTime, - this.totalElevationGain, - this.type, - this.startDate, - this.startDateLocal, - this.timezone, - this.utcOffset, - this.startLatlng, - this.endLatlng, - this.achievementCount, - this.kudosCount, - this.commentCount, - this.athleteCount, - this.photoCount, - this.map, - this.trainer, - this.commute, - this.manual, - this.private, - this.flagged, - this.gearId, - this.fromAcceptedTag, - this.averageSpeed, - this.maxSpeed, - this.averageCadence, - this.averageTemp, - this.averageWatts, - this.weightedAverageWatts, - this.kilojoules, - this.deviceWatts, - this.hasHeartrate, - this.maxWatts, - this.elevHigh, - this.elevLow, - this.prCount, - this.totalPhotoCount, - this.hasKudoed, - this.workoutType, - this.sufferScore, - this.description, - this.calories, - this.segmentEfforts, - this.splitsMetric, - this.laps, - this.gear, - this.partnerBrandTag, - this.photos, - this.highlightedKudosers, - this.deviceName, - this.embedToken, - this.segmentLeaderboardOptOut, - this.leaderboardOptOut, - this.sportType, - this.hideFromHome, - this.uploadIdStr, - this.bestEfforts, - this.splitsStandard}); + DetailedActivity({ + this.id, + this.resourceState, + this.externalId, + this.uploadId, + this.athlete, + this.name, + this.distance, + this.movingTime, + this.elapsedTime, + this.totalElevationGain, + this.type, + this.startDate, + this.startDateLocal, + this.timezone, + this.utcOffset, + this.startLatlng, + this.endLatlng, + this.achievementCount, + this.kudosCount, + this.commentCount, + this.athleteCount, + this.photoCount, + this.map, + this.trainer, + this.commute, + this.manual, + this.private, + this.flagged, + this.gearId, + this.fromAcceptedTag, + this.averageSpeed, + this.maxSpeed, + this.averageCadence, + this.averageTemp, + this.averageWatts, + this.weightedAverageWatts, + this.kilojoules, + this.deviceWatts, + this.hasHeartrate, + this.maxWatts, + this.elevHigh, + this.elevLow, + this.prCount, + this.totalPhotoCount, + this.hasKudoed, + this.workoutType, + this.sufferScore, + this.description, + this.calories, + this.segmentEfforts, + this.splitsMetric, + this.laps, + this.gear, + this.partnerBrandTag, + this.photos, + this.highlightedKudosers, + this.deviceName, + this.embedToken, + this.segmentLeaderboardOptOut, + this.leaderboardOptOut, + this.sportType, + this.hideFromHome, + this.uploadIdStr, + this.bestEfforts, + this.splitsStandard, + }); factory DetailedActivity.fromJson(Map json) => _$DetailedActivityFromJson(json); @@ -310,8 +311,12 @@ class HighlightedKudosers { @JsonKey(name: "show_name") bool? showName; - HighlightedKudosers( - {this.destinationUrl, this.displayName, this.avatarUrl, this.showName}); + HighlightedKudosers({ + this.destinationUrl, + this.displayName, + this.avatarUrl, + this.showName, + }); factory HighlightedKudosers.fromJson(Map json) => _$HighlightedKudosersFromJson(json); @@ -444,14 +449,15 @@ class SplitsMetric { @JsonKey(name: "pace_zone") int? paceZone; - SplitsMetric( - {this.distance, - this.elapsedTime, - this.elevationDifference, - this.movingTime, - this.split, - this.averageSpeed, - this.paceZone}); + SplitsMetric({ + this.distance, + this.elapsedTime, + this.elevationDifference, + this.movingTime, + this.split, + this.averageSpeed, + this.paceZone, + }); factory SplitsMetric.fromJson(Map json) => _$SplitsMetricFromJson(json); @@ -553,31 +559,32 @@ class DetailedSegmentEffort { @JsonKey(name: "is_kom") bool? isKom; - DetailedSegmentEffort( - {this.id, - this.resourceState, - this.name, - this.activity, - this.athlete, - this.elapsedTime, - this.movingTime, - this.startDate, - this.startDateLocal, - this.distance, - this.startIndex, - this.endIndex, - this.averageCadence, - this.deviceWatts, - this.averageWatts, - this.segment, - this.komRank, - this.prRank, - this.achievements, - this.hidden, - this.activityId, - this.averageHeartrate, - this.maxHeartrate, - this.isKom}); + DetailedSegmentEffort({ + this.id, + this.resourceState, + this.name, + this.activity, + this.athlete, + this.elapsedTime, + this.movingTime, + this.startDate, + this.startDateLocal, + this.distance, + this.startIndex, + this.endIndex, + this.averageCadence, + this.deviceWatts, + this.averageWatts, + this.segment, + this.komRank, + this.prRank, + this.achievements, + this.hidden, + this.activityId, + this.averageHeartrate, + this.maxHeartrate, + this.isKom, + }); factory DetailedSegmentEffort.fromJson(Map json) => _$DetailedSegmentEffortFromJson(json); @@ -664,25 +671,26 @@ class Segment { @JsonKey(name: "starred") bool? starred; - Segment( - {this.id, - this.resourceState, - this.name, - this.activityType, - this.distance, - this.averageGrade, - this.maximumGrade, - this.elevationHigh, - this.elevationLow, - this.startLatlng, - this.endLatlng, - this.climbCategory, - this.city, - this.state, - this.country, - this.private, - this.hazardous, - this.starred}); + Segment({ + this.id, + this.resourceState, + this.name, + this.activityType, + this.distance, + this.averageGrade, + this.maximumGrade, + this.elevationHigh, + this.elevationLow, + this.startLatlng, + this.endLatlng, + this.climbCategory, + this.city, + this.state, + this.country, + this.private, + this.hazardous, + this.starred, + }); factory Segment.fromJson(Map json) => _$SegmentFromJson(json); @@ -708,8 +716,12 @@ class PolyLineMap { @JsonKey(name: "summary_polyline") String? summaryPolyline; - PolyLineMap( - {this.id, this.polyline, this.resourceState, this.summaryPolyline}); + PolyLineMap({ + this.id, + this.polyline, + this.resourceState, + this.summaryPolyline, + }); factory PolyLineMap.fromJson(Map json) => _$PolyLineMapFromJson(json); diff --git a/lib/src/domain/model/model_detailed_athlete.dart b/lib/src/domain/model/model_detailed_athlete.dart index 2ee65c1..f093d71 100644 --- a/lib/src/domain/model/model_detailed_athlete.dart +++ b/lib/src/domain/model/model_detailed_athlete.dart @@ -27,8 +27,9 @@ List _clubsFromJson(dynamic value) { clubs = []; debugPrint("Exception: $exception"); debugPrintStack( - stackTrace: stackTrace, - label: "An error occurred while serializing summary club json"); + stackTrace: stackTrace, + label: "An error occurred while serializing summary club json", + ); } } return clubs; @@ -38,42 +39,42 @@ List _clubsFromJson(dynamic value) { /// gear field (`bikes` / `shoes`) is absent from the JSON payload. List _gearFromJson(dynamic value) => value == null ? [] - : List.from( - value.map((x) => SummaryGear.fromJson(x))); + : List.from(value.map((x) => SummaryGear.fromJson(x))); @JsonSerializable() class DetailedAthlete { - DetailedAthlete( - {required this.id, - required this.username, - required this.resourceState, - required this.firstname, - required this.lastname, - required this.city, - required this.state, - required this.country, - required this.sex, - required this.premium, - required this.createdAt, - required this.updatedAt, - required this.badgeTypeId, - required this.profileMedium, - required this.profile, - required this.friend, - required this.follower, - required this.followerCount, - required this.friendCount, - required this.mutualFriendCount, - required this.athleteType, - required this.datePreference, - required this.measurementPreference, - required this.clubs, - required this.ftp, - required this.weight, - required this.bikes, - required this.shoes, - this.bio, - this.summit}); + DetailedAthlete({ + required this.id, + required this.username, + required this.resourceState, + required this.firstname, + required this.lastname, + required this.city, + required this.state, + required this.country, + required this.sex, + required this.premium, + required this.createdAt, + required this.updatedAt, + required this.badgeTypeId, + required this.profileMedium, + required this.profile, + required this.friend, + required this.follower, + required this.followerCount, + required this.friendCount, + required this.mutualFriendCount, + required this.athleteType, + required this.datePreference, + required this.measurementPreference, + required this.clubs, + required this.ftp, + required this.weight, + required this.bikes, + required this.shoes, + this.bio, + this.summit, + }); @JsonKey(name: "id") int id; diff --git a/lib/src/domain/model/model_detailed_segment.dart b/lib/src/domain/model/model_detailed_segment.dart index 9b3a9fc..3844323 100644 --- a/lib/src/domain/model/model_detailed_segment.dart +++ b/lib/src/domain/model/model_detailed_segment.dart @@ -122,43 +122,45 @@ class DetailedSegment { int? starCount; @JsonKey( - name: "athlete_segment_stats", - includeIfNull: false, - readValue: _athleteSegmentStatsReadValue) + name: "athlete_segment_stats", + includeIfNull: false, + readValue: _athleteSegmentStatsReadValue, + ) SummaryPRSegmentEffort? athleteSegmentStats; /// The authenticated athlete's PR effort on this segment. @JsonKey(name: "athlete_pr_effort", includeIfNull: false) SummaryPRSegmentEffort? athletePrEffort; - DetailedSegment( - {this.id, - this.resourceState, - this.name, - this.activityType, - this.distance, - this.averageGrade, - this.maximumGrade, - this.elevationHigh, - this.elevationLow, - this.startLatlng, - this.endLatlng, - this.climbCategory, - this.city, - this.state, - this.country, - this.private, - this.hazardous, - this.starred, - this.createdAt, - this.updatedAt, - this.totalElevationGain, - this.map, - this.effortCount, - this.athleteCount, - this.starCount, - this.athleteSegmentStats, - this.athletePrEffort}); + DetailedSegment({ + this.id, + this.resourceState, + this.name, + this.activityType, + this.distance, + this.averageGrade, + this.maximumGrade, + this.elevationHigh, + this.elevationLow, + this.startLatlng, + this.endLatlng, + this.climbCategory, + this.city, + this.state, + this.country, + this.private, + this.hazardous, + this.starred, + this.createdAt, + this.updatedAt, + this.totalElevationGain, + this.map, + this.effortCount, + this.athleteCount, + this.starCount, + this.athleteSegmentStats, + this.athletePrEffort, + }); factory DetailedSegment.fromJson(Map json) => _$DetailedSegmentFromJson(json); diff --git a/lib/src/domain/model/model_fault.dart b/lib/src/domain/model/model_fault.dart index f6fb8d6..d62ba8b 100644 --- a/lib/src/domain/model/model_fault.dart +++ b/lib/src/domain/model/model_fault.dart @@ -18,10 +18,7 @@ List _errorsToJson(List? errors) => @JsonSerializable() class Fault { - Fault({ - this.errors, - this.message, - }); + Fault({this.errors, this.message}); @JsonKey(name: "errors", fromJson: _errorsFromJson, toJson: _errorsToJson) final List? errors; @@ -40,11 +37,7 @@ class Fault { @JsonSerializable() class Error { - Error({ - this.code, - this.field, - this.resource, - }); + Error({this.code, this.field, this.resource}); @JsonKey(name: "code") final String? code; diff --git a/lib/src/domain/model/model_gear.dart b/lib/src/domain/model/model_gear.dart index a1817dd..9784fa2 100644 --- a/lib/src/domain/model/model_gear.dart +++ b/lib/src/domain/model/model_gear.dart @@ -39,15 +39,16 @@ class Gear { @JsonKey(name: "description") String? description; - Gear( - {this.id, - this.primary, - this.resourceState, - this.distance, - this.brandName, - this.modelName, - this.frameType, - this.description}); + Gear({ + this.id, + this.primary, + this.resourceState, + this.distance, + this.brandName, + this.modelName, + this.frameType, + this.description, + }); factory Gear.fromJson(Map json) => _$GearFromJson(json); diff --git a/lib/src/domain/model/model_lap.dart b/lib/src/domain/model/model_lap.dart index ec7d2d9..0aff556 100644 --- a/lib/src/domain/model/model_lap.dart +++ b/lib/src/domain/model/model_lap.dart @@ -72,28 +72,29 @@ class Lap { @JsonKey(name: "pace_zone") int? paceZone; - Lap( - {this.id, - this.resourceState, - this.name, - this.activity, - this.athlete, - this.elapsedTime, - this.movingTime, - this.startDate, - this.startDateLocal, - this.distance, - this.startIndex, - this.endIndex, - this.totalElevationGain, - this.averageSpeed, - this.maxSpeed, - this.averageCadence, - this.deviceWatts, - this.averageWatts, - this.lapIndex, - this.split, - this.paceZone}); + Lap({ + this.id, + this.resourceState, + this.name, + this.activity, + this.athlete, + this.elapsedTime, + this.movingTime, + this.startDate, + this.startDateLocal, + this.distance, + this.startIndex, + this.endIndex, + this.totalElevationGain, + this.averageSpeed, + this.maxSpeed, + this.averageCadence, + this.deviceWatts, + this.averageWatts, + this.lapIndex, + this.split, + this.paceZone, + }); factory Lap.fromJson(Map json) => _$LapFromJson(json); diff --git a/lib/src/domain/model/model_route.dart b/lib/src/domain/model/model_route.dart index 061569d..ec4d776 100644 --- a/lib/src/domain/model/model_route.dart +++ b/lib/src/domain/model/model_route.dart @@ -84,25 +84,26 @@ class Route { @JsonKey(name: "waypoints", includeIfNull: false) List? waypoints; - Route( - {this.private, - this.distance, - this.athlete, - this.description, - this.createdAt, - this.elevationGain, - this.type, - this.estimatedMovingTime, - this.segments, - this.starred, - this.updatedAt, - this.subType, - this.idStr, - this.name, - this.id, - this.map, - this.timestamp, - this.waypoints}); + Route({ + this.private, + this.distance, + this.athlete, + this.description, + this.createdAt, + this.elevationGain, + this.type, + this.estimatedMovingTime, + this.segments, + this.starred, + this.updatedAt, + this.subType, + this.idStr, + this.name, + this.id, + this.map, + this.timestamp, + this.waypoints, + }); factory Route.fromJson(Map json) => _$RouteFromJson(json); @@ -137,13 +138,14 @@ class Waypoint { @JsonKey(name: "distance_into_route") double? distanceIntoRoute; - Waypoint( - {this.latlng, - this.targetLatlng, - this.categories, - this.title, - this.description, - this.distanceIntoRoute}); + Waypoint({ + this.latlng, + this.targetLatlng, + this.categories, + this.title, + this.description, + this.distanceIntoRoute, + }); factory Waypoint.fromJson(Map json) => _$WaypointFromJson(json); diff --git a/lib/src/domain/model/model_running_race.dart b/lib/src/domain/model/model_running_race.dart index 7d8e052..2d9ffb0 100644 --- a/lib/src/domain/model/model_running_race.dart +++ b/lib/src/domain/model/model_running_race.dart @@ -57,19 +57,20 @@ class RunningRace { @JsonKey(name: "url") String? url; - RunningRace( - {this.country, - this.routeIds, - this.runningRaceType, - this.distance, - this.websiteUrl, - this.city, - this.startDateLocal, - this.name, - this.measurementPreference, - this.id, - this.state, - this.url}); + RunningRace({ + this.country, + this.routeIds, + this.runningRaceType, + this.distance, + this.websiteUrl, + this.city, + this.startDateLocal, + this.name, + this.measurementPreference, + this.id, + this.state, + this.url, + }); factory RunningRace.fromJson(Map json) => _$RunningRaceFromJson(json); diff --git a/lib/src/domain/model/model_segment_leaderboard.dart b/lib/src/domain/model/model_segment_leaderboard.dart index 7cbe44c..f8fa860 100644 --- a/lib/src/domain/model/model_segment_leaderboard.dart +++ b/lib/src/domain/model/model_segment_leaderboard.dart @@ -13,8 +13,12 @@ class SegmentLeaderboard { @JsonKey(name: "entries") List? entries; - SegmentLeaderboard( - {this.effortCount, this.entryCount, this.komType, this.entries}); + SegmentLeaderboard({ + this.effortCount, + this.entryCount, + this.komType, + this.entries, + }); factory SegmentLeaderboard.fromJson(Map json) => _$SegmentLeaderboardFromJson(json); @@ -37,13 +41,14 @@ class SegmentLeaderboardEntry { @JsonKey(name: "rank") int? rank; - SegmentLeaderboardEntry( - {this.athleteName, - this.elapsedTime, - this.movingTime, - this.startDate, - this.startDateLocal, - this.rank}); + SegmentLeaderboardEntry({ + this.athleteName, + this.elapsedTime, + this.movingTime, + this.startDate, + this.startDateLocal, + this.rank, + }); factory SegmentLeaderboardEntry.fromJson(Map json) => _$SegmentLeaderboardEntryFromJson(json); diff --git a/lib/src/domain/model/model_segment_leaderboard_request.dart b/lib/src/domain/model/model_segment_leaderboard_request.dart index 987092a..a4ec7a0 100644 --- a/lib/src/domain/model/model_segment_leaderboard_request.dart +++ b/lib/src/domain/model/model_segment_leaderboard_request.dart @@ -10,31 +10,35 @@ class SegmentLeaderboardRequest { int page; int perPage; SegmentLeaderboardRequest( - this.dateRange, - this.gender, - this.ageGroup, - this.weightClass, - this.segmentId, - this.clubId, - this.maxEntries, - this.isFollowing, - this.page, - this.perPage); + this.dateRange, + this.gender, + this.ageGroup, + this.weightClass, + this.segmentId, + this.clubId, + this.maxEntries, + this.isFollowing, + this.page, + this.perPage, + ); Map toJson() => { - "gender": gender.toString().replaceAll("SegmentGender.", ""), - "age_group": - ageGroup.toString().replaceAll("SegmentAgeGroup.AgeGroup_", ""), - "weight_class": weightClass - .toString() - .replaceAll("SegmentWeightClass_WeightClass_", ""), - "following": isFollowing ? "true" : "false", - "club_id": clubId, - "data_range": dateRange.toString().replaceAll("SegmentDateRange.", ""), - "context_entries": "", - "page": page, - "per_page": perPage - }; + "gender": gender.toString().replaceAll("SegmentGender.", ""), + "age_group": ageGroup.toString().replaceAll( + "SegmentAgeGroup.AgeGroup_", + "", + ), + "weight_class": weightClass.toString().replaceAll( + "SegmentWeightClass_WeightClass_", + "", + ), + "following": isFollowing ? "true" : "false", + "club_id": clubId, + "data_range": dateRange.toString().replaceAll("SegmentDateRange.", ""), + "context_entries": "", + "page": page, + "per_page": perPage, + }; } // String reqLeaderboard = 'https://www.strava.com/api/v3/segments/' + @@ -65,7 +69,7 @@ enum SegmentAgeGroup { AgeGroup_55_64, AgeGroup_65_69, AgeGroup_70_74, - AgeGroup_75_plus + AgeGroup_75_plus, } enum SegmentWeightClass { @@ -84,7 +88,7 @@ enum SegmentWeightClass { WeightClass_85_94, WeightClass_95_104, WeightClass_105_114, - WeightClass_115_plus + WeightClass_115_plus, } enum SegmentDateRange { this_year, this_month, this_week, today } diff --git a/lib/src/domain/model/model_segments_explore.dart b/lib/src/domain/model/model_segments_explore.dart index 90f145e..41cf84c 100644 --- a/lib/src/domain/model/model_segments_explore.dart +++ b/lib/src/domain/model/model_segments_explore.dart @@ -75,19 +75,20 @@ class ExplorerSegment { @JsonKey(name: "starred") bool? starred; - ExplorerSegment( - {this.id, - this.resourceState, - this.name, - this.climbCategory, - this.climbCategoryDesc, - this.avgGrade, - this.startLatlng, - this.endLatlng, - this.elevDifference, - this.distance, - this.points, - this.starred}); + ExplorerSegment({ + this.id, + this.resourceState, + this.name, + this.climbCategory, + this.climbCategoryDesc, + this.avgGrade, + this.startLatlng, + this.endLatlng, + this.elevDifference, + this.distance, + this.points, + this.starred, + }); factory ExplorerSegment.fromJson(Map json) => _$ExplorerSegmentFromJson(json); diff --git a/lib/src/domain/model/model_stream_set.dart b/lib/src/domain/model/model_stream_set.dart index f5fde82..dc7bdd5 100644 --- a/lib/src/domain/model/model_stream_set.dart +++ b/lib/src/domain/model/model_stream_set.dart @@ -30,12 +30,13 @@ class StravaStream { @JsonKey(name: "resolution") String? resolution; - StravaStream( - {this.type, - this.data, - this.seriesType, - this.originalSize, - this.resolution}); + StravaStream({ + this.type, + this.data, + this.seriesType, + this.originalSize, + this.resolution, + }); factory StravaStream.fromJson(Map json) => _$StravaStreamFromJson(json); @@ -49,6 +50,8 @@ class StravaStream { /// of streams. The keyed object that Strava calls a "StreamSet" is now modeled /// by [StreamCollection]. This alias is kept for source compatibility and will /// be removed in the next major version. -@Deprecated('Renamed to StravaStream. A single stream is not a "set". ' - 'For the keyed object use StreamCollection. Removed in the next major.') +@Deprecated( + 'Renamed to StravaStream. A single stream is not a "set". ' + 'For the keyed object use StreamCollection. Removed in the next major.', +) typedef StreamSet = StravaStream; diff --git a/lib/src/domain/model/model_summary_activity.dart b/lib/src/domain/model/model_summary_activity.dart index ed2cdc6..741423f 100644 --- a/lib/src/domain/model/model_summary_activity.dart +++ b/lib/src/domain/model/model_summary_activity.dart @@ -186,62 +186,63 @@ class SummaryActivity { @JsonKey(name: "upload_id_str") String? uploadIdStr; - SummaryActivity( - {this.resourceState, - this.athlete, - this.name, - this.distance, - this.movingTime, - this.elapsedTime, - this.totalElevationGain, - this.type, - this.workoutType, - this.id, - this.externalId, - this.uploadId, - this.startDate, - this.startDateLocal, - this.timezone, - this.utcOffset, - this.startLatlng, - this.endLatlng, - this.locationCity, - this.locationState, - this.locationCountry, - this.achievementCount, - this.kudosCount, - this.commentCount, - this.athleteCount, - this.photoCount, - this.map, - this.trainer, - this.commute, - this.manual, - this.private, - this.flagged, - this.gearId, - this.fromAcceptedTag, - this.averageSpeed, - this.maxSpeed, - this.averageCadence, - this.averageWatts, - this.weightedAverageWatts, - this.kilojoules, - this.deviceWatts, - this.hasHeartrate, - this.averageHeartrate, - this.maxHeartrate, - this.maxWatts, - this.prCount, - this.totalPhotoCount, - this.hasKudoed, - this.sufferScore, - this.sportType, - this.elevHigh, - this.elevLow, - this.deviceName, - this.hideFromHome, - this.uploadIdStr}); + SummaryActivity({ + this.resourceState, + this.athlete, + this.name, + this.distance, + this.movingTime, + this.elapsedTime, + this.totalElevationGain, + this.type, + this.workoutType, + this.id, + this.externalId, + this.uploadId, + this.startDate, + this.startDateLocal, + this.timezone, + this.utcOffset, + this.startLatlng, + this.endLatlng, + this.locationCity, + this.locationState, + this.locationCountry, + this.achievementCount, + this.kudosCount, + this.commentCount, + this.athleteCount, + this.photoCount, + this.map, + this.trainer, + this.commute, + this.manual, + this.private, + this.flagged, + this.gearId, + this.fromAcceptedTag, + this.averageSpeed, + this.maxSpeed, + this.averageCadence, + this.averageWatts, + this.weightedAverageWatts, + this.kilojoules, + this.deviceWatts, + this.hasHeartrate, + this.averageHeartrate, + this.maxHeartrate, + this.maxWatts, + this.prCount, + this.totalPhotoCount, + this.hasKudoed, + this.sufferScore, + this.sportType, + this.elevHigh, + this.elevLow, + this.deviceName, + this.hideFromHome, + this.uploadIdStr, + }); factory SummaryActivity.fromJson(Map json) => _$SummaryActivityFromJson(json); diff --git a/lib/src/domain/model/model_summary_segment.dart b/lib/src/domain/model/model_summary_segment.dart index cfefce5..af77140 100644 --- a/lib/src/domain/model/model_summary_segment.dart +++ b/lib/src/domain/model/model_summary_segment.dart @@ -53,24 +53,25 @@ class SummarySegment { int? id; String? state; - SummarySegment( - {this.country, - this.private, - this.distance, - this.averageGrade, - this.maximumGrade, - this.climbCategory, - this.city, - this.elevationHigh, - this.athletePrEffort, - this.athleteSegmentStats, - this.startLatlng, - this.elevationLow, - this.endLatlng, - this.activityType, - this.name, - this.id, - this.state}); + SummarySegment({ + this.country, + this.private, + this.distance, + this.averageGrade, + this.maximumGrade, + this.climbCategory, + this.city, + this.elevationHigh, + this.athletePrEffort, + this.athleteSegmentStats, + this.startLatlng, + this.elevationLow, + this.endLatlng, + this.activityType, + this.name, + this.id, + this.state, + }); SummarySegment.fromJson(dynamic json) { country = json['country']; @@ -138,8 +139,12 @@ class SummaryPRSegmentEffort { @JsonKey(name: "pr_activity_id") int? prActivityId; - SummaryPRSegmentEffort( - {this.prElapsedTime, this.prDate, this.effortCount, this.prActivityId}); + SummaryPRSegmentEffort({ + this.prElapsedTime, + this.prDate, + this.effortCount, + this.prActivityId, + }); factory SummaryPRSegmentEffort.fromJson(Map json) => _$SummaryPRSegmentEffortFromJson(json); @@ -171,14 +176,15 @@ class SummarySegmentEffort { @JsonKey(name: "start_date") String? startDate; - SummarySegmentEffort( - {this.distance, - this.startDateLocal, - this.activityId, - this.elapsedTime, - this.isKom, - this.id, - this.startDate}); + SummarySegmentEffort({ + this.distance, + this.startDateLocal, + this.activityId, + this.elapsedTime, + this.isKom, + this.id, + this.startDate, + }); factory SummarySegmentEffort.fromJson(Map json) => _$SummarySegmentEffortFromJson(json); diff --git a/lib/src/domain/model/model_upload.dart b/lib/src/domain/model/model_upload.dart index 156d36d..cd4525a 100644 --- a/lib/src/domain/model/model_upload.dart +++ b/lib/src/domain/model/model_upload.dart @@ -28,13 +28,14 @@ class UploadResponse { @JsonKey(name: "status") String? status; - UploadResponse( - {this.idStr, - this.activityId, - this.externalId, - this.id, - this.error, - this.status}); + UploadResponse({ + this.idStr, + this.activityId, + this.externalId, + this.id, + this.error, + this.status, + }); factory UploadResponse.fromJson(Map json) => _$UploadResponseFromJson(json); diff --git a/lib/src/domain/model/model_upload_request.dart b/lib/src/domain/model/model_upload_request.dart index 7ffb626..9d2c5c4 100644 --- a/lib/src/domain/model/model_upload_request.dart +++ b/lib/src/domain/model/model_upload_request.dart @@ -33,14 +33,15 @@ class UploadActivityRequest { @JsonKey(name: "external_id") String? externalId; - UploadActivityRequest( - {this.file, - this.name, - this.description, - this.isTrainerActivity, - this.isCommuteActivity, - this.dataType, - this.externalId}); + UploadActivityRequest({ + this.file, + this.name, + this.description, + this.isTrainerActivity, + this.isCommuteActivity, + this.dataType, + this.externalId, + }); factory UploadActivityRequest.fromJson(Map json) => _$UploadActivityRequestFromJson(json); diff --git a/lib/src/domain/repository/repository_athlete.dart b/lib/src/domain/repository/repository_athlete.dart index fa5ff27..938b512 100644 --- a/lib/src/domain/repository/repository_athlete.dart +++ b/lib/src/domain/repository/repository_athlete.dart @@ -16,9 +16,11 @@ abstract class RepositoryAthlete { /// Requires [AuthenticationScope.profile_read_all]. /// /// {@macro fault_management} - @Deprecated('Mismodels GET /athlete/zones (which returns a single ' - '{heart_rate, power} object, not a list). Use getAthleteZones() instead. ' - 'Removed in the next major version.') + @Deprecated( + 'Mismodels GET /athlete/zones (which returns a single ' + '{heart_rate, power} object, not a list). Use getAthleteZones() instead. ' + 'Removed in the next major version.', + ) Future> getZones(); /// Returns the authenticated `athlete`'s heart-rate and power zones as an diff --git a/lib/src/domain/repository/repository_gear.dart b/lib/src/domain/repository/repository_gear.dart index d3e6f17..27f478f 100644 --- a/lib/src/domain/repository/repository_gear.dart +++ b/lib/src/domain/repository/repository_gear.dart @@ -5,8 +5,10 @@ abstract class RepositoryGear { /// Returns a [Gear] from its [gearId]. /// /// {@macro fault_management} - @Deprecated('Gear ids are strings (e.g. "b1234567"), not ints. ' - 'Use getGearById(String) instead. Removed in the next major version.') + @Deprecated( + 'Gear ids are strings (e.g. "b1234567"), not ints. ' + 'Use getGearById(String) instead. Removed in the next major version.', + ) Future getGear(int gearId); /// Returns a [Gear] from its string [gearId] (e.g. `"b1234567"`). diff --git a/lib/src/domain/repository/repository_stream.dart b/lib/src/domain/repository/repository_stream.dart index ca93888..730b611 100644 --- a/lib/src/domain/repository/repository_stream.dart +++ b/lib/src/domain/repository/repository_stream.dart @@ -13,7 +13,9 @@ abstract class RepositoryStream { /// /// {@macro fault_management} Future> getActivityStreams( - int activityId, List keys); + int activityId, + List keys, + ); /// Returns from [routeId] its route's streams as a list of [StravaStream]s. /// @@ -45,7 +47,9 @@ abstract class RepositoryStream { /// /// {@macro fault_management} Future> getSegmentStreams( - int segmentId, List keys); + int segmentId, + List keys, + ); /// Returns from [activityId] its activity's streams as a [StreamCollection] /// (Strava's `key_by_type=true` form), keyed by stream type. @@ -54,7 +58,9 @@ abstract class RepositoryStream { /// /// {@macro fault_management} Future getActivityStreamsByType( - int activityId, List keys); + int activityId, + List keys, + ); /// Returns from [routeId] its route's streams as a [StreamCollection]. /// @@ -80,5 +86,7 @@ abstract class RepositoryStream { /// /// {@macro fault_management} Future getSegmentStreamsByType( - int segmentId, List keys); + int segmentId, + List keys, + ); } diff --git a/pubspec.yaml b/pubspec.yaml index 62e4005..7909f93 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,18 @@ name: strava_client -description: A Flutter Client for Strava V3 Api Based on the old strava_flutter api. -version: 2.3.0 +description: >- + An unofficial Flutter client for the Strava V3 API: OAuth2 authentication with + automatic token refresh, typed models, and repository-based endpoints. +version: 2.3.1 homepage: https://github.com/dreampowder/strava_flutter +repository: https://github.com/dreampowder/strava_flutter +issue_tracker: https://github.com/dreampowder/strava_flutter/issues +documentation: https://github.com/dreampowder/strava_flutter#readme +topics: + - strava + - fitness + - oauth + - http + - api environment: sdk: ">=3.8.0 <4.0.0" diff --git a/test/model_serialization_test.dart b/test/model_serialization_test.dart index d0842d7..e91f894 100644 --- a/test/model_serialization_test.dart +++ b/test/model_serialization_test.dart @@ -40,10 +40,11 @@ void main() { test('fromRawJson / toRawJson still work', () { final a = SummaryAthlete.fromRawJson( - '{"id":1,"resource_state":2,"firstname":"A","lastname":"B",' - '"profile_medium":"m","profile":"p","city":"c","state":"s",' - '"country":"co","sex":"M","premium":false,"summit":false,' - '"created_at":"x","updated_at":"y"}'); + '{"id":1,"resource_state":2,"firstname":"A","lastname":"B",' + '"profile_medium":"m","profile":"p","city":"c","state":"s",' + '"country":"co","sex":"M","premium":false,"summit":false,' + '"created_at":"x","updated_at":"y"}', + ); expect(a.id, 1); expect(a.toRawJson(), contains('"firstname":"A"')); }); diff --git a/test/spec_example_roundtrip_test.dart b/test/spec_example_roundtrip_test.dart index 965f71c..b036cd8 100644 --- a/test/spec_example_roundtrip_test.dart +++ b/test/spec_example_roundtrip_test.dart @@ -12,9 +12,9 @@ import 'package:strava_client/src/domain/model/model_detailed_segment.dart'; /// `fromJson` -> `toJson` -> `fromJson` is idempotent (no behavior drift, no /// throw, no field loss across a round-trip). void main() { - final examples = json.decode( - File('test/_spec_examples.json').readAsStringSync()) - as Map; + final examples = + json.decode(File('test/_spec_examples.json').readAsStringSync()) + as Map; Map ex(String path) => (examples[path] as Map).cast(); @@ -33,8 +33,11 @@ void main() { once.remove(k); twice.remove(k); } - expect(twice, once, - reason: 'fromJson/toJson must be idempotent for $label'); + expect( + twice, + once, + reason: 'fromJson/toJson must be idempotent for $label', + ); }); } From 946b25d9d6a1a5b38ff733f12de79b12f0f3305b Mon Sep 17 00:00:00 2001 From: Serdar Coskun Date: Mon, 1 Jun 2026 23:43:00 +0300 Subject: [PATCH 2/2] =?UTF-8?q?Update=20copyright=20to=20current=20owner?= =?UTF-8?q?=20(Serdar=20Co=C5=9Fkun)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The repository ownership transferred to Serdar Coşkun. Update the LICENSE copyright holder and the README license line accordingly, and credit the original maintainer (Patrick Finkelstein) in the acknowledgements. Co-Authored-By: Claude Opus 4.8 (1M context) --- LICENSE | 2 +- README.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 5b2c3b6..bd65022 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 Patrick FINKELSTEIN and the strava_flutter contributors +Copyright (c) 2019-2026 Serdar Coşkun and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index db15062..d22dbc9 100644 --- a/README.md +++ b/README.md @@ -317,10 +317,11 @@ CI runs analysis and tests on every PR. ## Acknowledgements - [@Birdyf](https://github.com/Birdyf) for the original package. +- Patrick Finkelstein, the package's original maintainer. - [Joe Birch](https://github.com/hitherejoe/FlutterOAuth) — OAuth reference. - Strava's published [Swagger spec](https://developers.strava.com/swagger/), bundled and validated under [`openapi/`](openapi/). ## License -[MIT](LICENSE) — Copyright (c) 2019-present the strava_flutter contributors. +[MIT](LICENSE) — Copyright (c) 2019-present Serdar Coşkun and contributors.