Skip to content
This repository has been archived by the owner on Apr 27, 2024. It is now read-only.

Commit

Permalink
Fixed minor bugs in ApiHandlers and account views
Browse files Browse the repository at this point in the history
  • Loading branch information
carlobortolan committed Sep 11, 2023
1 parent 52ad85b commit ceba6e7
Show file tree
Hide file tree
Showing 9 changed files with 111 additions and 103 deletions.
4 changes: 4 additions & 0 deletions mobile.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
2221CE332AAE993100E94A50 /* CustomRating.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2221CE322AAE993100E94A50 /* CustomRating.swift */; };
2226B2E12AAF4509006FD759 /* Routes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2226B2E02AAF4509006FD759 /* Routes.swift */; };
2226B2E42AAF5357006FD759 /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 2226B2E32AAF5357006FD759 /* Alamofire */; };
2226B2E92AAFD7FC006FD759 /* Misc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2226B2E82AAFD7FC006FD759 /* Misc.swift */; };
22430F952AAA5C2D00747827 /* ImagePickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22430F942AAA5C2D00747827 /* ImagePickerView.swift */; };
22430F982AAA5CEC00747827 /* ImagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22430F972AAA5CEC00747827 /* ImagePicker.swift */; };
22430F9C2AAAB3C400747827 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22430F9B2AAAB3C400747827 /* SettingsView.swift */; };
Expand Down Expand Up @@ -124,6 +125,7 @@
2221CE322AAE993100E94A50 /* CustomRating.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomRating.swift; sourceTree = "<group>"; };
2226B2E02AAF4509006FD759 /* Routes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Routes.swift; sourceTree = "<group>"; };
2226B2E72AAF698F006FD759 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
2226B2E82AAFD7FC006FD759 /* Misc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Misc.swift; sourceTree = "<group>"; };
22430F942AAA5C2D00747827 /* ImagePickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagePickerView.swift; sourceTree = "<group>"; };
22430F972AAA5CEC00747827 /* ImagePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagePicker.swift; sourceTree = "<group>"; };
22430F9B2AAAB3C400747827 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -358,6 +360,7 @@
22D9277C2AA7B16F0017678C /* ReviewModel.swift */,
22D9277E2AA7B1A00017678C /* PreferencesModel.swift */,
22B804622AA91570008D7108 /* UserModel.swift */,
2226B2E82AAFD7FC006FD759 /* Misc.swift */,
);
path = Models;
sourceTree = "<group>";
Expand Down Expand Up @@ -645,6 +648,7 @@
2216EA6F2A8D262400C970DD /* ErrorHandlingManager.swift in Sources */,
2221CE2D2AAE0E2C00E94A50 /* JobDetail2.swift in Sources */,
2216EA712A8D2E7D00C970DD /* KeyChainManager.swift in Sources */,
2226B2E92AAFD7FC006FD759 /* Misc.swift in Sources */,
22DC3AF62AAD2F6800B783E8 /* JobMapView.swift in Sources */,
22B804612AA90EB7008D7108 /* ApplicationHandler.swift in Sources */,
227E9C952AABB83700049CAB /* ApplicationDetail.swift in Sources */,
Expand Down
5 changes: 3 additions & 2 deletions mobile/Controllers/AuthenticationManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -295,9 +295,10 @@ class AuthenticationManager: ObservableObject {
if let accessToken = self.getAccessToken() {
APIManager.uploadUserImage(accessToken: accessToken, image: image) { result in
switch result {
case .success(let apiResponse):
case .success(let imageResponse):
DispatchQueue.main.async {
print("case .success: \(apiResponse)")
print("IMAGEURL: case .success: \(imageResponse)")
self.current.imageURL = imageResponse.image_url
self.errorHandlingManager.errorMessage = nil
completion()
}
Expand Down
12 changes: 12 additions & 0 deletions mobile/Models/Misc.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// Misc.swift
// mobile
//
// Created by cb on 12.09.23.
//

import Foundation

struct ImageResponse: Codable {
let image_url: String
}
2 changes: 1 addition & 1 deletion mobile/Networking/APIManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ class APIManager {
/// - accessToken: The user's access token for authentication.
/// - image: The user's new profile image.
/// - completion: A closure that receives a `Result` with an `APIResponse` or an `APIError`.
static func uploadUserImage(accessToken: String, image: UIImage, completion: @escaping (Result<APIResponse, APIError>) -> Void) {
static func uploadUserImage(accessToken: String, image: UIImage, completion: @escaping (Result<ImageResponse, APIError>) -> Void) {
AccountHandler.uploadImage(accessToken: accessToken, image: image, completion: completion)
}

Expand Down
65 changes: 42 additions & 23 deletions mobile/Networking/AccountHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -130,27 +130,21 @@ class AccountHandler {
}

// TODO: Improve attachment
static func uploadImage(accessToken: String, image: UIImage, completion: @escaping (Result<APIResponse, APIError>) -> Void) {
static func uploadImage(accessToken: String, image: UIImage, completion: @escaping (Result<ImageResponse, APIError>) -> Void) {
print("Started uploading image with: \naccess_token: \(accessToken)")

// Prepare the image as Data
guard let imageData = image.resized(toWidth: 800)?.jpegData(compressionQuality: 0.9) else {
completion(.failure(APIError.imageProcessingError))
return
}

// Create a unique boundary string
let boundary = "Boundary-\(UUID().uuidString)"

// Create the URLRequest
guard let url = URL(string: Routes.ROOT_URL + Routes.USER_IMAGE_PATH) else {
completion(.failure(APIError.invalidURL))
return
}

var request = URLRequest(url: url)
request.httpMethod = "POST"

request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
request.setValue(accessToken, forHTTPHeaderField: "access_token")
var body = Data()
Expand All @@ -164,31 +158,56 @@ class AccountHandler {
}
body.append("\r\n".data(using: .utf8)!)
body.append("--\(boundary)--\r\n".data(using: .utf8)!)

request.httpBody = body

let task = URLSession.shared.dataTask(with: request) { data, response, error in
URLSession.shared.dataTask(with: request) { (data, response, error) in
if let error = error {
print("Error fetching data: \(error)")
completion(.failure(APIError.networkError(error)))
return
}

if let httpResponse = response as? HTTPURLResponse {
if (200...299).contains(httpResponse.statusCode) {
let statusCode = httpResponse.statusCode
print("HTTP Response Code: \(statusCode)")
print("HTTP Response: \(response.debugDescription)")

RequestHandler.handleApiErrorsNew(data: data, statusCode: statusCode, completion: completion)
switch statusCode {
case 204:
completion(.failure(APIError.noContent(String(describing: ImageResponse.self))))
case 200:
if let data = data {
print("Image uploaded successfully: \(data)")
do {
if let responseString = String(data: data, encoding: .utf8) {
print("Data as String: \(responseString)")
} else {
print("Failed to convert data to string")
}
let responseData = try JSONDecoder().decode(ImageResponse.self, from: data)
completion(.success(responseData))
} catch {
print("JSON Error: \(error)")
completion(.failure(APIError.jsonParsingError(error)))
}
} else {
print("Image uploaded successfully.")
completion(.failure(APIError.unknownError))
}
default:
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print("Error JSON = \(json)")
} catch {
completion(.failure(APIError.jsonParsingError(error)))
}
} else {
completion(.failure(APIError.unknownError))
}
completion(.success(APIResponse(message: "Image uploaded successfully")))
} else {
print("Server error: \(httpResponse.statusCode)")
print("Server response: \(httpResponse.debugDescription)")
completion(.failure(APIError.internalServerError))
}
}
}
task.resume()
}.resume()
}

static func deleteUser(accessToken: String, completion: @escaping (Result<APIResponse, APIError>) -> Void) {
Expand Down
64 changes: 3 additions & 61 deletions mobile/Views/AccountPage/AccountInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,12 @@ struct AccountInfo: View {
@EnvironmentObject var authenticationManager: AuthenticationManager
@EnvironmentObject var jobManager: JobManager
@EnvironmentObject var applicationManager: ApplicationManager
@State private var isLoading = false
// @Binding var user: User

@State private var isImagePickerViewPresented = false
@State private var isImageRemoveAlertPresented = false
//@State private var selectedImage: UIImage?

var body: some View {
VStack {
if isLoading {
ProgressView()
}
Group() {
if (authenticationManager.current.imageURL != nil) {
URLImage(URL(string: authenticationManager.current.imageURL ?? "https://embloy.onrender.com/assets/img/features_3.png")!) { image in
Expand All @@ -39,10 +33,8 @@ struct AccountInfo: View {
)
.onTapGesture {
if authenticationManager.current.imageURL != nil {
// Show the option to remove or upload a new image
isImageRemoveAlertPresented.toggle()
} else {
// Show the image picker to upload a new image
isImagePickerViewPresented.toggle()
}
}
Expand Down Expand Up @@ -75,22 +67,21 @@ struct AccountInfo: View {
message: Text("Do you want to remove your profile image or upload a new one?"),
primaryButton: .destructive(Text("Remove"), action: {
// Handle image removal
authenticationManager.current.imageURL = nil // Set the user's image URL to nil
authenticationManager.current.imageURL = nil
authenticationManager.removeUserImage(iteration: 0) {
}
}),
secondaryButton: .default(Text("Upload New"), action: {
// Show the image picker to upload a new image
isImagePickerViewPresented.toggle()
})
)
}
// Image Picker
.sheet(isPresented: $isImagePickerViewPresented) {
NavigationView {
ImagePickerView(isImagePickerViewPresented: $isImagePickerViewPresented, isLoading: $isLoading)
ImagePickerView(isImagePickerViewPresented: $isImagePickerViewPresented)
.navigationBarItems(trailing: Button("Close") {
isImagePickerViewPresented.toggle() // Close the image picker
isImagePickerViewPresented.toggle()
})
}
}
Expand Down Expand Up @@ -129,57 +120,8 @@ struct AccountInfo: View {
}
Spacer()
}
//.onAppear() {
// isLoading = true
// authenticationManager.loadProfile(iteration: 0) {
// isLoading = false
// }
// }
.padding()
}

/*
func removeUserImage(iteration: Int) {
print("Iteration \(iteration)")
if let accessToken = authenticationManager.getAccessToken() {
APIManager.removeUserImage(accessToken: accessToken) { result in
switch result {
case .success(let apiResponse):
DispatchQueue.main.async {
print("case .success \(apiResponse)")
self.errorHandlingManager.errorMessage = nil
}
case .failure(let error):
DispatchQueue.main.async {
print("case .failure, iteration: \(iteration)")
if iteration == 0 {
if case .authenticationError = error {
print("case .authenticationError")
// Authentication error (e.g., access token invalid)
// Refresh the access token and retry the request
self.authenticationManager.requestAccessToken() { accessTokenSuccess in
if accessTokenSuccess{
self.removeUserImage(iteration: 1)
} else {
self.errorHandlingManager.errorMessage = error.localizedDescription
}
}
} else {
print("case .else")
// Handle other errors
self.errorHandlingManager.errorMessage = error.localizedDescription
}
} else {
self.authenticationManager.isAuthenticated = false
self.errorHandlingManager.errorMessage = "Tokens expired. Log in to refresh tokens."
}
}
}
}
}
}
*/

}


Expand Down
11 changes: 3 additions & 8 deletions mobile/Views/AccountPage/ImagePickerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ struct ImagePickerView: View {
@State var selectedImage: UIImage?
@State var isImagePickerPresented = false
@Binding var isImagePickerViewPresented: Bool
@Binding var isLoading: Bool

var body: some View {
VStack {
Expand All @@ -27,14 +26,13 @@ struct ImagePickerView: View {
if let image = selectedImage {
Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100)
.frame(width: 200, height: 200)
.clipShape(Circle())
.padding()
} else {
Text("No Image Selected")
.font(.headline)
}

Button("Select Image") {
isImagePickerPresented.toggle()
}
Expand All @@ -49,11 +47,8 @@ struct ImagePickerView: View {

Button("Upload Image") {
if let image = selectedImage {
isLoading = true
DispatchQueue.main.async {
authenticationManager.uploadUserImage(iteration: 0, image: image) {
isLoading = false
}
authenticationManager.uploadUserImage(iteration: 0, image: image) {}
}
isImagePickerViewPresented = false
}
Expand Down
16 changes: 8 additions & 8 deletions mobile/Views/AccountPage/UpdateUserView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ struct UpdateUserView: View {
.keyboardType(.URL)
.autocapitalization(.none)
.disableAutocorrection(true)
.foregroundColor(.primary)
.foregroundColor(isLinkedInURLValid ? .primary : .red)

TextField("Twitter URL", text:Binding(
get: { user.twitterURL ?? "" },
Expand All @@ -127,7 +127,7 @@ struct UpdateUserView: View {
.keyboardType(.URL)
.autocapitalization(.none)
.disableAutocorrection(true)
.foregroundColor(.primary)
.foregroundColor(isTwitterURLValid ? .primary : .red)

TextField("Facebook URL", text: Binding(
get: { user.facebookURL ?? "" },
Expand All @@ -136,7 +136,7 @@ struct UpdateUserView: View {
.keyboardType(.URL)
.autocapitalization(.none)
.disableAutocorrection(true)
.foregroundColor(.primary)
.foregroundColor(isFacebookURLValid ? .primary : .red)

TextField("Instagram URL", text: Binding(
get: { user.instagramURL ?? "" },
Expand All @@ -145,7 +145,7 @@ struct UpdateUserView: View {
.keyboardType(.URL)
.autocapitalization(.none)
.disableAutocorrection(true)
.foregroundColor(.primary)
.foregroundColor(isInstagramURLValid ? .primary : .red)
}

}
Expand Down Expand Up @@ -191,10 +191,10 @@ struct UpdateUserView: View {
isDateOfBirthValid = true
isAddressValid = true
// TODO: Implement address validation
isLinkedInURLValid = Validator.isValidURL(user.linkedinURL)
isTwitterURLValid = Validator.isValidURL(user.twitterURL)
isFacebookURLValid = Validator.isValidURL(user.facebookURL)
isInstagramURLValid = Validator.isValidURL(user.instagramURL)
isLinkedInURLValid = Validator.isValidLinkedInURL(user.linkedinURL)
isTwitterURLValid = Validator.isValidTwitterURL(user.twitterURL)
isFacebookURLValid = Validator.isValidFacebookURL(user.facebookURL)
isInstagramURLValid = Validator.isValidInstagramURL(user.instagramURL)

print("isEmailValid \(isEmailValid)")
print("isFirstNameValid \(isFirstNameValid)")
Expand Down
Loading

0 comments on commit ceba6e7

Please sign in to comment.