Skip to content

Commit

Permalink
Merge branch 'main' of github.com:bulgariamitko/flutterflowtutorials
Browse files Browse the repository at this point in the history
  • Loading branch information
bulgariamitko committed Mar 21, 2024
2 parents f5983d1 + ae53250 commit 7a588ae
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 41 deletions.
8 changes: 3 additions & 5 deletions Source-Code/ff_source_code/firebase/firebase.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@
"codebase": "functions"
}
],
"storage": [
{
"rules": "storage.rules"
}
],
"storage": {
"rules": "storage.rules"
},
"hosting": {
"public": "public",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"]
Expand Down
151 changes: 119 additions & 32 deletions Source-Code/ff_source_code/lib/backend/api_requests/api_manager.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// ignore_for_file: constant_identifier_names, depend_on_referenced_packages, prefer_final_fields

import 'dart:convert';
import 'dart:io';
import 'dart:core';
import 'dart:io';
import 'dart:typed_data';

import 'package:collection/collection.dart';
Expand All @@ -9,7 +11,7 @@ import 'package:equatable/equatable.dart';
import 'package:http_parser/http_parser.dart';
import 'package:mime_type/mime_type.dart';

import '../../flutter_flow/uploaded_file.dart';
import '/flutter_flow/uploaded_file.dart';

enum ApiCallType {
GET,
Expand All @@ -27,19 +29,73 @@ enum BodyType {
MULTIPART,
}

class ApiCallRecord extends Equatable {
ApiCallRecord(this.callName, this.apiUrl, this.headers, this.params,
this.body, this.bodyType);
class ApiCallOptions extends Equatable {
const ApiCallOptions({
this.callName = '',
required this.callType,
required this.apiUrl,
required this.headers,
required this.params,
this.bodyType,
this.body,
this.returnBody = true,
this.encodeBodyUtf8 = false,
this.decodeUtf8 = false,
this.alwaysAllowBody = false,
this.cache = false,
});

final String callName;
final ApiCallType callType;
final String apiUrl;
final Map<String, dynamic> headers;
final Map<String, dynamic> params;
final String? body;
final BodyType? bodyType;
final String? body;
final bool returnBody;
final bool encodeBodyUtf8;
final bool decodeUtf8;
final bool alwaysAllowBody;
final bool cache;

ApiCallOptions clone() => ApiCallOptions(
callName: callName,
callType: callType,
apiUrl: apiUrl,
headers: _cloneMap(headers),
params: _cloneMap(params),
bodyType: bodyType,
body: body,
returnBody: returnBody,
encodeBodyUtf8: encodeBodyUtf8,
decodeUtf8: decodeUtf8,
alwaysAllowBody: alwaysAllowBody,
cache: cache,
);

@override
List<Object?> get props =>
[callName, apiUrl, headers, params, body, bodyType];
List<Object?> get props => [
callName,
callType.name,
apiUrl,
headers,
params,
bodyType,
body,
returnBody,
encodeBodyUtf8,
decodeUtf8,
alwaysAllowBody,
cache,
];

static Map<String, dynamic> _cloneMap(Map<String, dynamic> map) {
try {
return json.decode(json.encode(map)) as Map<String, dynamic>;
} catch (_) {
return Map.from(map);
}
}
}

class ApiCallResponse {
Expand All @@ -48,11 +104,13 @@ class ApiCallResponse {
this.headers,
this.statusCode, {
this.response,
this.exception,
});
final dynamic jsonBody;
final Map<String, String> headers;
final int statusCode;
final http.Response? response;
final Object? exception;
// Whether we received a 2xx status (which generally marks success).
bool get succeeded => statusCode >= 200 && statusCode < 300;
String getHeader(String headerName) => headers[headerName] ?? '';
Expand All @@ -67,7 +125,7 @@ class ApiCallResponse {
bool returnBody,
bool decodeUtf8,
) {
var jsonBody;
dynamic jsonBody;
try {
final responseBody = decodeUtf8 && returnBody
? const Utf8Decoder().convert(response.bodyBytes)
Expand All @@ -94,7 +152,7 @@ class ApiManager {
ApiManager._();

// Cache that will ensure identical calls are not repeatedly made.
static Map<ApiCallRecord, ApiCallResponse> _apiCache = {};
static Map<ApiCallOptions, ApiCallResponse> _apiCache = {};

static ApiManager? _instance;
static ApiManager get instance => _instance ??= ApiManager._();
Expand Down Expand Up @@ -191,28 +249,31 @@ class ApiManager {
(alwaysAllowBody && type == ApiCallType.DELETE),
'Invalid ApiCallType $type for request with body',
);
bool Function(dynamic) _isFile = (e) =>

bool isFile(dynamic e) =>
e is FFUploadedFile ||
e is List<FFUploadedFile> ||
(e is List && e.firstOrNull is FFUploadedFile);

final nonFileParams = toStringMap(
Map.fromEntries(params.entries.where((e) => !_isFile(e.value))));
Map.fromEntries(params.entries.where((e) => !isFile(e.value))));

List<http.MultipartFile> files = [];
params.entries.where((e) => _isFile(e.value)).forEach((e) {
params.entries.where((e) => isFile(e.value)).forEach((e) {
final param = e.value;
final uploadedFiles = param is List
? param as List<FFUploadedFile>
: [param as FFUploadedFile];
uploadedFiles.forEach((uploadedFile) => files.add(
http.MultipartFile.fromBytes(
e.key,
uploadedFile.bytes ?? Uint8List.fromList([]),
filename: uploadedFile.name,
contentType: _getMediaType(uploadedFile.name),
),
));
for (var uploadedFile in uploadedFiles) {
files.add(
http.MultipartFile.fromBytes(
e.key,
uploadedFile.bytes ?? Uint8List.fromList([]),
filename: uploadedFile.name,
contentType: _getMediaType(uploadedFile.name),
),
);
}
});

final request = http.MultipartRequest(
Expand Down Expand Up @@ -277,6 +338,22 @@ class ApiManager {
: postBody;
}

Future<ApiCallResponse> call(ApiCallOptions options) => makeApiCall(
callName: options.callName,
apiUrl: options.apiUrl,
callType: options.callType,
headers: options.headers,
params: options.params,
body: options.body,
bodyType: options.bodyType,
returnBody: options.returnBody,
encodeBodyUtf8: options.encodeBodyUtf8,
decodeUtf8: options.decodeUtf8,
alwaysAllowBody: options.alwaysAllowBody,
cache: options.cache,
options: options,
);

Future<ApiCallResponse> makeApiCall({
required String callName,
required String apiUrl,
Expand All @@ -288,12 +365,26 @@ class ApiManager {
bool returnBody = true,
bool encodeBodyUtf8 = false,
bool decodeUtf8 = false,
bool cache = false,
bool alwaysAllowBody = false,
bool cache = false,
ApiCallOptions? options,
http.Client? client,
}) async {
final callRecord =
ApiCallRecord(callName, apiUrl, headers, params, body, bodyType);
final callOptions = options ??
ApiCallOptions(
callName: callName,
callType: callType,
apiUrl: apiUrl,
headers: headers,
params: params,
bodyType: bodyType,
body: body,
returnBody: returnBody,
encodeBodyUtf8: encodeBodyUtf8,
decodeUtf8: decodeUtf8,
alwaysAllowBody: alwaysAllowBody,
cache: cache,
);
// Modify for your specific needs if this differs from your API.
if (_accessToken != null) {
headers[HttpHeaders.authorizationHeader] = 'Bearer $_accessToken';
Expand All @@ -304,8 +395,8 @@ class ApiManager {

// If we've already made this exact call before and caching is on,
// return the cached result.
if (cache && _apiCache.containsKey(callRecord)) {
return _apiCache[callRecord]!;
if (cache && _apiCache.containsKey(callOptions)) {
return _apiCache[callOptions]!;
}

ApiCallResponse result;
Expand Down Expand Up @@ -368,14 +459,10 @@ class ApiManager {

// If caching is on, cache the result (if present).
if (cache) {
_apiCache[callRecord] = result;
_apiCache[callOptions] = result;
}
} catch (e) {
result = ApiCallResponse(
null,
{},
-1,
);
result = ApiCallResponse(null, {}, -1, exception: e);
}

return result;
Expand Down
8 changes: 4 additions & 4 deletions Source-Code/ff_source_code/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,10 @@ dependencies:
supabase: 1.11.11
supabase_flutter: 1.10.24
timeago: 3.2.2
url_launcher: 6.1.10
url_launcher_android: 6.0.27
url_launcher_ios: 6.1.4
url_launcher_platform_interface: 2.1.2
url_launcher: 6.2.5
url_launcher_android: 6.3.0
url_launcher_ios: 6.2.5
url_launcher_platform_interface: 2.3.2
video_player: 2.8.1
video_player_android: 2.4.10
video_player_avfoundation: 2.5.1
Expand Down

0 comments on commit 7a588ae

Please sign in to comment.