feat: Enhance image handling in task reporting and commenting with compression and content type detection
This commit is contained in:
parent
3a449441fa
commit
1f784d96f4
@ -10,6 +10,7 @@ import 'package:marco/controller/task_planing/daily_task_planing_controller.dart
|
|||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'package:marco/helpers/widgets/my_image_compressor.dart';
|
||||||
|
|
||||||
final Logger logger = Logger();
|
final Logger logger = Logger();
|
||||||
|
|
||||||
@ -136,14 +137,27 @@ class ReportTaskController extends MyController {
|
|||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
List<Map<String, dynamic>>? imageData;
|
List<Map<String, dynamic>>? imageData;
|
||||||
if (images != null && images.isNotEmpty) {
|
if (images != null && images.isNotEmpty) {
|
||||||
imageData = await Future.wait(images.map((file) async {
|
if (images != null && images.isNotEmpty) {
|
||||||
final bytes = await file.readAsBytes();
|
final imageFutures = images.map((file) async {
|
||||||
final base64Image = base64Encode(bytes);
|
final compressedBytes = await compressImageToUnder100KB(file);
|
||||||
return {
|
if (compressedBytes == null) return null;
|
||||||
"fileName": file.path.split('/').last,
|
|
||||||
"fileData": base64Image,
|
final base64Image = base64Encode(compressedBytes);
|
||||||
};
|
final fileName = file.path.split('/').last;
|
||||||
}));
|
final contentType = _getContentTypeFromFileName(fileName);
|
||||||
|
|
||||||
|
return {
|
||||||
|
"fileName": fileName,
|
||||||
|
"base64Data": base64Image,
|
||||||
|
"contentType": contentType,
|
||||||
|
"fileSize": compressedBytes.lengthInBytes,
|
||||||
|
"description": "Image uploaded for task report",
|
||||||
|
};
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
final results = await Future.wait(imageFutures);
|
||||||
|
imageData = results.whereType<Map<String, dynamic>>().toList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final success = await ApiService.reportTask(
|
final success = await ApiService.reportTask(
|
||||||
@ -180,6 +194,23 @@ class ReportTaskController extends MyController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _getContentTypeFromFileName(String fileName) {
|
||||||
|
final ext = fileName.split('.').last.toLowerCase();
|
||||||
|
switch (ext) {
|
||||||
|
case 'jpg':
|
||||||
|
case 'jpeg':
|
||||||
|
return 'image/jpeg';
|
||||||
|
case 'png':
|
||||||
|
return 'image/png';
|
||||||
|
case 'webp':
|
||||||
|
return 'image/webp';
|
||||||
|
case 'gif':
|
||||||
|
return 'image/gif';
|
||||||
|
default:
|
||||||
|
return 'application/octet-stream';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> commentTask({
|
Future<void> commentTask({
|
||||||
required String projectId,
|
required String projectId,
|
||||||
required String comment,
|
required String comment,
|
||||||
@ -200,22 +231,37 @@ class ReportTaskController extends MyController {
|
|||||||
try {
|
try {
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
List<Map<String, dynamic>>? imageData;
|
List<Map<String, dynamic>>? imageData;
|
||||||
|
|
||||||
if (images != null && images.isNotEmpty) {
|
if (images != null && images.isNotEmpty) {
|
||||||
imageData = await Future.wait(images.map((file) async {
|
final imageFutures = images.map((file) async {
|
||||||
final bytes = await file.readAsBytes();
|
final compressedBytes = await compressImageToUnder100KB(file);
|
||||||
final base64Image = base64Encode(bytes);
|
if (compressedBytes == null) return null;
|
||||||
|
|
||||||
|
final base64Image = base64Encode(compressedBytes);
|
||||||
|
final fileName = file.path.split('/').last;
|
||||||
|
final contentType = _getContentTypeFromFileName(fileName);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"fileName": file.path.split('/').last,
|
"fileName": fileName,
|
||||||
"fileData": base64Image,
|
"base64Data": base64Image,
|
||||||
|
"contentType": contentType,
|
||||||
|
"fileSize": compressedBytes.lengthInBytes,
|
||||||
|
"description": "Image uploaded for task comment",
|
||||||
};
|
};
|
||||||
}));
|
}).toList();
|
||||||
|
|
||||||
|
final results = await Future.wait(imageFutures);
|
||||||
|
imageData = results.whereType<Map<String, dynamic>>().toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
final success = await ApiService.commentTask(
|
final success = await ApiService.commentTask(
|
||||||
id: projectId,
|
id: projectId,
|
||||||
comment: commentField,
|
comment: commentField,
|
||||||
images: imageData,
|
images: imageData,
|
||||||
);
|
).timeout(const Duration(seconds: 30), onTimeout: () {
|
||||||
|
logger.e("Request timed out.");
|
||||||
|
throw Exception("Request timed out.");
|
||||||
|
});
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
showAppSnackbar(
|
showAppSnackbar(
|
||||||
@ -223,7 +269,6 @@ class ReportTaskController extends MyController {
|
|||||||
message: "Task commented successfully!",
|
message: "Task commented successfully!",
|
||||||
type: SnackbarType.success,
|
type: SnackbarType.success,
|
||||||
);
|
);
|
||||||
|
|
||||||
await taskController.fetchTaskData(projectId);
|
await taskController.fetchTaskData(projectId);
|
||||||
} else {
|
} else {
|
||||||
showAppSnackbar(
|
showAppSnackbar(
|
||||||
|
@ -95,7 +95,10 @@ class ApiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Future<http.Response?> _postRequest(
|
static Future<http.Response?> _postRequest(
|
||||||
String endpoint, dynamic body) async {
|
String endpoint,
|
||||||
|
dynamic body, {
|
||||||
|
Duration customTimeout = timeout,
|
||||||
|
}) async {
|
||||||
String? token = await _getToken();
|
String? token = await _getToken();
|
||||||
if (token == null) return null;
|
if (token == null) return null;
|
||||||
|
|
||||||
@ -108,7 +111,7 @@ class ApiService {
|
|||||||
try {
|
try {
|
||||||
final response = await http
|
final response = await http
|
||||||
.post(uri, headers: _headers(token), body: jsonEncode(body))
|
.post(uri, headers: _headers(token), body: jsonEncode(body))
|
||||||
.timeout(timeout);
|
.timeout(customTimeout);
|
||||||
|
|
||||||
_log("Response Status: ${response.statusCode}");
|
_log("Response Status: ${response.statusCode}");
|
||||||
return response;
|
return response;
|
||||||
@ -117,6 +120,7 @@ class ApiService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Attendence Screen API Calls =====
|
// ===== Attendence Screen API Calls =====
|
||||||
|
|
||||||
static Future<List<dynamic>?> getProjects() async {
|
static Future<List<dynamic>?> getProjects() async {
|
||||||
@ -354,22 +358,35 @@ class ApiService {
|
|||||||
if (images != null && images.isNotEmpty) "images": images,
|
if (images != null && images.isNotEmpty) "images": images,
|
||||||
};
|
};
|
||||||
|
|
||||||
final response = await _postRequest(ApiEndpoints.reportTask, body);
|
String? token = await _getToken();
|
||||||
|
if (token == null) return false;
|
||||||
|
|
||||||
if (response == null) {
|
final uri = Uri.parse("${ApiEndpoints.baseUrl}${ApiEndpoints.reportTask}");
|
||||||
_log("Error: No response from server.");
|
|
||||||
return false;
|
_log("POST $uri");
|
||||||
|
_log("Headers: ${_headers(token)}");
|
||||||
|
_log("Body: $body");
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await http
|
||||||
|
.post(uri, headers: _headers(token), body: jsonEncode(body))
|
||||||
|
.timeout(const Duration(seconds: 30));
|
||||||
|
|
||||||
|
_log("Response Status: ${response.statusCode}");
|
||||||
|
|
||||||
|
final json = jsonDecode(response.body);
|
||||||
|
|
||||||
|
if (response.statusCode == 200 && json['success'] == true) {
|
||||||
|
Get.back();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
_log("Failed to report task: ${json['message'] ?? 'Unknown error'}");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
_log("HTTP POST Exception (reportTask): $e");
|
||||||
}
|
}
|
||||||
|
|
||||||
final json = jsonDecode(response.body);
|
return false;
|
||||||
|
|
||||||
if (response.statusCode == 200 && json['success'] == true) {
|
|
||||||
Get.back();
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
_log("Failed to report task: ${json['message'] ?? 'Unknown error'}");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<bool> commentTask({
|
static Future<bool> commentTask({
|
||||||
@ -384,21 +401,33 @@ class ApiService {
|
|||||||
if (images != null && images.isNotEmpty) "images": images,
|
if (images != null && images.isNotEmpty) "images": images,
|
||||||
};
|
};
|
||||||
|
|
||||||
final response = await _postRequest(ApiEndpoints.commentTask, body);
|
String? token = await _getToken();
|
||||||
|
if (token == null) return false;
|
||||||
|
|
||||||
if (response == null) {
|
final uri = Uri.parse("${ApiEndpoints.baseUrl}${ApiEndpoints.commentTask}");
|
||||||
_log("Error: No response from server.");
|
|
||||||
return false;
|
_log("POST $uri");
|
||||||
|
_log("Headers: ${_headers(token)}");
|
||||||
|
_log("Body: $body");
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await http
|
||||||
|
.post(uri, headers: _headers(token), body: jsonEncode(body))
|
||||||
|
.timeout(const Duration(seconds: 30));
|
||||||
|
|
||||||
|
_log("Response Status: ${response.statusCode}");
|
||||||
|
|
||||||
|
final json = jsonDecode(response.body);
|
||||||
|
if (response.statusCode == 200 && json['success'] == true) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
_log("Failed to comment task: ${json['message'] ?? 'Unknown error'}");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
_log("HTTP POST Exception (commentTask): $e");
|
||||||
}
|
}
|
||||||
|
|
||||||
final json = jsonDecode(response.body);
|
return false;
|
||||||
|
|
||||||
if (response.statusCode == 200 && json['success'] == true) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
_log("Failed to comment task: ${json['message'] ?? 'Unknown error'}");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Daily Task Planing //
|
// Daily Task Planing //
|
||||||
|
Loading…
x
Reference in New Issue
Block a user