From 211e00edd270c04965ef1b4d073e246bce4ed01f Mon Sep 17 00:00:00 2001 From: yashjagtap23 <108370176+yashjagtap23@users.noreply.github.com> Date: Wed, 19 Feb 2025 20:18:47 -0600 Subject: [PATCH] Points shop fix (#626) * should display simple list of redeemed items - hopefully it works but can't check it - if it does work it may need to be reset everytime a new qr code is scanned so likely reset the array after success case * Fixed API call to redeem cart * Success/error popup on scanning * Remove events from points shop scanner --------- Co-authored-by: anushkasankaran --- HIAPI/Models/CartItem.swift | 15 +++- HIAPI/Models/Item.swift | 6 -- HIAPI/Services/ShopService.swift | 14 +--- .../HIScanPointsShopViewController.swift | 81 ++++++++++++------- 4 files changed, 66 insertions(+), 50 deletions(-) diff --git a/HIAPI/Models/CartItem.swift b/HIAPI/Models/CartItem.swift index 04127e5d..1fb58635 100644 --- a/HIAPI/Models/CartItem.swift +++ b/HIAPI/Models/CartItem.swift @@ -24,9 +24,22 @@ public struct CartItemContainer: Decodable, APIReturnable { } } +public struct RedeemReturnItem: Codable, APIReturnable { + public let userId: String? + public let items: [RedeemItem]? // Return items upon success + public let error: String? + public let message: String? +} + +public struct RedeemItem: Codable, APIReturnable { + public let itemId: String + public let name: String + public let quantity: Int +} + public struct CartReturnItem: Codable, APIReturnable { - public let items: [String: Int]? // Return items upon success public let userId: String? + public let items: [String: Int]? // Return items upon success public let error: String? public let message: String? } diff --git a/HIAPI/Models/Item.swift b/HIAPI/Models/Item.swift index 6076d597..04418102 100644 --- a/HIAPI/Models/Item.swift +++ b/HIAPI/Models/Item.swift @@ -37,9 +37,3 @@ public struct Item: Codable, Hashable { public let imageURL: String } - -public struct RedeemItem: Codable, APIReturnable { - public let itemName: String? // Return itemName upon success - public let success: Bool - public let error: String? -} diff --git a/HIAPI/Services/ShopService.swift b/HIAPI/Services/ShopService.swift index 6b35abc5..ba7495db 100644 --- a/HIAPI/Services/ShopService.swift +++ b/HIAPI/Services/ShopService.swift @@ -22,23 +22,13 @@ public final class ShopService: BaseService { return APIRequest(service: self, endpoint: "shop/cart/", headers: headers, method: .GET) } - public static func redeemPrize(itemId: String, itemInstance: String, userToken: String) -> APIRequest { - let jsonBody: [String: Any] = [ - "itemId": itemId, - "instance": itemInstance - ] - let headers: HTTPParameters = ["Authorization": userToken] - - return APIRequest(service: self, endpoint: "shop/item/buy/", body: jsonBody, headers: headers, method: .POST) - } - - public static func redeemCart(qrCode: String, userToken: String) -> APIRequest { + public static func redeemCart(qrCode: String, userToken: String) -> APIRequest { let jsonBody: [String: Any] = [ "QRCode": qrCode ] let headers: HTTPParameters = ["Authorization": userToken] - return APIRequest(service: self, endpoint: "shop/cart/redeem/", body: jsonBody, headers: headers, method: .POST) + return APIRequest(service: self, endpoint: "shop/cart/redeem/", body: jsonBody, headers: headers, method: .POST) } public static func addToCart(itemId: String, userToken: String) -> APIRequest { diff --git a/HackIllinois/ViewControllers/HIScanPointsShopViewController.swift b/HackIllinois/ViewControllers/HIScanPointsShopViewController.swift index 9e1184d8..3b58c033 100644 --- a/HackIllinois/ViewControllers/HIScanPointsShopViewController.swift +++ b/HackIllinois/ViewControllers/HIScanPointsShopViewController.swift @@ -62,17 +62,18 @@ extension HIScanPointsShopViewController { containerView.constrain(to: view, trailingInset: 0, leadingInset: 0) containerView.addSubview(previewView) setupCaptureSession() - if user.roles.contains(.STAFF) { - let observable = HIStaffButtonViewObservable() - observable.$selectedEventId.sink { eventID in - self.selectedEventID = eventID - }.store(in: &cancellables) - let staffButtonController = UIHostingController(rootView: HIStaffButtonView(observable: observable)) - addChild(staffButtonController) - staffButtonController.view.backgroundColor = .clear - staffButtonController.view.frame = CGRect(x: 0, y: 100, width: Int(view.frame.maxX), height: 600) - view.addSubview(staffButtonController.view) - } +// if user.roles.contains(.STAFF) { +// let observable = HIStaffButtonViewObservable() +// observable.$selectedEventId.sink { eventID in +// self.selectedEventID = eventID +// }.store(in: &cancellables) +// +// let staffButtonController = UIHostingController(rootView: HIStaffButtonView(observable: observable)) +// addChild(staffButtonController) +// staffButtonController.view.backgroundColor = .clear +// staffButtonController.view.frame = CGRect(x: 0, y: 100, width: Int(view.frame.maxX), height: 600) +// view.addSubview(staffButtonController.view) +// } } view.addSubview(closeButton) closeButton.addTarget(self, action: #selector(didSelectCloseButton(_:)), for: .touchUpInside) @@ -198,31 +199,38 @@ extension HIScanPointsShopViewController: AVCaptureMetadataOutputObjectsDelegate } } - func handlePointsShopAlert(status: String, itemName: String) { - print(status) + func handlePointsShopAlert(code: Int, description: String, items: [RedeemItem]) { + print("Shop alert with code: \(code)") var alertTitle = "" var alertMessage = "" var error = true - switch status { - case "Success": - alertTitle = "\n\nPrize Obtained!" - alertMessage = "\nYou have successfully redeemed \(itemName) at the Points Shop!" + switch code { + case 0: + alertTitle = "\n\nSuccess!" + if items.isEmpty { + alertMessage += "\nAttendee cart is empty." + } else { + alertMessage = "\nAttendee has successfully redeemed:\n" + for item in items { + alertMessage += "\n\(item.name): \(item.quantity)" + } + } error = false - case "invalidHTTPReponse(code: 404, description: \"forbidden\")": + case 404: alertTitle = "\n\nError!" - alertMessage = "\nUser has no attendee profile." + alertMessage = "\nShop item is not found." self.respondingToQRCodeFound = true - case "invalidHTTPReponse(code: 404, description: \"not found\")": - alertTitle = "\n\nError!" - alertMessage = "\nItem with itemId not found or already purchased." + case 402: + alertTitle = "\n\nInsufficient Funds!" + alertMessage = "\nAttendee does not have enough points to purchase." self.respondingToQRCodeFound = true - case "invalidHTTPReponse(code: 400, description: \"bad request\")": + case 400: alertTitle = "\n\nError!" - alertMessage = "\nYou have insufficient funds." + alertMessage = "\nInsufficient quantity in shop or QR is invalid/expired. Have attendee go back to cart and regenrate QR code." self.respondingToQRCodeFound = true default: alertTitle = "\n\nError!" - alertMessage = "\nSomething isn't quite right. Double check your coins amount and make sure you have the correct QR code." + alertMessage = "\nSomething isn't quite right. API returned: \(description)" self.respondingToQRCodeFound = true } // Create custom alert for points shop @@ -270,22 +278,25 @@ extension HIScanPointsShopViewController: AVCaptureMetadataOutputObjectsDelegate guard respondingToQRCodeFound else { return } let meta = metadataObjects.first as? AVMetadataMachineReadableCodeObject let code = meta?.stringValue ?? "" + let query = extractQueryValue(from: code) guard let user = HIApplicationStateController.shared.user else { return } respondingToQRCodeFound = false - HIAPI.ShopService.redeemCart(qrCode: code, userToken: user.token) + HIAPI.ShopService.redeemCart(qrCode: query ?? "", userToken: user.token) .onCompletion { result in do { let (codeResult, _) = try result.get() - let status = codeResult.error - let itemName = "Hello" - NSLog(status ?? "Success") DispatchQueue.main.async { - self.handlePointsShopAlert(status: status ?? "Success", itemName: itemName) + self.handlePointsShopAlert(code: 0, description: "", items: codeResult.items ?? []) + } + } catch APIRequestError.invalidHTTPReponse(code: let code, description: let description) { + NSLog("Error info \(code): \(description)") + DispatchQueue.main.async { + self.handlePointsShopAlert(code: code, description: "", items: []) } } catch { NSLog("Error info: \(error)") DispatchQueue.main.async { [self] in - self.handlePointsShopAlert(status: "\(error)", itemName: "") + self.handlePointsShopAlert(code: -1, description: "unable to parse API error response", items: []) } } sleep(2) @@ -294,6 +305,14 @@ extension HIScanPointsShopViewController: AVCaptureMetadataOutputObjectsDelegate .launch() } + func extractQueryValue(from url: String) -> String? { + guard let components = URLComponents(string: url), + let queryItem = components.queryItems?.first(where: { $0.name == "qr" }) else { + return nil + } + return queryItem.value + } + func decode(_ token: String) -> [String: AnyObject]? { let string = token.components(separatedBy: ".") if string.count == 1 { return nil }