Merge branch 'main'

release
Razmig Sarkissian 1 year ago
commit 60eb840604
  1. 2
      PadelClub/Extensions/String+Extensions.swift
  2. 203
      PadelClub/Utils/CloudConvert.swift
  3. 5
      PadelClub/ViewModel/SearchViewModel.swift
  4. 7
      PadelClub/Views/Club/ClubDetailView.swift
  5. 5
      PadelClub/Views/Tournament/Screen/AddTeamView.swift

@ -71,7 +71,7 @@ extension String {
// Join the first letters together into a string
let result = String(firstLetters)
return result
return String(result.prefix(10))
}
}

@ -29,196 +29,65 @@ class CloudConvert {
static let manager = CloudConvert()
func uploadFile(_ url: URL) async throws -> String {
let taskResponse = try await createJob(url)
let uploadResponse = try await uploadFile(taskResponse, url: url)
var fileReady = false
while fileReady == false {
try await Task.sleep(nanoseconds: 3_000_000_000)
let progressResponse = try await checkFile(id: uploadResponse.data.id)
if progressResponse.data.step == "finish" && progressResponse.data.stepPercent == 100 {
fileReady = true
print("progressResponse.data.minutes", progressResponse.data.minutes)
}
}
let convertedFile = try await downloadConvertedFile(id: uploadResponse.data.id)
return convertedFile
return try await createJob(url)
}
func createJob(_ url: URL) async throws -> TaskResponse {
guard let taskURL = URL(string: "https://api.convertio.co/convert") else {
throw CloudConvertionError.urlNotFound("https://api.convertio.co/convert")
func createJob(_ url: URL) async throws -> String {
let apiPath = "https://\(URLs.activationHost.rawValue)/utils/xls-to-csv/"
guard let taskURL = URL(string: apiPath) else {
throw CloudConvertionError.urlNotFound(apiPath)
}
var request: URLRequest = URLRequest(url: taskURL)
let parameters = """
{"apikey":"d97cf13ef6d163e5e386c381fc8d256f","input":"upload","file":"","filename":"","outputformat":"csv","options":""}
"""
let postData = parameters.data(using: .utf8)
request.httpMethod = "POST"
request.httpBody = postData
let task = try await URLSession.shared.data(for: request)
//print("tried: \(request.url)")
if let errorResponse = try? JSONDecoder().decode(ErrorResponse.self, from: task.0) {
print("errorResponse.error", errorResponse.error)
throw CloudConvertionError.serviceError(errorResponse)
}
// Create the boundary string for multipart/form-data
let boundary = UUID().uuidString
return try JSONDecoder().decode(TaskResponse.self, from: task.0)
}
// Set the content type to multipart/form-data with the boundary
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
func uploadFile(_ response: TaskResponse, url: URL) async throws -> UploadResponse {
guard let uploadTaskURL = URL(string: "https://api.convertio.co/convert/\(response.data.id)/\(url.encodedLastPathComponent)") else {
throw CloudConvertionError.urlNotFound("https://api.convertio.co/convert/\(response.data.id)/\(url.encodedLastPathComponent)")
}
var uploadRequest: URLRequest = URLRequest(url: uploadTaskURL)
uploadRequest.httpMethod = "PUT"
let uploadTask = try await URLSession.shared.upload(for: uploadRequest, fromFile: url)
//print("tried: \(uploadRequest.url)")
if let errorResponse = try? JSONDecoder().decode(ErrorResponse.self, from: uploadTask.0) {
print("errorResponse.error", errorResponse.error)
throw CloudConvertionError.serviceError(errorResponse)
}
// The file to upload
let fileName = url.lastPathComponent
let fileURL = url
return try JSONDecoder().decode(UploadResponse.self, from: uploadTask.0)
}
// Construct the body of the request
var body = Data()
func checkFile(id: String) async throws -> ProgressResponse {
guard let taskURL = URL(string: "https://api.convertio.co/convert/\(id)/status") else {
throw CloudConvertionError.urlNotFound("https://api.convertio.co/convert/\(id)/status")
}
var request: URLRequest = URLRequest(url: taskURL)
request.httpMethod = "GET"
let task = try await URLSession.shared.data(for: request)
// Start the body with the boundary and content-disposition for the file
body.append("--\(boundary)\r\n".data(using: .utf8)!)
body.append("Content-Disposition: form-data; name=\"file\"; filename=\"\(fileName)\"\r\n".data(using: .utf8)!)
body.append("Content-Type: application/vnd.ms-excel\r\n\r\n".data(using: .utf8)!)
//print("tried: \(request.url)")
if let errorResponse = try? JSONDecoder().decode(ErrorResponse.self, from: task.0) {
print("errorResponse.error", errorResponse.error)
throw CloudConvertionError.serviceError(errorResponse)
// Append the file data
if let fileData = try? Data(contentsOf: fileURL) {
body.append(fileData)
}
return try JSONDecoder().decode(ProgressResponse.self, from: task.0)
}
// End the body with the boundary
body.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)
func downloadConvertedFile(id: String) async throws -> String {
// try await Task.sleep(nanoseconds: 3_000_000_000)
// Set the body of the request
request.httpBody = body
guard let downloadTaskURL = URL(string: "https://api.convertio.co/convert/\(id)/dl/base64") else {
throw CloudConvertionError.urlNotFound("https://api.convertio.co/convert/\(id)/dl/base64")
}
var downloadRequest: URLRequest = URLRequest(url: downloadTaskURL)
downloadRequest.httpMethod = "GET"
let downloadTask = try await URLSession.shared.data(for: downloadRequest)
if let errorResponse = try? JSONDecoder().decode(ErrorResponse.self, from: downloadTask.0) {
print("errorResponse.error", errorResponse.error)
throw CloudConvertionError.serviceError(errorResponse)
}
let (data, response) = try await URLSession.shared.data(for: request)
//print("tried: \(downloadRequest.url)")
let dataResponse = try JSONDecoder().decode(DataResponse.self, from: downloadTask.0)
if let decodedData = Data(base64Encoded: dataResponse.data.content), let string = String(data: decodedData, encoding: .utf8) {
return string
// Check the response status code
if let httpResponse = response as? HTTPURLResponse {
print("Status code: \(httpResponse.statusCode)")
}
throw CloudConvertionError.unknownError
// Convert the response data to a String
if let responseString = String(data: data, encoding: .utf8) {
return responseString
} else {
let error = ErrorResponse(code: 1, status: "Encodage", error: "Encodage des données de classement invalide")
throw CloudConvertionError.serviceError(error)
}
}
}
// MARK: - DataResponse
struct DataResponse: Decodable {
let code: Int
let status: String
let data: DataDownloadClass
}
// MARK: - DataClass
struct DataDownloadClass: Decodable {
let id, encode, content: String
}
// MARK: - ErrorResponse
struct ErrorResponse: Decodable {
let code: Int
let status, error: String
}
// MARK: - TaskResponse
struct TaskResponse: Decodable {
let code: Int
let status: String
let data: DataClass
}
// MARK: - DataClass
struct DataClass: Decodable {
let id: String
}
// MARK: - ProgressResponse
struct ProgressResponse: Decodable {
let code: Int
let status: String
let data: ProgressDataClass
}
// MARK: - DataClass
struct ProgressDataClass: Decodable {
let id, step: String
let stepPercent: Int
let minutes: String
enum CodingKeys: String, CodingKey {
case id, step
case stepPercent = "step_percent"
case minutes
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: .id)
step = try container.decode(String.self, forKey: .step)
minutes = try container.decode(String.self, forKey: .minutes)
if let value = try? container.decode(String.self, forKey: .stepPercent) {
print(value)
stepPercent = Int(value) ?? 0
} else {
stepPercent = try container.decode(Int.self, forKey: .stepPercent)
}
}
}
// MARK: - Output
struct Output: Decodable {
let url: String
let size: String
}
// MARK: - UploadResponse
struct UploadResponse: Decodable {
let code: Int
let status: String
let data: UploadDataClass
}
// MARK: - DataClass
struct UploadDataClass: Decodable {
let id, file: String
let size: Int
}
extension URL {
var encodedLastPathComponent: String {
if #available(iOS 17.0, *) {
lastPathComponent
} else {
lastPathComponent.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? lastPathComponent
}
}
}

@ -68,7 +68,10 @@ class SearchViewModel: ObservableObject, Identifiable {
var contentUnavailableMessage: String {
var message = ["Vérifiez l'ortographe ou lancez une nouvelle recherche."]
if tokens.isEmpty {
message.append("Il est possible que cette personne n'est joué aucun tournoi depuis les 12 derniers mois. Dans ce pas, Padel Club ne pourra pas le trouver.")
message.append("Il est possible que cette personne n'est joué aucun tournoi depuis les 12 derniers mois, dans ce cas, Padel Club ne pourra pas le trouver.")
if filterOption == .male {
message.append("Depuis août 2024, le classement fédérale disponible est limité aux 40.000 premiers joueurs. Si le joueur n'a pas encore assez de points pour être visible, Padel Club ne pourra pas non plus le trouver.")
}
}
return message.joined(separator: "\n")
}

@ -66,6 +66,11 @@ struct ClubDetailView: View {
.frame(maxWidth: .infinity)
.focused($focusedField, equals: ._name)
.submitLabel( displayContext == .addition ? .next : .done)
.onChange(of: club.name) {
if club.name.count > 50 {
club.name = String(club.name.prefix(50))
}
}
.onSubmit(of: .text) {
if club.acronym.isEmpty {
club.acronym = club.name.canonicalVersion.replaceCharactersFromSet(characterSet: .whitespacesAndNewlines).acronym()
@ -90,7 +95,7 @@ struct ClubDetailView: View {
.focused($focusedField, equals: ._acronym)
.submitLabel(.done)
.multilineTextAlignment(.trailing)
.onSubmit(of: .text) {
.onChange(of: club.acronym) {
if club.acronym.count > 10 {
club.acronym = String(club.acronym.prefix(10))
} else if club.acronym.count == 0 {

@ -438,7 +438,7 @@ struct AddTeamView: View {
ContentUnavailableView {
Label("Aucun résultat", systemImage: "person.2.slash")
} description: {
Text("Aucun joueur classé n'a été trouvé dans ce message.")
Text("Aucun joueur classé n'a été trouvé dans ce message. Attention, si un joueur n'a pas joué de tournoi dans les 12 derniers, Padel Club ne pourra pas le trouver.")
} actions: {
RowButtonView("Créer un joueur non classé") {
presentPlayerCreation = true
@ -451,7 +451,8 @@ struct AddTeamView: View {
} else {
Section {
ForEach(fetchPlayers.sorted(by: { $0.hitForSearch(pasteString) > $1.hitForSearch(pasteString) })) { player in
let sortedPlayers = fetchPlayers.sorted(by: { $0.hitForSearch(pasteString) > $1.hitForSearch(pasteString) })
ForEach(sortedPlayers) { player in
ImportedPlayerView(player: player).tag(player.license!)
}
} header: {

Loading…
Cancel
Save