-
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
docs(example): add Smolov Jr calculator for Hevy app (#232)
- Loading branch information
1 parent
b015e22
commit f41135c
Showing
23 changed files
with
3,016 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
name: Deploy Hevy Smolov Jr to Firebase Hosting | ||
|
||
on: | ||
push: | ||
branches: | ||
- main | ||
|
||
jobs: | ||
build_and_deploy: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v3 | ||
- uses: subosito/flutter-action@v2 | ||
- uses: bluefireteam/melos-action@v2 | ||
- name: Run tests | ||
run: melos run test | ||
- name: Build for web | ||
run: flutter build web | ||
working-directory: examples/hevy_smolov_jr | ||
- uses: FirebaseExtended/action-hosting-deploy@v0 | ||
with: | ||
entryPoint: examples/hevy_smolov_jr | ||
repoToken: '${{ secrets.GITHUB_TOKEN }}' | ||
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_HEVY_SMOLOV_JR }}' | ||
channelId: live | ||
projectId: hevy-smolov-jr |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"projects": { | ||
"default": "hevy-smolov-jr" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# Miscellaneous | ||
*.class | ||
*.log | ||
*.pyc | ||
*.swp | ||
.DS_Store | ||
.atom/ | ||
.buildlog/ | ||
.history | ||
.svn/ | ||
migrate_working_dir/ | ||
|
||
# IntelliJ related | ||
*.iml | ||
*.ipr | ||
*.iws | ||
.idea/ | ||
|
||
# The .vscode folder contains launch configuration and tasks you configure in | ||
# VS Code which you may wish to be included in version control, so this line | ||
# is commented out by default. | ||
#.vscode/ | ||
|
||
# Flutter/Dart/Pub related | ||
**/doc/api/ | ||
**/ios/Flutter/.last_build_id | ||
.dart_tool/ | ||
.flutter-plugins | ||
.flutter-plugins-dependencies | ||
.pub-cache/ | ||
.pub/ | ||
/build/ | ||
|
||
# Symbolication related | ||
app.*.symbols | ||
|
||
# Obfuscation related | ||
app.*.map.json | ||
|
||
# Android Studio will place build artifacts here | ||
/android/app/debug | ||
/android/app/profile | ||
/android/app/release |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# This file tracks properties of this Flutter project. | ||
# Used by Flutter tool to assess capabilities and perform upgrades etc. | ||
# | ||
# This file should be version controlled and should not be manually edited. | ||
|
||
version: | ||
revision: "2663184aa79047d0a33a14a3b607954f8fdd8730" | ||
channel: "stable" | ||
|
||
project_type: app | ||
|
||
# Tracks metadata for the flutter migrate command | ||
migration: | ||
platforms: | ||
- platform: root | ||
create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 | ||
base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 | ||
- platform: macos | ||
create_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 | ||
base_revision: 2663184aa79047d0a33a14a3b607954f8fdd8730 | ||
|
||
# User provided section | ||
|
||
# List of Local paths (relative to this file) that should be | ||
# ignored by the migrate tool. | ||
# | ||
# Files that are not part of the templates will be ignored by default. | ||
unmanaged_files: | ||
- 'lib/main.dart' | ||
- 'ios/Runner.xcodeproj/project.pbxproj' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
targets: | ||
$default: | ||
builders: | ||
json_serializable: | ||
options: | ||
# explicit_to_json: true | ||
field_rename: snake |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
"hosting": { | ||
"public": "build/web", | ||
"ignore": [ | ||
"firebase.json", | ||
"**/.*", | ||
"**/node_modules/**" | ||
], | ||
"rewrites": [ | ||
{ | ||
"source": "**", | ||
"destination": "/index.html" | ||
} | ||
] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
import 'dart:convert'; | ||
|
||
import 'package:hevy_smolov_jr/shared_prefs.dart'; | ||
import 'package:http/http.dart' as http; | ||
import 'package:rearch/experimental.dart'; | ||
import 'package:rearch/rearch.dart'; | ||
|
||
/// [Capsule] representing the user's Hevy API key. | ||
final Capsule<(String, void Function(String))> apiKeyCapsule = capsule((use) { | ||
const sharedPrefsKey = 'api-key'; | ||
final sharedPrefs = use(sharedPrefsCapsule); | ||
final (apiKey, setApiKey) = | ||
use.state(sharedPrefs.getString(sharedPrefsKey) ?? ''); | ||
return ( | ||
apiKey, | ||
(newApiKey) { | ||
sharedPrefs.setString(sharedPrefsKey, newApiKey); | ||
setApiKey(newApiKey); | ||
}, | ||
); | ||
}); | ||
|
||
final Capsule<String> _apiDomainCapsule = capsule((use) => 'api.hevyapp.com'); | ||
|
||
/// Represents an [Exception] from the Hevy API. | ||
sealed class HevyApiException implements Exception {} | ||
|
||
/// Represents an [Exception] while completing a Hevy API request. | ||
final class HevyApiNetworkException implements HevyApiException { | ||
/// Represents an [Exception] while completing a Hevy API request. | ||
const HevyApiNetworkException(this.underlyingException); | ||
|
||
/// The underlying [Exception] or [Error] thrown during the request. | ||
final Object underlyingException; | ||
|
||
@override | ||
String toString() { | ||
return 'HevyApiNetworkException(underlyingException: $underlyingException)'; | ||
} | ||
} | ||
|
||
/// Represents an [Exception] regarding the [http.Response]. | ||
final class HevyApiResponseException implements HevyApiException { | ||
/// Represents an [Exception] regarding the [http.Response]. | ||
const HevyApiResponseException(this.statusCode, this.errorMessage); | ||
|
||
/// The HTTP status code returned by the Hevy API. | ||
final int statusCode; | ||
|
||
/// The error message returned by the Hevy API. | ||
final String errorMessage; | ||
|
||
@override | ||
String toString() { | ||
return 'HevyApiResponseException(statusCode: $statusCode, ' | ||
'errorMessage: "$errorMessage")'; | ||
} | ||
} | ||
|
||
/// Represents an [Exception] parsing the [http.Response] from the Hevy API. | ||
final class HevyApiResponseParseException implements HevyApiException { | ||
/// Represents an [Exception] parsing the [http.Response] from the Hevy API. | ||
HevyApiResponseParseException({ | ||
required this.statusCode, | ||
required this.responseBody, | ||
required this.parseException, | ||
}); | ||
|
||
/// The HTTP status code returned by the Hevy API. | ||
final int statusCode; | ||
|
||
/// The response body returned by the Hevy API that could not be parsed. | ||
final String responseBody; | ||
|
||
/// The [Exception] or [Error] thrown while parsing [responseBody]. | ||
final Object parseException; | ||
|
||
@override | ||
String toString() { | ||
return 'HevyApiResponseParseException(statusCode: $statusCode, ' | ||
'parseException: $parseException, responseBody: $responseBody)'; | ||
} | ||
} | ||
|
||
/// Wraps a raw Hevy API HTTP call so that it: | ||
/// - returns the response body as the decoded `Map<String, dynamic>` | ||
/// - throws the appropriate type of [Exception] as needed | ||
final Capsule<Future<Map<String, dynamic>> Function(Future<http.Response>)> | ||
_parseApiRequestAction = capsule((use) { | ||
return (hevyApiRequest) async { | ||
late http.Response response; | ||
try { | ||
response = await hevyApiRequest; | ||
} catch (underlyingException, stackTrace) { | ||
Error.throwWithStackTrace( | ||
HevyApiNetworkException(underlyingException), | ||
stackTrace, | ||
); | ||
} | ||
|
||
late Map<String, dynamic> body; | ||
try { | ||
body = json.decode(response.body) as Map<String, dynamic>; | ||
|
||
if (response.statusCode < 200 || response.statusCode > 299) { | ||
throw HevyApiResponseException( | ||
response.statusCode, | ||
body['error'] as String, | ||
); | ||
} | ||
} catch (e, stackTrace) { | ||
Error.throwWithStackTrace( | ||
HevyApiResponseParseException( | ||
statusCode: response.statusCode, | ||
responseBody: response.body, | ||
parseException: e, | ||
), | ||
stackTrace, | ||
); | ||
} | ||
|
||
return body; | ||
}; | ||
}); | ||
|
||
/// Represents an HTTP GET request that returns JSON. | ||
typedef GetRequest = Future<Map<String, dynamic>> Function({ | ||
required String path, | ||
Map<String, String>? queryParams, | ||
}); | ||
|
||
/// Represents an HTTP GET request to the Hevy API. | ||
final Capsule<GetRequest> apiGetAction = capsule((use) { | ||
final parseRequest = use(_parseApiRequestAction); | ||
final apiDomain = use(_apiDomainCapsule); | ||
final headers = { | ||
'accept': 'application/json', | ||
'api-key': use(apiKeyCapsule).$1, | ||
}; | ||
|
||
return ({required String path, Map<String, dynamic>? queryParams}) { | ||
return parseRequest( | ||
http.get( | ||
Uri.https(apiDomain, path, queryParams), | ||
headers: headers, | ||
), | ||
); | ||
}; | ||
}); | ||
|
||
/// Represents an HTTP POST request that returns JSON. | ||
typedef PostRequest = Future<Map<String, dynamic>> Function({ | ||
required String path, | ||
Object? jsonBody, | ||
Map<String, String>? queryParams, | ||
}); | ||
|
||
/// Represents an HTTP POST request to the Hevy API. | ||
final Capsule<PostRequest> apiPostAction = capsule((use) { | ||
final parseRequest = use(_parseApiRequestAction); | ||
final apiDomain = use(_apiDomainCapsule); | ||
final headers = { | ||
'accept': 'application/json', | ||
'api-key': use(apiKeyCapsule).$1, | ||
'Content-Type': 'application/json', | ||
}; | ||
|
||
return ({ | ||
required String path, | ||
Object? jsonBody, | ||
Map<String, dynamic>? queryParams, | ||
}) { | ||
return parseRequest( | ||
http.post( | ||
Uri.https(apiDomain, path, queryParams), | ||
headers: headers, | ||
body: json.encode(jsonBody), | ||
), | ||
); | ||
}; | ||
}); |
Oops, something went wrong.