diff --git a/HIAPI/Models/Event.swift b/HIAPI/Models/Event.swift index a88bc0c1..f12f48ac 100644 --- a/HIAPI/Models/Event.swift +++ b/HIAPI/Models/Event.swift @@ -47,6 +47,8 @@ public struct StaffEvent: Codable { case isAsync case isPrivate case displayOnStaffCheckin + case isStaff + case mapImageUrl } public var id: String @@ -61,6 +63,8 @@ public struct StaffEvent: Codable { public let isAsync: Bool public let isPrivate: Bool? public let displayOnStaffCheckin: Bool? + public let isStaff: Bool + public let mapImageUrl: String? } @@ -77,6 +81,8 @@ public struct Event: Codable { case points case isAsync case mapImageUrl + case displayOnStaffCheckIn + case isPro } public let id: String @@ -91,6 +97,8 @@ public struct Event: Codable { public let points: Int public let isAsync: Bool public let mapImageUrl: String? + public let displayOnStaffCheckIn: Bool? + public let isPro: Bool? } public struct Location: Codable { diff --git a/HIAPI/Models/Item.swift b/HIAPI/Models/Item.swift index 91bf9315..aac82d2b 100644 --- a/HIAPI/Models/Item.swift +++ b/HIAPI/Models/Item.swift @@ -33,3 +33,9 @@ public struct Item: Codable { 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/Models/Mentor.swift b/HIAPI/Models/Mentor.swift index 45db93eb..2425ba8d 100644 --- a/HIAPI/Models/Mentor.swift +++ b/HIAPI/Models/Mentor.swift @@ -14,5 +14,5 @@ import Foundation import APIManager public struct MentorAttendanceContainer: Codable, APIReturnable { - public let status: String + public let status: String? } diff --git a/HIAPI/Models/Profile.swift b/HIAPI/Models/Profile.swift index 7893681a..9931c089 100644 --- a/HIAPI/Models/Profile.swift +++ b/HIAPI/Models/Profile.swift @@ -28,7 +28,7 @@ public struct Profile: Codable, APIReturnable { case userId case displayName case points - //case foodWave + case foodWave case discordTag case avatarUrl case coins @@ -37,7 +37,7 @@ public struct Profile: Codable, APIReturnable { public let userId: String public let displayName: String public let points: Int - //public let foodWave: Int + public let foodWave: Int public let discordTag: String public let avatarUrl: String public let coins: Int diff --git a/HIAPI/Models/Staff.swift b/HIAPI/Models/Staff.swift index dfefdc21..805c101f 100644 --- a/HIAPI/Models/Staff.swift +++ b/HIAPI/Models/Staff.swift @@ -39,10 +39,10 @@ public struct Staff: Codable { case isPrivate case displayOnStaffCheckIn } - public let isPro: String + public let isPro: Bool public let eventId: String public let isStaff: Bool - public let name: Int + public let name: String public let description: String public let startTime: Date public let endTime: Date @@ -55,14 +55,17 @@ public struct Staff: Codable { public let isPrivate: Bool public let displayOnStaffCheckIn: Bool - } public struct UserAttendanceContainer: Codable, APIReturnable { internal enum CodingKeys: String, CodingKey { - case sucess + case success + case error + case dietaryRestrictions } - public let sucess: Bool + public let success: Bool + public let error: String? + public let dietaryRestrictions: [String]? } public struct StaffAttendanceContainer: Codable, APIReturnable { diff --git a/HIAPI/Models/User.swift b/HIAPI/Models/User.swift index 626f46fa..03d36295 100644 --- a/HIAPI/Models/User.swift +++ b/HIAPI/Models/User.swift @@ -187,3 +187,8 @@ public struct FollowStatus: Codable, APIReturnable { public let userId: String public let following: [String] } + +public struct UserCheckInStatus: Codable, APIReturnable { + public let status: String? + public let points: Int? +} diff --git a/HIAPI/Services/EventService.swift b/HIAPI/Services/EventService.swift index 121553a8..57346616 100644 --- a/HIAPI/Services/EventService.swift +++ b/HIAPI/Services/EventService.swift @@ -48,10 +48,10 @@ public class EventService: BaseService { return APIRequest(service: self, endpoint: "staff/attendance/", body: body, headers: headers, method: .POST) } - public static func getStaffCheckInEvents(authToken: String) -> APIRequest { + public static func getStaffCheckInEvents(authToken: String) -> APIRequest { var header = HTTPHeaders() header["Authorization"] = authToken - return APIRequest(service: self, endpoint: "event/filter/?displayOnStaffCheckin=true", headers: header, method: .GET) + return APIRequest(service: self, endpoint: "event/", headers: header, method: .GET) } public static func create(event: Event) -> APIRequest { diff --git a/HIAPI/Services/ShopService.swift b/HIAPI/Services/ShopService.swift index d59fb519..78be7325 100644 --- a/HIAPI/Services/ShopService.swift +++ b/HIAPI/Services/ShopService.swift @@ -17,4 +17,14 @@ public final class ShopService: BaseService { public static func getAllItems() -> APIRequest { return APIRequest(service: self, endpoint: "shop/", 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) + } } diff --git a/HIAPI/Services/StaffService.swift b/HIAPI/Services/StaffService.swift index d3dbb2ed..c0e2ac7d 100644 --- a/HIAPI/Services/StaffService.swift +++ b/HIAPI/Services/StaffService.swift @@ -32,9 +32,9 @@ public final class StaffService: BaseService { return APIRequest(service: self, endpoint: "attendance/", body: body, headers: headers, method: .POST) } - public static func recordUserAttendance(userToken: String, userId: String, eventId: String) -> APIRequest { + public static func recordUserAttendance(userToken: String, staffToken: String, eventId: String) -> APIRequest { var body = HTTPBody() - body["userId"] = userId + body["attendeeJWT"] = userToken body["eventId"] = eventId var headers = HTTPHeaders() headers["Authorization"] = userToken diff --git a/HIAPI/Services/UserService.swift b/HIAPI/Services/UserService.swift index 0773541a..b3a59eb6 100644 --- a/HIAPI/Services/UserService.swift +++ b/HIAPI/Services/UserService.swift @@ -43,4 +43,12 @@ public final class UserService: BaseService { body["eventId"] = eventID return APIRequest(service: self, endpoint: "unfollow/", body: body, headers: authorizationHeaders, method: .PUT) } + + public static func userScanEvent(userToken: String, eventID: String) -> APIRequest { + var authorizationHeaders = HTTPHeaders() + authorizationHeaders["Authorization"] = userToken + var body = HTTPBody() + body["eventId"] = eventID + return APIRequest(service: self, endpoint: "scan-event/", body: body, headers: authorizationHeaders, method: .PUT) + } } diff --git a/HackIllinois.xcodeproj/project.pbxproj b/HackIllinois.xcodeproj/project.pbxproj index dd86141d..f63fe346 100644 --- a/HackIllinois.xcodeproj/project.pbxproj +++ b/HackIllinois.xcodeproj/project.pbxproj @@ -137,11 +137,14 @@ D12B40FF2AC0B0BD001BCB05 /* HIScanAttendanceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D12B40FE2AC0B0BD001BCB05 /* HIScanAttendanceViewController.swift */; }; D134D30F296B3F56006EA589 /* Montserrat-VariableFont_wght.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D134D30E296B3F56006EA589 /* Montserrat-VariableFont_wght.ttf */; }; D14D3AE3295FBDA200EB7995 /* HIBannerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D14D3AE2295FBDA100EB7995 /* HIBannerViewController.swift */; }; + D158EC312B5A2D440003D799 /* HIQRAttendeeScannerSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = D158EC302B5A2D440003D799 /* HIQRAttendeeScannerSelection.swift */; }; + D158EC332B5A2DFE0003D799 /* HIScanPointsShopViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D158EC322B5A2DFE0003D799 /* HIScanPointsShopViewController.swift */; }; D187BF582B781A1100AD7356 /* HIShiftCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D187BF572B781A1100AD7356 /* HIShiftCell.swift */; }; - D1BBA5682B70AB940017BD13 /* Staff.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1BBA5672B70AB940017BD13 /* Staff.swift */; }; - D1BBA56A2B70ACCC0017BD13 /* StaffService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1BBA5692B70ACCC0017BD13 /* StaffService.swift */; }; D187BF5A2B78317600AD7356 /* Mentor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D187BF592B78317600AD7356 /* Mentor.swift */; }; D187BF5C2B78318000AD7356 /* MentorService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D187BF5B2B78318000AD7356 /* MentorService.swift */; }; + D19D4C932B794F9100376E1B /* HIScanMentorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D19D4C922B794F9100376E1B /* HIScanMentorViewController.swift */; }; + D1BBA5682B70AB940017BD13 /* Staff.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1BBA5672B70AB940017BD13 /* Staff.swift */; }; + D1BBA56A2B70ACCC0017BD13 /* StaffService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1BBA5692B70ACCC0017BD13 /* StaffService.swift */; }; D1BE81172AC8FC680042C078 /* HIAPI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 95E3142A21FAD5B30092C22E /* HIAPI.framework */; }; D1BE81182AC8FC690042C078 /* HIAPI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 95E3142A21FAD5B30092C22E /* HIAPI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; D1C5B6582B732E44000E766D /* refresh.json in Resources */ = {isa = PBXBuildFile; fileRef = D1C5B6572B732E44000E766D /* refresh.json */; }; @@ -352,11 +355,14 @@ D12B40FE2AC0B0BD001BCB05 /* HIScanAttendanceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HIScanAttendanceViewController.swift; sourceTree = ""; }; D134D30E296B3F56006EA589 /* Montserrat-VariableFont_wght.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Montserrat-VariableFont_wght.ttf"; sourceTree = ""; }; D14D3AE2295FBDA100EB7995 /* HIBannerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HIBannerViewController.swift; sourceTree = ""; }; + D158EC302B5A2D440003D799 /* HIQRAttendeeScannerSelection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HIQRAttendeeScannerSelection.swift; sourceTree = ""; }; + D158EC322B5A2DFE0003D799 /* HIScanPointsShopViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HIScanPointsShopViewController.swift; sourceTree = ""; }; D187BF572B781A1100AD7356 /* HIShiftCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HIShiftCell.swift; sourceTree = ""; }; - D1BBA5672B70AB940017BD13 /* Staff.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Staff.swift; sourceTree = ""; }; - D1BBA5692B70ACCC0017BD13 /* StaffService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaffService.swift; sourceTree = ""; }; D187BF592B78317600AD7356 /* Mentor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Mentor.swift; sourceTree = ""; }; D187BF5B2B78318000AD7356 /* MentorService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MentorService.swift; sourceTree = ""; }; + D19D4C922B794F9100376E1B /* HIScanMentorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HIScanMentorViewController.swift; sourceTree = ""; }; + D1BBA5672B70AB940017BD13 /* Staff.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Staff.swift; sourceTree = ""; }; + D1BBA5692B70ACCC0017BD13 /* StaffService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaffService.swift; sourceTree = ""; }; D1C5B6572B732E44000E766D /* refresh.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = refresh.json; sourceTree = ""; }; D1F146392B605C57004E7FC9 /* Hack_Mushroom_Loading.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Hack_Mushroom_Loading.json; sourceTree = ""; }; D3A309BA221116A600CBA351 /* HackIllinois.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = HackIllinois.entitlements; sourceTree = ""; }; @@ -587,6 +593,7 @@ 95C3BE2F2008B99C0008ED79 /* HIBaseViewController.swift */, 35240BD0201408DB00E0C0D8 /* HICountdownViewController.swift */, D14D3AE2295FBDA100EB7995 /* HIBannerViewController.swift */, + D158EC302B5A2D440003D799 /* HIQRAttendeeScannerSelection.swift */, 95C18DC720096539004784B5 /* HIEventDetailViewController.swift */, 95C18DC920096539004784B5 /* HIEventListViewController.swift */, 9515E43622093E1300BCAF92 /* HIEventScannerViewController.swift */, @@ -601,6 +608,8 @@ 60EAF98A273B1AC4002CD205 /* HIScanQRCodeViewController.swift */, D12B40FE2AC0B0BD001BCB05 /* HIScanAttendanceViewController.swift */, E1BF5E79298B462F00A98390 /* HIStaffButtonView.swift */, + D158EC322B5A2DFE0003D799 /* HIScanPointsShopViewController.swift */, + D19D4C922B794F9100376E1B /* HIScanMentorViewController.swift */, D128F2942ABFF93B0016D8CB /* HIQRScannerSelection.swift */, 1C9045D3274F065F00DDA74E /* HIOnboardingViewController.swift */, 3FBFA7AA27335554001FFF28 /* HILeaderboardListViewController.swift */, @@ -1063,6 +1072,7 @@ 95838AC921FDCB5C00BD8F71 /* Location+CoreDataProperties.swift in Sources */, 95838ACA21FDCB5C00BD8F71 /* Location+CoreDataClass.swift in Sources */, 9521A6AA20191737009059C6 /* HIAnnouncementDataSource.swift in Sources */, + D19D4C932B794F9100376E1B /* HIScanMentorViewController.swift in Sources */, 95C18DCB20096539004784B5 /* HIScheduleViewController.swift in Sources */, ACCB199D25F56DF100F88379 /* HIInterestCell.swift in Sources */, 95CCEA1F203D6B3600E3E28C /* HINavigationController.swift in Sources */, @@ -1092,6 +1102,7 @@ 95A7D28A203D4F54005EAEAF /* HIButton.swift in Sources */, 95A7D28E203D4F54005EAEAF /* HIView.swift in Sources */, 3CF8DB1323B419980035ECAE /* HIProjectCell.swift in Sources */, + D158EC332B5A2DFE0003D799 /* HIScanPointsShopViewController.swift in Sources */, ACBFE29A25EB2C9700C94732 /* HIInterest.swift in Sources */, 35B4A44F21E091BA001B3AF0 /* Logger.swift in Sources */, 9544799C200C39870005408B /* HIUser.swift in Sources */, @@ -1102,6 +1113,7 @@ 95A7D28C203D4F54005EAEAF /* HITextField.swift in Sources */, D12B40FF2AC0B0BD001BCB05 /* HIScanAttendanceViewController.swift in Sources */, 95C18DCA20096539004784B5 /* HIEventDetailViewController.swift in Sources */, + D158EC312B5A2D440003D799 /* HIQRAttendeeScannerSelection.swift in Sources */, 9523A2902218BA2600341EBD /* HIImageView.swift in Sources */, 2FF0F9322B58D5A200017D08 /* HIPointsShopSwiftUIView.swift in Sources */, 95C18DC52009618C004784B5 /* HIHomeViewController.swift in Sources */, diff --git a/HackIllinois/Assets.xcassets/Attendee.imageset/Attendee.png b/HackIllinois/Assets.xcassets/Attendee.imageset/Attendee.png new file mode 100644 index 00000000..e51dfc3e Binary files /dev/null and b/HackIllinois/Assets.xcassets/Attendee.imageset/Attendee.png differ diff --git a/HackIllinois/Assets.xcassets/Attendee.imageset/Attendee2x.png b/HackIllinois/Assets.xcassets/Attendee.imageset/Attendee2x.png new file mode 100644 index 00000000..01812b45 Binary files /dev/null and b/HackIllinois/Assets.xcassets/Attendee.imageset/Attendee2x.png differ diff --git a/HackIllinois/Assets.xcassets/Attendee.imageset/Attendee3x.png b/HackIllinois/Assets.xcassets/Attendee.imageset/Attendee3x.png new file mode 100644 index 00000000..c4c09c08 Binary files /dev/null and b/HackIllinois/Assets.xcassets/Attendee.imageset/Attendee3x.png differ diff --git a/HackIllinois/Assets.xcassets/Attendee.imageset/Contents.json b/HackIllinois/Assets.xcassets/Attendee.imageset/Contents.json new file mode 100644 index 00000000..3e4d5c0f --- /dev/null +++ b/HackIllinois/Assets.xcassets/Attendee.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Attendee.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Attendee2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Attendee3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/HackIllinois/Assets.xcassets/BookmarkSelected.imageset/BookmarkSelected1x.png b/HackIllinois/Assets.xcassets/BookmarkSelected.imageset/BookmarkSelected1x.png new file mode 100644 index 00000000..29c822d5 Binary files /dev/null and b/HackIllinois/Assets.xcassets/BookmarkSelected.imageset/BookmarkSelected1x.png differ diff --git a/HackIllinois/Assets.xcassets/BookmarkSelected.imageset/BookmarkSelected2x.png b/HackIllinois/Assets.xcassets/BookmarkSelected.imageset/BookmarkSelected2x.png new file mode 100644 index 00000000..e4e1e89d Binary files /dev/null and b/HackIllinois/Assets.xcassets/BookmarkSelected.imageset/BookmarkSelected2x.png differ diff --git a/HackIllinois/Assets.xcassets/BookmarkSelected.imageset/BookmarkSelected3x.png b/HackIllinois/Assets.xcassets/BookmarkSelected.imageset/BookmarkSelected3x.png new file mode 100644 index 00000000..06cd9443 Binary files /dev/null and b/HackIllinois/Assets.xcassets/BookmarkSelected.imageset/BookmarkSelected3x.png differ diff --git a/HackIllinois/Assets.xcassets/BookmarkSelected.imageset/Contents.json b/HackIllinois/Assets.xcassets/BookmarkSelected.imageset/Contents.json new file mode 100644 index 00000000..0e354117 --- /dev/null +++ b/HackIllinois/Assets.xcassets/BookmarkSelected.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "BookmarkSelected1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "BookmarkSelected2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "BookmarkSelected3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/HackIllinois/Assets.xcassets/BookmarkUnselected.imageset/BookmarkUnselected.png b/HackIllinois/Assets.xcassets/BookmarkUnselected.imageset/BookmarkUnselected.png new file mode 100644 index 00000000..2a4bda05 Binary files /dev/null and b/HackIllinois/Assets.xcassets/BookmarkUnselected.imageset/BookmarkUnselected.png differ diff --git a/HackIllinois/Assets.xcassets/BookmarkUnselected.imageset/BookmarkUnselected2x.png b/HackIllinois/Assets.xcassets/BookmarkUnselected.imageset/BookmarkUnselected2x.png new file mode 100644 index 00000000..0bafc9b3 Binary files /dev/null and b/HackIllinois/Assets.xcassets/BookmarkUnselected.imageset/BookmarkUnselected2x.png differ diff --git a/HackIllinois/Assets.xcassets/BookmarkUnselected.imageset/BookmarkUnselected3x.png b/HackIllinois/Assets.xcassets/BookmarkUnselected.imageset/BookmarkUnselected3x.png new file mode 100644 index 00000000..93207606 Binary files /dev/null and b/HackIllinois/Assets.xcassets/BookmarkUnselected.imageset/BookmarkUnselected3x.png differ diff --git a/HackIllinois/Assets.xcassets/BookmarkUnselected.imageset/Contents.json b/HackIllinois/Assets.xcassets/BookmarkUnselected.imageset/Contents.json new file mode 100644 index 00000000..2a35150f --- /dev/null +++ b/HackIllinois/Assets.xcassets/BookmarkUnselected.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "BookmarkUnselected.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "BookmarkUnselected2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "BookmarkUnselected3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/HackIllinois/Assets.xcassets/GrayPotion.imageset/Contents.json b/HackIllinois/Assets.xcassets/GrayPotion.imageset/Contents.json new file mode 100644 index 00000000..75944daa --- /dev/null +++ b/HackIllinois/Assets.xcassets/GrayPotion.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "GrayPotion1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "GrayPotion2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "GrayPotion3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/HackIllinois/Assets.xcassets/GrayPotion.imageset/GrayPotion1x.png b/HackIllinois/Assets.xcassets/GrayPotion.imageset/GrayPotion1x.png new file mode 100644 index 00000000..0e244520 Binary files /dev/null and b/HackIllinois/Assets.xcassets/GrayPotion.imageset/GrayPotion1x.png differ diff --git a/HackIllinois/Assets.xcassets/GrayPotion.imageset/GrayPotion2x.png b/HackIllinois/Assets.xcassets/GrayPotion.imageset/GrayPotion2x.png new file mode 100644 index 00000000..28724cb0 Binary files /dev/null and b/HackIllinois/Assets.xcassets/GrayPotion.imageset/GrayPotion2x.png differ diff --git a/HackIllinois/Assets.xcassets/GrayPotion.imageset/GrayPotion3x.png b/HackIllinois/Assets.xcassets/GrayPotion.imageset/GrayPotion3x.png new file mode 100644 index 00000000..f0c3da79 Binary files /dev/null and b/HackIllinois/Assets.xcassets/GrayPotion.imageset/GrayPotion3x.png differ diff --git a/HackIllinois/Assets.xcassets/GreenPotion.imageset/Contents.json b/HackIllinois/Assets.xcassets/GreenPotion.imageset/Contents.json new file mode 100644 index 00000000..c7479437 --- /dev/null +++ b/HackIllinois/Assets.xcassets/GreenPotion.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "greenPotion.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "greenPotion2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "greenPotion3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/HackIllinois/Assets.xcassets/GreenPotion.imageset/greenPotion.png b/HackIllinois/Assets.xcassets/GreenPotion.imageset/greenPotion.png new file mode 100644 index 00000000..7783e5e0 Binary files /dev/null and b/HackIllinois/Assets.xcassets/GreenPotion.imageset/greenPotion.png differ diff --git a/HackIllinois/Assets.xcassets/GreenPotion.imageset/greenPotion2x.png b/HackIllinois/Assets.xcassets/GreenPotion.imageset/greenPotion2x.png new file mode 100644 index 00000000..5e77a4c1 Binary files /dev/null and b/HackIllinois/Assets.xcassets/GreenPotion.imageset/greenPotion2x.png differ diff --git a/HackIllinois/Assets.xcassets/GreenPotion.imageset/greenPotion3x.png b/HackIllinois/Assets.xcassets/GreenPotion.imageset/greenPotion3x.png new file mode 100644 index 00000000..743a1bb0 Binary files /dev/null and b/HackIllinois/Assets.xcassets/GreenPotion.imageset/greenPotion3x.png differ diff --git a/HackIllinois/Assets.xcassets/Home/Question Mark Toggled.imageset/Question Mark Toggled.png b/HackIllinois/Assets.xcassets/Home/Question Mark Toggled.imageset/Question Mark Toggled.png index e3e0eb66..348e9ed9 100644 Binary files a/HackIllinois/Assets.xcassets/Home/Question Mark Toggled.imageset/Question Mark Toggled.png and b/HackIllinois/Assets.xcassets/Home/Question Mark Toggled.imageset/Question Mark Toggled.png differ diff --git a/HackIllinois/Assets.xcassets/Home/Question Mark Toggled.imageset/Question Mark Toggled@2x.png b/HackIllinois/Assets.xcassets/Home/Question Mark Toggled.imageset/Question Mark Toggled@2x.png index 228d2ea1..83b008ee 100644 Binary files a/HackIllinois/Assets.xcassets/Home/Question Mark Toggled.imageset/Question Mark Toggled@2x.png and b/HackIllinois/Assets.xcassets/Home/Question Mark Toggled.imageset/Question Mark Toggled@2x.png differ diff --git a/HackIllinois/Assets.xcassets/Home/Question Mark Toggled.imageset/Question Mark Toggled@3x.png b/HackIllinois/Assets.xcassets/Home/Question Mark Toggled.imageset/Question Mark Toggled@3x.png index ef029352..a2809b1f 100644 Binary files a/HackIllinois/Assets.xcassets/Home/Question Mark Toggled.imageset/Question Mark Toggled@3x.png and b/HackIllinois/Assets.xcassets/Home/Question Mark Toggled.imageset/Question Mark Toggled@3x.png differ diff --git a/HackIllinois/Assets.xcassets/Home/Question Mark.imageset/Contents.json b/HackIllinois/Assets.xcassets/Home/Question Mark.imageset/Contents.json index 20e5554a..d5c27e8e 100644 --- a/HackIllinois/Assets.xcassets/Home/Question Mark.imageset/Contents.json +++ b/HackIllinois/Assets.xcassets/Home/Question Mark.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "Question Mark.png", + "filename" : "Question Mark@1x.png", "idiom" : "universal", "scale" : "1x" }, diff --git a/HackIllinois/Assets.xcassets/Home/Question Mark.imageset/Question Mark.png b/HackIllinois/Assets.xcassets/Home/Question Mark.imageset/Question Mark.png deleted file mode 100644 index a0803ff2..00000000 Binary files a/HackIllinois/Assets.xcassets/Home/Question Mark.imageset/Question Mark.png and /dev/null differ diff --git a/HackIllinois/Assets.xcassets/Home/Question Mark.imageset/Question Mark@1x.png b/HackIllinois/Assets.xcassets/Home/Question Mark.imageset/Question Mark@1x.png new file mode 100644 index 00000000..f6fe9280 Binary files /dev/null and b/HackIllinois/Assets.xcassets/Home/Question Mark.imageset/Question Mark@1x.png differ diff --git a/HackIllinois/Assets.xcassets/Home/Question Mark.imageset/Question Mark@2x.png b/HackIllinois/Assets.xcassets/Home/Question Mark.imageset/Question Mark@2x.png index 2d9a494c..7697aa6e 100644 Binary files a/HackIllinois/Assets.xcassets/Home/Question Mark.imageset/Question Mark@2x.png and b/HackIllinois/Assets.xcassets/Home/Question Mark.imageset/Question Mark@2x.png differ diff --git a/HackIllinois/Assets.xcassets/Home/Question Mark.imageset/Question Mark@3x.png b/HackIllinois/Assets.xcassets/Home/Question Mark.imageset/Question Mark@3x.png index 5f51ee0e..d36d36b2 100644 Binary files a/HackIllinois/Assets.xcassets/Home/Question Mark.imageset/Question Mark@3x.png and b/HackIllinois/Assets.xcassets/Home/Question Mark.imageset/Question Mark@3x.png differ diff --git a/HackIllinois/Assets.xcassets/Prize Bag Sad.imageset/Contents.json b/HackIllinois/Assets.xcassets/Prize Bag Sad.imageset/Contents.json new file mode 100644 index 00000000..3856bdd5 --- /dev/null +++ b/HackIllinois/Assets.xcassets/Prize Bag Sad.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Prize Bag Sad.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Prize Bag Sad2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Prize Bag Sad3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/HackIllinois/Assets.xcassets/Prize Bag Sad.imageset/Prize Bag Sad.png b/HackIllinois/Assets.xcassets/Prize Bag Sad.imageset/Prize Bag Sad.png new file mode 100644 index 00000000..1029726a Binary files /dev/null and b/HackIllinois/Assets.xcassets/Prize Bag Sad.imageset/Prize Bag Sad.png differ diff --git a/HackIllinois/Assets.xcassets/Prize Bag Sad.imageset/Prize Bag Sad2x.png b/HackIllinois/Assets.xcassets/Prize Bag Sad.imageset/Prize Bag Sad2x.png new file mode 100644 index 00000000..235598f6 Binary files /dev/null and b/HackIllinois/Assets.xcassets/Prize Bag Sad.imageset/Prize Bag Sad2x.png differ diff --git a/HackIllinois/Assets.xcassets/Prize Bag Sad.imageset/Prize Bag Sad3x.png b/HackIllinois/Assets.xcassets/Prize Bag Sad.imageset/Prize Bag Sad3x.png new file mode 100644 index 00000000..c05f5489 Binary files /dev/null and b/HackIllinois/Assets.xcassets/Prize Bag Sad.imageset/Prize Bag Sad3x.png differ diff --git a/HackIllinois/Assets.xcassets/Prize Bag.imageset/Contents.json b/HackIllinois/Assets.xcassets/Prize Bag.imageset/Contents.json new file mode 100644 index 00000000..b1318aef --- /dev/null +++ b/HackIllinois/Assets.xcassets/Prize Bag.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Prize Bag.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Prize Bag2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Prize Bag3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/HackIllinois/Assets.xcassets/Prize Bag.imageset/Prize Bag.png b/HackIllinois/Assets.xcassets/Prize Bag.imageset/Prize Bag.png new file mode 100644 index 00000000..93149e8d Binary files /dev/null and b/HackIllinois/Assets.xcassets/Prize Bag.imageset/Prize Bag.png differ diff --git a/HackIllinois/Assets.xcassets/Prize Bag.imageset/Prize Bag2x.png b/HackIllinois/Assets.xcassets/Prize Bag.imageset/Prize Bag2x.png new file mode 100644 index 00000000..1cb4ee67 Binary files /dev/null and b/HackIllinois/Assets.xcassets/Prize Bag.imageset/Prize Bag2x.png differ diff --git a/HackIllinois/Assets.xcassets/Prize Bag.imageset/Prize Bag3x.png b/HackIllinois/Assets.xcassets/Prize Bag.imageset/Prize Bag3x.png new file mode 100644 index 00000000..8d2979dd Binary files /dev/null and b/HackIllinois/Assets.xcassets/Prize Bag.imageset/Prize Bag3x.png differ diff --git a/HackIllinois/Assets.xcassets/ProfileView/ProfileCardBackground.imageset/Contents.json b/HackIllinois/Assets.xcassets/ProfileView/ProfileCardBackground.imageset/Contents.json index f25f56a9..a0715f52 100644 --- a/HackIllinois/Assets.xcassets/ProfileView/ProfileCardBackground.imageset/Contents.json +++ b/HackIllinois/Assets.xcassets/ProfileView/ProfileCardBackground.imageset/Contents.json @@ -1,17 +1,17 @@ { "images" : [ { - "filename" : "Group 344.png", + "filename" : "ProfileCard1x.png", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "Group 344 (1).png", + "filename" : "ProfileCard2x.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "Group 344 (2).png", + "filename" : "ProfileCard3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/HackIllinois/Assets.xcassets/ProfileView/ProfileCardBackground.imageset/Group 344 (1).png b/HackIllinois/Assets.xcassets/ProfileView/ProfileCardBackground.imageset/Group 344 (1).png deleted file mode 100644 index 3f4a7afc..00000000 Binary files a/HackIllinois/Assets.xcassets/ProfileView/ProfileCardBackground.imageset/Group 344 (1).png and /dev/null differ diff --git a/HackIllinois/Assets.xcassets/ProfileView/ProfileCardBackground.imageset/Group 344 (2).png b/HackIllinois/Assets.xcassets/ProfileView/ProfileCardBackground.imageset/Group 344 (2).png deleted file mode 100644 index 1be89a35..00000000 Binary files a/HackIllinois/Assets.xcassets/ProfileView/ProfileCardBackground.imageset/Group 344 (2).png and /dev/null differ diff --git a/HackIllinois/Assets.xcassets/ProfileView/ProfileCardBackground.imageset/Group 344.png b/HackIllinois/Assets.xcassets/ProfileView/ProfileCardBackground.imageset/Group 344.png deleted file mode 100644 index 8311b585..00000000 Binary files a/HackIllinois/Assets.xcassets/ProfileView/ProfileCardBackground.imageset/Group 344.png and /dev/null differ diff --git a/HackIllinois/Assets.xcassets/ProfileView/ProfileCardBackground.imageset/ProfileCard1x.png b/HackIllinois/Assets.xcassets/ProfileView/ProfileCardBackground.imageset/ProfileCard1x.png new file mode 100644 index 00000000..47cf7e41 Binary files /dev/null and b/HackIllinois/Assets.xcassets/ProfileView/ProfileCardBackground.imageset/ProfileCard1x.png differ diff --git a/HackIllinois/Assets.xcassets/ProfileView/ProfileCardBackground.imageset/ProfileCard2x.png b/HackIllinois/Assets.xcassets/ProfileView/ProfileCardBackground.imageset/ProfileCard2x.png new file mode 100644 index 00000000..40c82cd5 Binary files /dev/null and b/HackIllinois/Assets.xcassets/ProfileView/ProfileCardBackground.imageset/ProfileCard2x.png differ diff --git a/HackIllinois/Assets.xcassets/ProfileView/ProfileCardBackground.imageset/ProfileCard3x.png b/HackIllinois/Assets.xcassets/ProfileView/ProfileCardBackground.imageset/ProfileCard3x.png new file mode 100644 index 00000000..98c43867 Binary files /dev/null and b/HackIllinois/Assets.xcassets/ProfileView/ProfileCardBackground.imageset/ProfileCard3x.png differ diff --git a/HackIllinois/Assets.xcassets/Staff.imageset/Contents.json b/HackIllinois/Assets.xcassets/Staff.imageset/Contents.json new file mode 100644 index 00000000..cb838682 --- /dev/null +++ b/HackIllinois/Assets.xcassets/Staff.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Staff.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Staff2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Staff3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/HackIllinois/Assets.xcassets/Staff.imageset/Staff.png b/HackIllinois/Assets.xcassets/Staff.imageset/Staff.png new file mode 100644 index 00000000..37e4dfdb Binary files /dev/null and b/HackIllinois/Assets.xcassets/Staff.imageset/Staff.png differ diff --git a/HackIllinois/Assets.xcassets/Staff.imageset/Staff2x.png b/HackIllinois/Assets.xcassets/Staff.imageset/Staff2x.png new file mode 100644 index 00000000..1f65d840 Binary files /dev/null and b/HackIllinois/Assets.xcassets/Staff.imageset/Staff2x.png differ diff --git a/HackIllinois/Assets.xcassets/Staff.imageset/Staff3x.png b/HackIllinois/Assets.xcassets/Staff.imageset/Staff3x.png new file mode 100644 index 00000000..9cbd8a74 Binary files /dev/null and b/HackIllinois/Assets.xcassets/Staff.imageset/Staff3x.png differ diff --git a/HackIllinois/Assets.xcassets/Treasure Chest.imageset/Contents.json b/HackIllinois/Assets.xcassets/Treasure Chest.imageset/Contents.json new file mode 100644 index 00000000..848be307 --- /dev/null +++ b/HackIllinois/Assets.xcassets/Treasure Chest.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Treasure Chest1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Treasure Chest2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Treasure Chest3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/HackIllinois/Assets.xcassets/Treasure Chest.imageset/Treasure Chest1x.png b/HackIllinois/Assets.xcassets/Treasure Chest.imageset/Treasure Chest1x.png new file mode 100644 index 00000000..8f1240b2 Binary files /dev/null and b/HackIllinois/Assets.xcassets/Treasure Chest.imageset/Treasure Chest1x.png differ diff --git a/HackIllinois/Assets.xcassets/Treasure Chest.imageset/Treasure Chest2x.png b/HackIllinois/Assets.xcassets/Treasure Chest.imageset/Treasure Chest2x.png new file mode 100644 index 00000000..86613940 Binary files /dev/null and b/HackIllinois/Assets.xcassets/Treasure Chest.imageset/Treasure Chest2x.png differ diff --git a/HackIllinois/Assets.xcassets/Treasure Chest.imageset/Treasure Chest3x.png b/HackIllinois/Assets.xcassets/Treasure Chest.imageset/Treasure Chest3x.png new file mode 100644 index 00000000..bfb60378 Binary files /dev/null and b/HackIllinois/Assets.xcassets/Treasure Chest.imageset/Treasure Chest3x.png differ diff --git a/HackIllinois/DataSources/HIEventDataSource.swift b/HackIllinois/DataSources/HIEventDataSource.swift index 217b54d6..a7ff21e8 100644 --- a/HackIllinois/DataSources/HIEventDataSource.swift +++ b/HackIllinois/DataSources/HIEventDataSource.swift @@ -122,6 +122,7 @@ final class HIEventDataSource { coreDataEvent.points = Int32(apiEvent.points) coreDataEvent.favorite = false coreDataEvent.mapImageUrl = apiEvent.mapImageUrl ?? "None" + coreDataEvent.isPro = apiEvent.isPro ?? false } apiEventsToInsert.forEach { apiEvent in @@ -144,6 +145,7 @@ final class HIEventDataSource { coreDataEvent.favorite = false coreDataEvent.isAsync = apiEvent.isAsync coreDataEvent.mapImageUrl = apiEvent.mapImageUrl ?? "None" + coreDataEvent.isPro = apiEvent.isPro ?? false } // 10) Save changes, call completion handler, unlock refresh @@ -198,6 +200,7 @@ final class HIEventDataSource { coreDataEvent.points = Int32(apiEvent.points) coreDataEvent.favorite = apiFavorites.contains(coreDataEvent.id) coreDataEvent.mapImageUrl = apiEvent.mapImageUrl ?? "None" + coreDataEvent.isPro = apiEvent.isPro ?? false } try context.save() diff --git a/HackIllinois/FlowControllers/HITabBarController.swift b/HackIllinois/FlowControllers/HITabBarController.swift index 0ac6a888..9ee46af9 100644 --- a/HackIllinois/FlowControllers/HITabBarController.swift +++ b/HackIllinois/FlowControllers/HITabBarController.swift @@ -43,8 +43,8 @@ class HITabBarController: UITabBarController { @objc private func qrScannerPopupButtonPressed(_ sender: UIButton) { guard let user = HIApplicationStateController.shared.user else { return } - if (HIApplicationStateController.shared.isGuest && !user.roles.contains(.STAFF)) || !user.roles.contains(.STAFF) { - let scanQRCodePopup = HIScanQRCodeViewController() + if user.roles.contains(.ATTENDEE) && !user.roles.contains(.STAFF) { + let scanQRCodePopup = HIQRAttendeeScannerSelection() scanQRCodePopup.modalPresentationStyle = .overFullScreen scanQRCodePopup.modalTransitionStyle = .crossDissolve self.present(scanQRCodePopup, animated: true, completion: nil) @@ -53,6 +53,11 @@ class HITabBarController: UITabBarController { scanQRCodePopup.modalPresentationStyle = .overFullScreen scanQRCodePopup.modalTransitionStyle = .crossDissolve self.present(scanQRCodePopup, animated: true, completion: nil) + } else if HIApplicationStateController.shared.isGuest { + let scanQRCodePopup = HIScanQRCodeViewController() + scanQRCodePopup.modalPresentationStyle = .overFullScreen + scanQRCodePopup.modalTransitionStyle = .crossDissolve + self.present(scanQRCodePopup, animated: true, completion: nil) } } diff --git a/HackIllinois/Models/CoreDataModels/Event+CoreDataProperties.swift b/HackIllinois/Models/CoreDataModels/Event+CoreDataProperties.swift index 532771ef..d167d199 100644 --- a/HackIllinois/Models/CoreDataModels/Event+CoreDataProperties.swift +++ b/HackIllinois/Models/CoreDataModels/Event+CoreDataProperties.swift @@ -32,6 +32,7 @@ extension Event { @NSManaged public var points: Int32 @NSManaged public var isAsync: Bool @NSManaged public var mapImageUrl: String + @NSManaged public var isPro: Bool @objc dynamic var sectionIdentifier: Date { let excessComponents: Set = [.second, .nanosecond] diff --git a/HackIllinois/Models/CoreDataModels/HackIllinois.xcdatamodeld/HackIllinois.xcdatamodel/contents b/HackIllinois/Models/CoreDataModels/HackIllinois.xcdatamodeld/HackIllinois.xcdatamodel/contents index dfd14fbf..42dcdba9 100644 --- a/HackIllinois/Models/CoreDataModels/HackIllinois.xcdatamodeld/HackIllinois.xcdatamodel/contents +++ b/HackIllinois/Models/CoreDataModels/HackIllinois.xcdatamodeld/HackIllinois.xcdatamodel/contents @@ -18,6 +18,7 @@ + diff --git a/HackIllinois/Models/HIProfile.swift b/HackIllinois/Models/HIProfile.swift index 89eb3c1f..58d46fbb 100644 --- a/HackIllinois/Models/HIProfile.swift +++ b/HackIllinois/Models/HIProfile.swift @@ -24,7 +24,7 @@ struct HIProfile: Codable { var userId = "" var displayName = "" var points = 0 - //var foodWave = 0 + var foodWave = 0 var timezone = "" var discordTag = "" var avatarUrl = "" diff --git a/HackIllinois/UI/HIAppearance.swift b/HackIllinois/UI/HIAppearance.swift index 6f5098a5..2dab07e3 100644 --- a/HackIllinois/UI/HIAppearance.swift +++ b/HackIllinois/UI/HIAppearance.swift @@ -20,7 +20,7 @@ struct HIAppearance: Equatable { let lightYellowText: UIColor let baseText: UIColor let accent: UIColor - let viewTitleGreen: UIColor + let viewTitleBrown: UIColor let action: UIColor let baseBackground: UIColor let contentBackground: UIColor @@ -65,12 +65,10 @@ struct HIAppearance: Equatable { let profile8: UIImage let profile9: UIImage let profile10: UIImage - let leaderboardText: UIColor let leaderboardBackgroundOne: UIColor let leaderboardBackgroundTwo: UIColor let pointsBackground: UIColor - let profileCardBackground: UIColor let profileCardVegetarian: UIColor let profileCardVegan: UIColor @@ -78,39 +76,33 @@ struct HIAppearance: Equatable { let profileCardLactoseIntolerant: UIColor let profileCardOther: UIColor let profileCardNone: UIColor - let profileBaseText: UIColor - let countdownTextColor: UIColor let countdownBackground: UIColor let bannerBackground: UIColor + let scannerButtonPink: UIColor + let scannerButtonTeal: UIColor + let scannerButtonYellow: UIColor + let scannerButtonBorder: UIColor + let scannerButtonShadow: UIColor let proBackground: UIColor let eventCard: UIColor - let clear: UIColor = .clear let black: UIColor = .black let white: UIColor = .white - // Hack 2023 color palette - private static let lightBlack = #colorLiteral(red: 0.03137254902, green: 0.08235294118, blue: 0.1058823529, alpha: 1) - private static let yellowWhite = #colorLiteral(red: 0.9647058824, green: 0.9568627451, blue: 0.831372549, alpha: 1) - private static let lightYellow = #colorLiteral(red: 0.9882352941, green: 0.862745098, blue: 0.5607843137, alpha: 1) - private static let tan = #colorLiteral(red: 0.9098039216, green: 0.8431372549, blue: 0.6470588235, alpha: 1) - private static let blue = #colorLiteral(red: 0.03137254902, green: 0.5450980392, blue: 0.7568627451, alpha: 1) - private static let mediumOrange = #colorLiteral(red: 0.9764705882, green: 0.5843137255, blue: 0.3411764706, alpha: 1) - private static let darkBlue = #colorLiteral(red: 0.05098039216, green: 0.2196078431, blue: 0.4862745098, alpha: 1) - private static let magenta = #colorLiteral(red: 0.9960784314, green: 0.4392156863, blue: 0.5960784314, alpha: 1) - private static let darkBlueGreen = #colorLiteral(red: 0.03137254902, green: 0.5450980392, blue: 0.7568627451, alpha: 1) - private static let vegetarian = #colorLiteral(red: 0.2156862745, green: 0.8705882353, blue: 0.8039215686, alpha: 1) - private static let vegan = #colorLiteral(red: 0.9882352941, green: 0.862745098, blue: 0.5607843137, alpha: 1) - private static let glutenfree = #colorLiteral(red: 0.9764705882, green: 0.5843137255, blue: 0.3411764706, alpha: 1) + private static let lightBlack = #colorLiteral(red: 0.03137254902, green: 0.08235294118, blue: 0.1058823529, alpha: 1); private static let yellowWhite = #colorLiteral(red: 0.9647058824, green: 0.9568627451, blue: 0.831372549, alpha: 1) + private static let lightYellow = #colorLiteral(red: 0.9882352941, green: 0.862745098, blue: 0.5607843137, alpha: 1); private static let tan = #colorLiteral(red: 0.9098039216, green: 0.8431372549, blue: 0.6470588235, alpha: 1) + private static let blue = #colorLiteral(red: 0.03137254902, green: 0.5450980392, blue: 0.7568627451, alpha: 1); private static let mediumOrange = #colorLiteral(red: 0.9764705882, green: 0.5843137255, blue: 0.3411764706, alpha: 1) + private static let darkBlue = #colorLiteral(red: 0.05098039216, green: 0.2196078431, blue: 0.4862745098, alpha: 1); private static let magenta = #colorLiteral(red: 0.9960784314, green: 0.4392156863, blue: 0.5960784314, alpha: 1) + private static let darkBlueGreen = #colorLiteral(red: 0.03137254902, green: 0.5450980392, blue: 0.7568627451, alpha: 1); private static let vegetarian = #colorLiteral(red: 0.2156862745, green: 0.8705882353, blue: 0.8039215686, alpha: 1) + private static let vegan = #colorLiteral(red: 0.9882352941, green: 0.862745098, blue: 0.5607843137, alpha: 1); private static let glutenfree = #colorLiteral(red: 0.9764705882, green: 0.5843137255, blue: 0.3411764706, alpha: 1) private static let lactoseintolerant = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1) private static let other = #colorLiteral(red: 0.9960784314, green: 0.6392156863, blue: 0.6666666667, alpha: 1) private static let none = #colorLiteral(red: 0.1137254902, green: 0.1490196078, blue: 0.2666666667, alpha: 1) private static let profileBaseText = #colorLiteral(red: 0.1137254902, green: 0.1490196078, blue: 0.2666666667, alpha: 1) private static let yellowGreen = #colorLiteral(red: 0.7882352941, green: 0.8117647059, blue: 0.462745098, alpha: 1) private static let green = #colorLiteral(red: 0.4705882353, green: 0.6745098039, blue: 0.2588235294, alpha: 1) - // Hack 2024 color palette private static let icyBlue = #colorLiteral(red: 0.8235294118, green: 0.968627451, blue: 1, alpha: 1) private static let bLightYellow = #colorLiteral(red: 1, green: 0.9803921569, blue: 0.8, alpha: 1) @@ -126,6 +118,7 @@ struct HIAppearance: Equatable { private static let darkGreen = #colorLiteral(red: 0.05098039216, green: 0.2470588235, blue: 0.2549019608, alpha: 1) private static let darkestGreen = #colorLiteral(red: 0.02745098039, green: 0.1725490196, blue: 0.1803921569, alpha: 1) private static let lightBrown = #colorLiteral(red: 0.5882352941, green: 0.2980392157, blue: 0.1019607843, alpha: 1) + private static let medBrown = #colorLiteral(red: 0.4588235294, green: 0.1960784314, blue: 0.07843137255, alpha: 1) private static let brown = #colorLiteral(red: 0.4, green: 0.168627451, blue: 0.07450980392, alpha: 1) private static let darkBrown = #colorLiteral(red: 0.337254902, green: 0.1411764706, blue: 0.06666666667, alpha: 1) private static let lightPink = #colorLiteral(red: 0.9215686275, green: 0.8235294118, blue: 0.8235294118, alpha: 1) @@ -137,8 +130,10 @@ struct HIAppearance: Equatable { private static let transparent = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 0) private static let white = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1) private static let black = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1) + private static let buttonPink = #colorLiteral(red: 0.862745098, green: 0.5411764706, blue: 0.662745098, alpha: 1) + private static let buttonTeal = #colorLiteral(red: 0.6470588235, green: 0.8549019608, blue: 0.8352941176, alpha: 1) + private static let buttonYellow = #colorLiteral(red: 1, green: 0.7882352941, blue: 0.3568627451, alpha: 1) private static let offWhite = #colorLiteral(red: 1, green: 0.9725490196, blue: 0.9607843137, alpha: 1) - private static let transparent2 = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 0) private static var statusBarWhite: UIStatusBarStyle { return .lightContent @@ -159,7 +154,7 @@ struct HIAppearance: Equatable { lightYellowText: bLightYellow, baseText: lightBlack, accent: white, - viewTitleGreen: darkGreen, + viewTitleBrown: darkBrown, action: tan, baseBackground: white, contentBackground: offWhite, @@ -219,6 +214,11 @@ struct HIAppearance: Equatable { countdownTextColor: brown, countdownBackground: orange, bannerBackground: lightYellow, + scannerButtonPink: buttonPink, + scannerButtonTeal: buttonTeal, + scannerButtonYellow: buttonYellow, + scannerButtonBorder: medBrown, + scannerButtonShadow: darkBrown, proBackground: medTeal, eventCard: offWhite ) @@ -231,7 +231,7 @@ struct HIAppearance: Equatable { lightYellowText: bLightYellow, baseText: lightBlack, accent: white, - viewTitleGreen: darkGreen, + viewTitleBrown: darkBrown, action: tan, baseBackground: white, contentBackground: offWhite, @@ -291,6 +291,11 @@ struct HIAppearance: Equatable { countdownTextColor: brown, countdownBackground: orange, bannerBackground: lightYellow, + scannerButtonPink: buttonPink, + scannerButtonTeal: buttonTeal, + scannerButtonYellow: buttonYellow, + scannerButtonBorder: medBrown, + scannerButtonShadow: darkBrown, proBackground: medTeal, eventCard: offWhite ) @@ -395,7 +400,7 @@ struct HIAppearance: Equatable { static let welcomeTitle = UIFont(name: "MontserratRoman-Bold", size: UIDevice.current.userInterfaceIdiom == .pad ? 40: 24) // Font for Staff QR code selection - static let QRSelection = UIFont(name: "MontserratRoman-SemiBold", size: 20) + static let QRSelection = UIFont(name: "MontserratRoman-Bold", size: 24) } } diff --git a/HackIllinois/UI/HILabel.swift b/HackIllinois/UI/HILabel.swift index 069c4155..f3cdf5bf 100644 --- a/HackIllinois/UI/HILabel.swift +++ b/HackIllinois/UI/HILabel.swift @@ -23,7 +23,7 @@ class HILabel: UILabel { case sponsor case project case viewTitle - case viewTitleGreen + case viewTitleBrown case detailTitle case subtitle case description @@ -148,8 +148,8 @@ class HILabel: UILabel { textAlignment = .center font = HIAppearance.Font.viewTitle - case .viewTitleGreen: - textHIColor = \.viewTitleGreen + case .viewTitleBrown: + textHIColor = \.viewTitleBrown backgroundHIColor = \.clear textAlignment = .center font = HIAppearance.Font.viewTitle @@ -343,7 +343,7 @@ class HILabel: UILabel { font = HIAppearance.Font.locationText case .QRSelection: - textHIColor = \.black + textHIColor = \.viewTitleBrown backgroundHIColor = \.clear font = HIAppearance.Font.QRSelection } diff --git a/HackIllinois/UI/TableView/Cells/HIBubbleCell/HIEventCell.swift b/HackIllinois/UI/TableView/Cells/HIBubbleCell/HIEventCell.swift index d8d2296c..72abad41 100644 --- a/HackIllinois/UI/TableView/Cells/HIBubbleCell/HIEventCell.swift +++ b/HackIllinois/UI/TableView/Cells/HIBubbleCell/HIEventCell.swift @@ -161,6 +161,12 @@ extension HIEventCell { pointsView.constrain(height: 20 * bubbleConstant) pointsView.leadingAnchor.constraint(equalTo: eventTypeView.trailingAnchor, constant: 8 * bubbleConstant).isActive = true pointsView.centerYAnchor.constraint(equalTo: eventTypeView.centerYAnchor).isActive = true + if rhs.isPro { + upperContainerView.addSubview(proTypeView); proTypeView.addSubview(proLabel); proLabel.text = "Pro" + proTypeView.constrain(height: 20 * bubbleConstant); proTypeView.centerYAnchor.constraint(equalTo: pointsView.centerYAnchor).isActive = true + proTypeView.leadingAnchor.constraint(equalTo: pointsView.trailingAnchor, constant: 8 * bubbleConstant).isActive = true + proLabel.constrain(to: proTypeView, topInset: 4, trailingInset: -8, bottomInset: -4, leadingInset: 8) + } upperContainerView.addSubview(timeImageView) upperContainerView.addSubview(timeLabel) timeLabel.leadingAnchor.constraint(equalTo: timeImageView.trailingAnchor, constant: eventCellSpacing + 1).isActive = true diff --git a/HackIllinois/ViewControllers/HIBaseViewController.swift b/HackIllinois/ViewControllers/HIBaseViewController.swift index 9cb4e213..b4ca21f1 100644 --- a/HackIllinois/ViewControllers/HIBaseViewController.swift +++ b/HackIllinois/ViewControllers/HIBaseViewController.swift @@ -115,7 +115,7 @@ extension HIBaseViewController { label.text = customTitle let view = UIView(frame: CGRect(x: 0, y: 0, width: 80, height: 30)) - view.bounds = view.bounds.offsetBy(dx: -24, dy: 0) + view.bounds = view.bounds.offsetBy(dx: -24, dy: (UIDevice.current.userInterfaceIdiom == .pad) ? 10 : 0) view.addSubview(label) self.navigationItem.leftBarButtonItem = UIBarButtonItem.init(customView: view) diff --git a/HackIllinois/ViewControllers/HIEventDetailViewController.swift b/HackIllinois/ViewControllers/HIEventDetailViewController.swift index c9395619..eedb274f 100644 --- a/HackIllinois/ViewControllers/HIEventDetailViewController.swift +++ b/HackIllinois/ViewControllers/HIEventDetailViewController.swift @@ -278,6 +278,7 @@ extension HIEventDetailViewController { mapView.constrain(height: mapHeight) mapView.layer.cornerRadius = 20 } + func setupCloseButton() { view.addSubview(closeButton) closeButton.addTarget(self, action: #selector(didSelectCloseButton(_:)), for: .touchUpInside) diff --git a/HackIllinois/ViewControllers/HIHomeViewController.swift b/HackIllinois/ViewControllers/HIHomeViewController.swift index 5f39d650..9a2ede63 100644 --- a/HackIllinois/ViewControllers/HIHomeViewController.swift +++ b/HackIllinois/ViewControllers/HIHomeViewController.swift @@ -41,7 +41,7 @@ class HIHomeViewController: HIEventListViewController { private var countdownDataStoreIndex = 0 private var staticDataStore: [(date: Date, displayText: String)] = [ - (HITimeDataSource.shared.eventTimes.eventStart, "HACKILLINOIS BEGINS IN"), + (HITimeDataSource.shared.eventTimes.checkInStart, "HACKILLINOIS BEGINS IN"), (HITimeDataSource.shared.eventTimes.hackStart, "HACKING BEGINS IN"), (HITimeDataSource.shared.eventTimes.hackEnd, "HACKING ENDS IN"), (HITimeDataSource.shared.eventTimes.eventEnd, "HACKILLINOIS ENDS IN") @@ -88,15 +88,15 @@ extension HIHomeViewController { func layoutLegendButton() { view.addSubview(legendButton) - if UIDevice.current.userInterfaceIdiom != .pad { - legendButton.constrain(width: 25, height: 25) - } else { - legendButton.constrain(width: 50, height: 50) - } + let buttonSize: CGFloat = UIDevice.current.userInterfaceIdiom != .pad ? 30 : 50 + let padding: CGFloat = UIDevice.current.userInterfaceIdiom != .pad ? 16 : 50 + legendButton.constrain(width: buttonSize, height: buttonSize) legendButton.translatesAutoresizingMaskIntoConstraints = false - legendButton.topAnchor.constraint(equalTo: bannerFrameView.topAnchor, constant: -2).isActive = true - legendButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -15).isActive = true + + // Increase the hit target area by adding padding + legendButton.topAnchor.constraint(equalTo: bannerFrameView.topAnchor, constant: -5).isActive = true + legendButton.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -padding).isActive = true legendButton.addTarget(self, action: #selector(didSelectLegendButton(_:)), for: .touchUpInside) } diff --git a/HackIllinois/ViewControllers/HIPointsShopSwiftUIView.swift b/HackIllinois/ViewControllers/HIPointsShopSwiftUIView.swift index 0837ad89..16fae7bc 100644 --- a/HackIllinois/ViewControllers/HIPointsShopSwiftUIView.swift +++ b/HackIllinois/ViewControllers/HIPointsShopSwiftUIView.swift @@ -43,7 +43,7 @@ struct HIPointShopSwiftUIView: View { .background(Color(red: 0.1647, green: 0.1647, blue: 0.1647)) .cornerRadius(1000) .frame(maxWidth: .infinity, alignment: .trailing) - .offset(y: -38) + .offset(y: isIpad ? -42 : -38) Spacer() } Image("KnickKnacks") diff --git a/HackIllinois/ViewControllers/HIProfileCardView.swift b/HackIllinois/ViewControllers/HIProfileCardView.swift index 540ae4ac..d02281c0 100644 --- a/HackIllinois/ViewControllers/HIProfileCardView.swift +++ b/HackIllinois/ViewControllers/HIProfileCardView.swift @@ -58,7 +58,7 @@ struct HIProfileCardView: View { let isIpad = UIDevice.current.userInterfaceIdiom == .pad let role: String @State var startFetchingQR = false - @State var qrInfo = "" + @State var qrInfo = "hackillinois://user?userToken=111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111" // Factors used to change frame to alter based on device let padFactor = UIScreen.main.bounds.height/1366 let phoneFactor = UIScreen.main.bounds.height/844 @@ -199,6 +199,7 @@ struct HIProfileCardView: View { let (qr, _) = try result.get() DispatchQueue.main.async { self.qrInfo = qr.qrInfo + print("qrinfo with attendee jwt", qrInfo) } } catch { print("An error has occurred \(error)") diff --git a/HackIllinois/ViewControllers/HIProfileViewController.swift b/HackIllinois/ViewControllers/HIProfileViewController.swift index b1f9af9c..525ecbaf 100644 --- a/HackIllinois/ViewControllers/HIProfileViewController.swift +++ b/HackIllinois/ViewControllers/HIProfileViewController.swift @@ -98,7 +98,7 @@ extension HIProfileViewController { profileCardController = UIHostingController(rootView: HIProfileCardView(displayName: profile.displayName, points: profile.points, tier: profileTier, - foodWave: 1, + foodWave: profile.foodWave, avatarUrl: profile.avatarUrl, userId: profile.userId, role: role @@ -186,7 +186,7 @@ extension HIProfileViewController { self?.profile.displayName = apiProfile.discordTag self?.profile.points = apiProfile.points self?.profile.avatarUrl = apiProfile.avatarUrl - //self?.profile.foodWave = apiProfile.foodWave + self?.profile.foodWave = apiProfile.foodWave DispatchQueue.main.async { NotificationCenter.default.post(name: .loginProfile, object: nil, userInfo: ["profile": self?.profile]) self?.updateProfile() diff --git a/HackIllinois/ViewControllers/HIQRAttendeeScannerSelection.swift b/HackIllinois/ViewControllers/HIQRAttendeeScannerSelection.swift new file mode 100644 index 00000000..36b2ee24 --- /dev/null +++ b/HackIllinois/ViewControllers/HIQRAttendeeScannerSelection.swift @@ -0,0 +1,219 @@ +// +// HIQRAttendeeScannerSelection.swift +// HackIllinois +// +// Created by HackIllinois on 1/12/24. +// Copyright © 2024 HackIllinois. All rights reserved. +// + +import Foundation +import SwiftUI +import UIKit +import HIAPI + +class HIQRAttendeeScannerSelection: HIBaseViewController { + private let backgroundHIColor: HIColor = \.clear + private let containerView = HIView { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.backgroundHIColor = \.baseBackground + } + private let errorView = HIErrorView(style: .codePopup) + private let closeButton = HIButton { + $0.tintHIColor = \.action + $0.backgroundHIColor = \.clear + $0.activeImage = #imageLiteral(resourceName: "CloseButton") + $0.baseImage = #imageLiteral(resourceName: "CloseButton") + } + private let previewView = HIView { + $0.backgroundHIColor = \.baseBackground + } + private let label = HILabel { + $0.textHIColor = \.whiteText + $0.backgroundHIColor = \.clear + $0.textAlignment = .center + $0.font = HIAppearance.Font.glyph + } + private let eventCheckInButton = HIButton { + $0.tintHIColor = \.action + $0.backgroundHIColor = \.scannerButtonPink + $0.layer.borderWidth = 4.0 + $0.layer.borderColor = #colorLiteral(red: 0.4588235294, green: 0.1960784314, blue: 0.07843137255, alpha: 1) + // 3D Effect + $0.layer.masksToBounds = false + $0.layer.shadowColor = #colorLiteral(red: 0.337254902, green: 0.1411764706, blue: 0.06666666667, alpha: 1) + $0.layer.shadowOpacity = 1 + $0.layer.shadowOffset = CGSize(width: 0, height: 12) + $0.layer.shadowRadius = 0 + } + private let mentorCheckInButton = HIButton { + $0.tintHIColor = \.action + $0.backgroundHIColor = \.scannerButtonTeal + $0.layer.borderWidth = 4.0 + $0.layer.borderColor = #colorLiteral(red: 0.4588235294, green: 0.1960784314, blue: 0.07843137255, alpha: 1) + // 3D Effect + $0.layer.masksToBounds = false + $0.layer.shadowColor = #colorLiteral(red: 0.337254902, green: 0.1411764706, blue: 0.06666666667, alpha: 1) + $0.layer.shadowOpacity = 1 + $0.layer.shadowOffset = CGSize(width: 0, height: 12) + $0.layer.shadowRadius = 0 + } + private let pointShopScanButton = HIButton { + $0.tintHIColor = \.action + $0.backgroundHIColor = \.scannerButtonYellow + $0.layer.borderWidth = 4.0 + $0.layer.borderColor = #colorLiteral(red: 0.4588235294, green: 0.1960784314, blue: 0.07843137255, alpha: 1) + // 3D Effect + $0.layer.masksToBounds = false + $0.layer.shadowColor = #colorLiteral(red: 0.337254902, green: 0.1411764706, blue: 0.06666666667, alpha: 1) + $0.layer.shadowOpacity = 1 + $0.layer.shadowOffset = CGSize(width: 0, height: 10) + $0.layer.shadowRadius = 0 + } + @objc dynamic override func setUpBackgroundView() { + super.setUpBackgroundView() + if UIDevice.current.userInterfaceIdiom == .pad { + backgroundView.image = UIImage(named: "BackgroundPad") + } else { + backgroundView.image = #imageLiteral(resourceName: "Attendee") + } + } +} + +// MARK: - UIViewController +extension HIQRAttendeeScannerSelection { + override func loadView() { + super.loadView() + guard let user = HIApplicationStateController.shared.user else { return } + if HIApplicationStateController.shared.isGuest && !user.roles.contains(.STAFF) { // Guest + // Guest handler + let background = #imageLiteral(resourceName: "PurpleBackground") + let imageView: UIImageView = UIImageView(frame: view.bounds) + view.addSubview(imageView) + view.sendSubviewToBack(imageView) + layoutErrorView() + } else if !user.roles.contains(.STAFF) { + view.addSubview(eventCheckInButton) + view.addSubview(mentorCheckInButton) + view.addSubview(pointShopScanButton) + view.addSubview(closeButton) + view.addSubview(label) + + // Add constraints for meetingButton and attendeeButton here + eventCheckInButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 140).isActive = true + eventCheckInButton.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true + eventCheckInButton.addTarget(self, action: #selector(didSelectEventCheckInButton(_:)), for: .touchUpInside) + eventCheckInButton.layer.cornerRadius = (UIDevice.current.userInterfaceIdiom == .pad) ? 30 : 15 + eventCheckInButton.constrain(width: (UIDevice.current.userInterfaceIdiom == .pad) ? 500 : 290, height: (UIDevice.current.userInterfaceIdiom == .pad) ? 150 : 80) + eventCheckInButton.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true + + mentorCheckInButton.topAnchor.constraint(equalTo: eventCheckInButton.bottomAnchor, constant: (UIDevice.current.userInterfaceIdiom == .pad) ? 100 : 50).isActive = true + mentorCheckInButton.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true + mentorCheckInButton.addTarget(self, action: #selector(didSelectMentorCheckInButton(_:)), for: .touchUpInside) + mentorCheckInButton.layer.cornerRadius = (UIDevice.current.userInterfaceIdiom == .pad) ? 30 : 15 + mentorCheckInButton.constrain(width: (UIDevice.current.userInterfaceIdiom == .pad) ? 500 : 290, height: (UIDevice.current.userInterfaceIdiom == .pad) ? 150 : 80) + + pointShopScanButton.topAnchor.constraint(equalTo: mentorCheckInButton.bottomAnchor, constant: (UIDevice.current.userInterfaceIdiom == .pad) ? 100 : 50).isActive = true + pointShopScanButton.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true + pointShopScanButton.addTarget(self, action: #selector(didSelectPointsShopButton(_:)), for: .touchUpInside) + pointShopScanButton.layer.cornerRadius = (UIDevice.current.userInterfaceIdiom == .pad) ? 30 : 15 + pointShopScanButton.constrain(width: (UIDevice.current.userInterfaceIdiom == .pad) ? 500 : 290, height: (UIDevice.current.userInterfaceIdiom == .pad) ? 150 : 80) + + let eventCheckInLabel = HILabel(style: .QRSelection) + eventCheckInLabel.text = "Event Check In" + eventCheckInButton.addSubview(eventCheckInLabel) + eventCheckInLabel.centerYAnchor.constraint(equalTo: eventCheckInButton.centerYAnchor).isActive = true + eventCheckInLabel.centerXAnchor.constraint(equalTo: eventCheckInButton.centerXAnchor).isActive = true + + let mentorCheckInLabel = HILabel(style: .QRSelection) + mentorCheckInLabel.text = "Mentor Check In" + mentorCheckInButton.addSubview(mentorCheckInLabel) + mentorCheckInLabel.centerYAnchor.constraint(equalTo: mentorCheckInButton.centerYAnchor).isActive = true + mentorCheckInLabel.centerXAnchor.constraint(equalTo: mentorCheckInButton.centerXAnchor).isActive = true + + let pointsShopLabel = HILabel(style: .QRSelection) + pointsShopLabel.text = "Points Shop" + pointShopScanButton.addSubview(pointsShopLabel) + pointsShopLabel.centerYAnchor.constraint(equalTo: pointShopScanButton.centerYAnchor).isActive = true + pointsShopLabel.centerXAnchor.constraint(equalTo: pointShopScanButton.centerXAnchor).isActive = true + } + closeButton.addTarget(self, action: #selector(didSelectCloseButton(_:)), for: .touchUpInside) + closeButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10).isActive = true + closeButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 8).isActive = true + closeButton.constrain(width: 60, height: 60) + closeButton.imageView?.contentMode = .scaleToFill + let label = HILabel(style: .viewTitle) + label.text = "SCANNER" + view.addSubview(label) + label.centerYAnchor.constraint(equalTo: closeButton.centerYAnchor).isActive = true + label.leadingAnchor.constraint(equalTo: closeButton.trailingAnchor, constant: 3).isActive = true + } + override func viewDidLoad() { + setCustomTitle(customTitle: "SCANNER") + super.viewDidLoad() + } + func layoutErrorView() { + errorView.delegate = self + view.addSubview(errorView) + errorView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true + errorView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true + errorView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true + errorView.heightAnchor.constraint(equalToConstant: 100).isActive = true + errorView.addSubview(closeButton) + } +} + +// MARK: - HIErrorViewDelegate +extension HIQRAttendeeScannerSelection: HIErrorViewDelegate { + func didSelectErrorLogout(_ sender: UIButton) { + let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) + alert.addAction( + UIAlertAction(title: "Log Out", style: .destructive) { _ in + self.dismiss(animated: true, completion: nil) + NotificationCenter.default.post(name: .logoutUser, object: nil) + } + ) + alert.addAction( + UIAlertAction(title: "Cancel", style: .cancel, handler: nil) + ) + alert.popoverPresentationController?.sourceView = sender + present(alert, animated: true, completion: nil) + } +} + +// MARK: - Actions +extension HIQRAttendeeScannerSelection { + @objc func didSelectCloseButton(_ sender: HIButton) { + self.dismiss(animated: true, completion: nil) + let scanQRCodePopup = HIQRAttendeeScannerSelection() + scanQRCodePopup.modalPresentationStyle = .overFullScreen + scanQRCodePopup.modalTransitionStyle = .crossDissolve + self.present(scanQRCodePopup, animated: true, completion: nil) + } +} + +extension HIQRAttendeeScannerSelection { + @objc func didSelectEventCheckInButton(_ sender: HIButton) { + let scanQRCodePopup = HIScanQRCodeViewController() + scanQRCodePopup.modalPresentationStyle = .overFullScreen + scanQRCodePopup.modalTransitionStyle = .crossDissolve + self.present(scanQRCodePopup, animated: true, completion: nil) + } +} + +extension HIQRAttendeeScannerSelection { + @objc func didSelectMentorCheckInButton(_ sender: HIButton) { + let scanQRCodePopup = HIScanMentorViewController() + scanQRCodePopup.modalPresentationStyle = .overFullScreen + scanQRCodePopup.modalTransitionStyle = .crossDissolve + self.present(scanQRCodePopup, animated: true, completion: nil) + } +} + +extension HIQRAttendeeScannerSelection { + @objc func didSelectPointsShopButton(_ sender: HIButton) { + let scanQRCodePopup = HIScanPointsShopViewController() + scanQRCodePopup.modalPresentationStyle = .overFullScreen + scanQRCodePopup.modalTransitionStyle = .crossDissolve + self.present(scanQRCodePopup, animated: true, completion: nil) + } +} diff --git a/HackIllinois/ViewControllers/HIQRScannerSelection.swift b/HackIllinois/ViewControllers/HIQRScannerSelection.swift index 906cda7e..b09e0a5d 100644 --- a/HackIllinois/ViewControllers/HIQRScannerSelection.swift +++ b/HackIllinois/ViewControllers/HIQRScannerSelection.swift @@ -35,19 +35,34 @@ class HIQRScannerSelection: HIBaseViewController { } private let meetingButton = HIButton { $0.tintHIColor = \.action - $0.backgroundHIColor = \.white - $0.layer.borderWidth = 3.0 - $0.layer.borderColor = #colorLiteral(red: 0.7859038711, green: 0.9741206765, blue: 1, alpha: 1) + $0.backgroundHIColor = \.scannerButtonPink + $0.layer.borderWidth = 4.0 + $0.layer.borderColor = #colorLiteral(red: 0.4588235294, green: 0.1960784314, blue: 0.07843137255, alpha: 1) + // 3D Effect + $0.layer.masksToBounds = false + $0.layer.shadowColor = #colorLiteral(red: 0.337254902, green: 0.1411764706, blue: 0.06666666667, alpha: 1) + $0.layer.shadowOpacity = 1 + $0.layer.shadowOffset = CGSize(width: 0, height: 12) + $0.layer.shadowRadius = 0 } private let attendeeButton = HIButton { $0.tintHIColor = \.action - $0.backgroundHIColor = \.buttonYellow - $0.layer.borderWidth = 3.0 - $0.layer.borderColor = #colorLiteral(red: 1, green: 0.9568627451, blue: 0.5529411765, alpha: 1) + $0.backgroundHIColor = \.scannerButtonTeal + $0.layer.borderWidth = 4.0 + $0.layer.borderColor = #colorLiteral(red: 0.4588235294, green: 0.1960784314, blue: 0.07843137255, alpha: 1) + $0.layer.masksToBounds = false + $0.layer.shadowColor = #colorLiteral(red: 0.337254902, green: 0.1411764706, blue: 0.06666666667, alpha: 1) + $0.layer.shadowOpacity = 1 + $0.layer.shadowOffset = CGSize(width: 0, height: 12) + $0.layer.shadowRadius = 0 } @objc dynamic override func setUpBackgroundView() { super.setUpBackgroundView() - backgroundView.image = #imageLiteral(resourceName: "BackgroundNew") + if UIDevice.current.userInterfaceIdiom == .pad { + backgroundView.image = #imageLiteral(resourceName: "Pink Background") + } else { + backgroundView.image = #imageLiteral(resourceName: "Staff") + } } } @@ -58,7 +73,7 @@ extension HIQRScannerSelection { guard let user = HIApplicationStateController.shared.user else { return } if HIApplicationStateController.shared.isGuest && !user.roles.contains(.STAFF) { // Guest // Guest handler - let background = #imageLiteral(resourceName: "ProfileBackground") + let background = #imageLiteral(resourceName: "PurpleBackground") let imageView: UIImageView = UIImageView(frame: view.bounds) view.addSubview(imageView) view.sendSubviewToBack(imageView) @@ -74,17 +89,17 @@ extension HIQRScannerSelection { view.addSubview(closeButton) view.addSubview(label) // Add constraints for meetingButton and attendeeButton here - meetingButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 150).isActive = true + meetingButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 140).isActive = true meetingButton.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true meetingButton.addTarget(self, action: #selector(didSelectMeetingButton(_:)), for: .touchUpInside) - meetingButton.layer.cornerRadius = 15 - meetingButton.constrain(width: 290, height: 90) + meetingButton.layer.cornerRadius = (UIDevice.current.userInterfaceIdiom == .pad) ? 30 : 15 + meetingButton.constrain(width: (UIDevice.current.userInterfaceIdiom == .pad) ? 500 : 290, height: (UIDevice.current.userInterfaceIdiom == .pad) ? 150 : 80) meetingButton.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true - attendeeButton.topAnchor.constraint(equalTo: meetingButton.bottomAnchor, constant: 30).isActive = true + attendeeButton.topAnchor.constraint(equalTo: meetingButton.bottomAnchor, constant: (UIDevice.current.userInterfaceIdiom == .pad) ? 100 : 50).isActive = true attendeeButton.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true attendeeButton.addTarget(self, action: #selector(didSelectAttendeeButton(_:)), for: .touchUpInside) - attendeeButton.layer.cornerRadius = 15 - attendeeButton.constrain(width: 290, height: 90) + attendeeButton.layer.cornerRadius = (UIDevice.current.userInterfaceIdiom == .pad) ? 30 : 15 + attendeeButton.constrain(width: (UIDevice.current.userInterfaceIdiom == .pad) ? 500 : 290, height: (UIDevice.current.userInterfaceIdiom == .pad) ? 150 : 80) let meetingLabel = HILabel(style: .QRSelection) meetingLabel.text = "Meeting Attendance" meetingButton.addSubview(meetingLabel) @@ -101,14 +116,14 @@ extension HIQRScannerSelection { closeButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 8).isActive = true closeButton.constrain(width: 60, height: 60) closeButton.imageView?.contentMode = .scaleToFill - let label = HILabel(style: .viewTitleGreen) - label.text = "ATTENDANCE" + let label = HILabel(style: (UIDevice.current.userInterfaceIdiom == .pad) ? .viewTitleBrown : .viewTitleBrown) + label.text = "SCANNER" view.addSubview(label) label.centerYAnchor.constraint(equalTo: closeButton.centerYAnchor).isActive = true label.leadingAnchor.constraint(equalTo: closeButton.trailingAnchor, constant: 3).isActive = true } override func viewDidLoad() { - setCustomTitle(customTitle: "ATTENDANCE") + setCustomTitle(customTitle: "SCANNER") super.viewDidLoad() } func layoutErrorView() { diff --git a/HackIllinois/ViewControllers/HIScanMentorViewController.swift b/HackIllinois/ViewControllers/HIScanMentorViewController.swift new file mode 100644 index 00000000..b93737ae --- /dev/null +++ b/HackIllinois/ViewControllers/HIScanMentorViewController.swift @@ -0,0 +1,308 @@ +// +// HIScanMentorViewController.swift +// HackIllinois +// +// Created by HackIllinois on 2/11/24. +// Copyright © 2024 HackIllinois. All rights reserved. +// + +import Foundation +import UIKit +import Combine +import AVKit +import CoreData +import APIManager +import HIAPI +import SwiftUI + +class HIScanMentorViewController: HIBaseViewController { + private var captureSession: AVCaptureSession? + private let containerView = HIView { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.backgroundHIColor = \.baseBackground + } + private let previewView = HIView { + $0.backgroundHIColor = \.baseBackground + } + private var previewLayer: AVCaptureVideoPreviewLayer? + let hapticGenerator = UINotificationFeedbackGenerator() + private let pickerView = UIPickerView() + private var loadFailed = false + var respondingToQRCodeFound = true + private let closeButton = HIButton { + $0.tintHIColor = \.action + $0.backgroundHIColor = \.clear + $0.activeImage = #imageLiteral(resourceName: "CloseButton") + $0.baseImage = #imageLiteral(resourceName: "CloseButton") + } + private let errorView = HIErrorView(style: .codePopup) + private var selectedEventID = "" + private var cancellables = Set() + var currentUserID = "" + var currentUserName = "" + var dietaryString = "" +} + +// MARK: - UIViewController +extension HIScanMentorViewController { + override func loadView() { + super.loadView() + print("Points Shop QR scanner") + guard let user = HIApplicationStateController.shared.user else { return } + if HIApplicationStateController.shared.isGuest && !user.roles.contains(.STAFF) { + let background = #imageLiteral(resourceName: "ProfileBackground") + let imageView: UIImageView = UIImageView(frame: view.bounds) + view.addSubview(imageView) + view.sendSubviewToBack(imageView) + layoutErrorView() + } else { + view.addSubview(containerView) + view.bringSubviewToFront(containerView) + containerView.constrain(to: view, topInset: 0, bottomInset: 0) + 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) + } + } + view.addSubview(closeButton) + closeButton.addTarget(self, action: #selector(didSelectCloseButton(_:)), for: .touchUpInside) + closeButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true + closeButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 8).isActive = true + closeButton.constrain(width: 60, height: 60) + closeButton.imageView?.contentMode = .scaleToFill + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + if loadFailed { + presentErrorController( + title: "Scanning not supported", + message: "Your device does not support scanning a code from an item. Please use a device with a camera.", + dismissParentOnCompletion: true + ) + } else if captureSession?.isRunning == false { + previewLayer?.frame = view.layer.bounds + DispatchQueue.main.async { [weak self] in + self?.captureSession?.startRunning() + } + } + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + if captureSession?.isRunning == true { + DispatchQueue.main.async { [weak self] in + self?.captureSession?.stopRunning() + } + } + } + + override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + coordinator.animate(alongsideTransition: { [weak self] (_) in + self?.setFrameForPreviewLayer() + }, completion: nil) + } + func layoutErrorView() { + errorView.delegate = self + view.addSubview(errorView) + errorView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true + errorView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true + errorView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true + errorView.heightAnchor.constraint(equalToConstant: 100).isActive = true + } +} + +// MARK: - Actions +extension HIScanMentorViewController { + @objc func didSelectCloseButton(_ sender: HIButton) { + self.dismiss(animated: true, completion: nil) + } +} + +// MARK: - HIErrorViewDelegate +extension HIScanMentorViewController: HIErrorViewDelegate { + func didSelectErrorLogout(_ sender: UIButton) { + let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) + alert.addAction( + UIAlertAction(title: "Log Out", style: .destructive) { _ in + self.dismiss(animated: true, completion: nil) + NotificationCenter.default.post(name: .logoutUser, object: nil) + } + ) + alert.addAction( + UIAlertAction(title: "Cancel", style: .cancel, handler: nil) + ) + alert.popoverPresentationController?.sourceView = sender + present(alert, animated: true, completion: nil) + } +} + +// MARK: AVCaptureMetadataOutputObjectsDelegate +extension HIScanMentorViewController: AVCaptureMetadataOutputObjectsDelegate { + func setupCaptureSession() { + captureSession = AVCaptureSession() + let metadataOutput = AVCaptureMetadataOutput() + guard + let captureSession = captureSession, + let videoCaptureDevice = AVCaptureDevice.default(for: .video), + let videoInput = try? AVCaptureDeviceInput(device: videoCaptureDevice), + captureSession.canAddInput(videoInput), + captureSession.canAddOutput(metadataOutput) else { + loadFailed = true + return + } + captureSession.addInput(videoInput) + captureSession.addOutput(metadataOutput) + metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main) + metadataOutput.metadataObjectTypes = [.qr] + let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) + previewLayer.videoGravity = .resizeAspectFill + self.previewLayer = previewLayer + setFrameForPreviewLayer() + previewView.layer.addSublayer(previewLayer) + } + + func setFrameForPreviewLayer() { + guard let previewLayer = previewLayer else { return } + previewLayer.frame = previewView.layer.bounds + guard previewLayer.connection?.isVideoOrientationSupported == true else { return } +#warning("Not Tested") + let interfaceOrientation = UIApplication.shared.windows.first?.windowScene?.interfaceOrientation + switch interfaceOrientation { + case .portrait, .unknown: + previewLayer.connection?.videoOrientation = .portrait + case .portraitUpsideDown: + previewLayer.connection?.videoOrientation = .portraitUpsideDown + case .landscapeLeft: + previewLayer.connection?.videoOrientation = .landscapeLeft + case .landscapeRight: + previewLayer.connection?.videoOrientation = .landscapeRight + case .none: + break + @unknown default: + previewLayer.connection?.videoOrientation = .portrait + } + } + + func handleMentorAlert(status: String) { + print(status) + var alertTitle = "" + var alertMessage = "" + var error = true + switch status { + case "Success": + alertTitle = "\n\nPoints Earned!" + alertMessage = "\nYou have obtained points from mentor office hours!" + error = false + default: + alertTitle = "\n\nError!" + alertMessage = "\nSomething isn't quite right." + self.respondingToQRCodeFound = true + } + // Create custom alert for attendee check in functionality + let alert = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert) + let titleFont = UIFont(name: "MontserratRoman-Bold", size: 22) + let messageFont = UIFont(name: "MontserratRoman-Medium", size: 16) + let titleColor = #colorLiteral(red: 0.337254902, green: 0.1411764706, blue: 0.06666666667, alpha: 1) + let messageColor = #colorLiteral(red: 0.337254902, green: 0.1411764706, blue: 0.06666666667, alpha: 1) + let attributedTitle = NSAttributedString(string: alertTitle, attributes: [NSAttributedString.Key.font: titleFont as Any, NSAttributedString.Key.foregroundColor: titleColor]) + let attributedMessage = NSAttributedString(string: alertMessage, attributes: [NSAttributedString.Key.font: messageFont as Any, NSAttributedString.Key.foregroundColor: messageColor]) + alert.setValue(attributedTitle, forKey: "attributedTitle") + alert.setValue(attributedMessage, forKey: "attributedMessage") + + // Create image view + let imageView = UIImageView(image: UIImage(named: "Treasure Chest")) + imageView.contentMode = .scaleAspectFit + alert.view.addSubview(imageView) + imageView.translatesAutoresizingMaskIntoConstraints = false + imageView.centerXAnchor.constraint(equalTo: alert.view.centerXAnchor).isActive = true + imageView.centerYAnchor.constraint(equalTo: alert.view.topAnchor, constant: -5).isActive = true + if alertTitle == "Success!" { + alert.addAction( + UIAlertAction(title: "OK", style: .default, handler: { _ in + self.dismiss(animated: true, completion: nil) + //Dismisses view controller + self.didSelectCloseButton(self.closeButton) + NotificationCenter.default.post(name: .qrCodeSuccessfulScan, object: nil) + })) + } else { + alert.addAction( + UIAlertAction(title: "OK", style: .default, handler: { _ in + self.registerForKeyboardNotifications() + })) + } + self.present(alert, animated: true, completion: nil) + } + + func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) { + guard respondingToQRCodeFound else { return } + let meta = metadataObjects.first as? AVMetadataMachineReadableCodeObject + let code = meta?.stringValue ?? "" + guard let user = HIApplicationStateController.shared.user else { return } + if let rangeItemId = code.range(of: "mentorId="), let rangeInstance = code.range(of: "&instance=") { + let mentorId = code[rangeItemId.upperBound.. [String: AnyObject]? { + let string = token.components(separatedBy: ".") + if string.count == 1 { return nil } + let toDecode = string[1] as String + var stringtoDecode: String = toDecode.replacingOccurrences(of: "-", with: "+") // 62nd char of encoding + stringtoDecode = stringtoDecode.replacingOccurrences(of: "_", with: "/") // 63rd char of encoding + switch stringtoDecode.utf16.count % 4 { + case 2: stringtoDecode = "\(stringtoDecode)==" + case 3: stringtoDecode = "\(stringtoDecode)=" + default: // nothing to do stringtoDecode can stay the same + print("") + } + let dataToDecode = Data(base64Encoded: stringtoDecode, options: []) + let base64DecodedString = NSString(data: dataToDecode!, encoding: String.Encoding.utf8.rawValue) + var values: [String: AnyObject]? + if let string = base64DecodedString { + if let data = string.data(using: String.Encoding.utf8.rawValue, allowLossyConversion: true) { + values = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: AnyObject] + } + } + return values + } +} diff --git a/HackIllinois/ViewControllers/HIScanPointsShopViewController.swift b/HackIllinois/ViewControllers/HIScanPointsShopViewController.swift new file mode 100644 index 00000000..e87cf9e1 --- /dev/null +++ b/HackIllinois/ViewControllers/HIScanPointsShopViewController.swift @@ -0,0 +1,325 @@ +// +// HIScanPointsShopViewController.swift +// HackIllinois +// +// Created by HackIllinois on 1/13/24. +// Copyright © 2024 HackIllinois. All rights reserved. +// + +import Foundation +import UIKit +import Combine +import AVKit +import CoreData +import APIManager +import HIAPI +import SwiftUI + +class HIScanPointsShopViewController: HIBaseViewController { + private var captureSession: AVCaptureSession? + private let containerView = HIView { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.backgroundHIColor = \.baseBackground + } + private let previewView = HIView { + $0.backgroundHIColor = \.baseBackground + } + private var previewLayer: AVCaptureVideoPreviewLayer? + let hapticGenerator = UINotificationFeedbackGenerator() + private let pickerView = UIPickerView() + private var loadFailed = false + var respondingToQRCodeFound = true + private let closeButton = HIButton { + $0.tintHIColor = \.action + $0.backgroundHIColor = \.clear + $0.activeImage = #imageLiteral(resourceName: "CloseButton") + $0.baseImage = #imageLiteral(resourceName: "CloseButton") + } + private let errorView = HIErrorView(style: .codePopup) + private var selectedEventID = "" + private var cancellables = Set() + var currentUserID = "" + var currentUserName = "" + var dietaryString = "" +} + +// MARK: - UIViewController +extension HIScanPointsShopViewController { + override func loadView() { + super.loadView() + print("Points Shop QR scanner") + guard let user = HIApplicationStateController.shared.user else { return } + if HIApplicationStateController.shared.isGuest && !user.roles.contains(.STAFF) { + let background = #imageLiteral(resourceName: "ProfileBackground") + let imageView: UIImageView = UIImageView(frame: view.bounds) + view.addSubview(imageView) + view.sendSubviewToBack(imageView) + layoutErrorView() + } else { + view.addSubview(containerView) + view.bringSubviewToFront(containerView) + containerView.constrain(to: view, topInset: 0, bottomInset: 0) + 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) + } + } + view.addSubview(closeButton) + closeButton.addTarget(self, action: #selector(didSelectCloseButton(_:)), for: .touchUpInside) + closeButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true + closeButton.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 8).isActive = true + closeButton.constrain(width: 60, height: 60) + closeButton.imageView?.contentMode = .scaleToFill + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + if loadFailed { + presentErrorController( + title: "Scanning not supported", + message: "Your device does not support scanning a code from an item. Please use a device with a camera.", + dismissParentOnCompletion: true + ) + } else if captureSession?.isRunning == false { + previewLayer?.frame = view.layer.bounds + DispatchQueue.main.async { [weak self] in + self?.captureSession?.startRunning() + } + } + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + if captureSession?.isRunning == true { + DispatchQueue.main.async { [weak self] in + self?.captureSession?.stopRunning() + } + } + } + + override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + coordinator.animate(alongsideTransition: { [weak self] (_) in + self?.setFrameForPreviewLayer() + }, completion: nil) + } + func layoutErrorView() { + errorView.delegate = self + view.addSubview(errorView) + errorView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true + errorView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true + errorView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true + errorView.heightAnchor.constraint(equalToConstant: 100).isActive = true + } +} + +// MARK: - Actions +extension HIScanPointsShopViewController { + @objc func didSelectCloseButton(_ sender: HIButton) { + self.dismiss(animated: true, completion: nil) + } +} + +// MARK: - HIErrorViewDelegate +extension HIScanPointsShopViewController: HIErrorViewDelegate { + func didSelectErrorLogout(_ sender: UIButton) { + let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) + alert.addAction( + UIAlertAction(title: "Log Out", style: .destructive) { _ in + self.dismiss(animated: true, completion: nil) + NotificationCenter.default.post(name: .logoutUser, object: nil) + } + ) + alert.addAction( + UIAlertAction(title: "Cancel", style: .cancel, handler: nil) + ) + alert.popoverPresentationController?.sourceView = sender + present(alert, animated: true, completion: nil) + } +} + +// MARK: AVCaptureMetadataOutputObjectsDelegate +extension HIScanPointsShopViewController: AVCaptureMetadataOutputObjectsDelegate { + func setupCaptureSession() { + captureSession = AVCaptureSession() + let metadataOutput = AVCaptureMetadataOutput() + guard + let captureSession = captureSession, + let videoCaptureDevice = AVCaptureDevice.default(for: .video), + let videoInput = try? AVCaptureDeviceInput(device: videoCaptureDevice), + captureSession.canAddInput(videoInput), + captureSession.canAddOutput(metadataOutput) else { + loadFailed = true + return + } + captureSession.addInput(videoInput) + captureSession.addOutput(metadataOutput) + metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main) + metadataOutput.metadataObjectTypes = [.qr] + let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) + previewLayer.videoGravity = .resizeAspectFill + self.previewLayer = previewLayer + setFrameForPreviewLayer() + previewView.layer.addSublayer(previewLayer) + } + + func setFrameForPreviewLayer() { + guard let previewLayer = previewLayer else { return } + previewLayer.frame = previewView.layer.bounds + guard previewLayer.connection?.isVideoOrientationSupported == true else { return } +#warning("Not Tested") + let interfaceOrientation = UIApplication.shared.windows.first?.windowScene?.interfaceOrientation + switch interfaceOrientation { + case .portrait, .unknown: + previewLayer.connection?.videoOrientation = .portrait + case .portraitUpsideDown: + previewLayer.connection?.videoOrientation = .portraitUpsideDown + case .landscapeLeft: + previewLayer.connection?.videoOrientation = .landscapeLeft + case .landscapeRight: + previewLayer.connection?.videoOrientation = .landscapeRight + case .none: + break + @unknown default: + previewLayer.connection?.videoOrientation = .portrait + } + } + + func handlePointsShopAlert(status: String, itemName: String) { + print(status) + 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!" + error = false + case "invalidHTTPReponse(code: 404, description: \"forbidden\")": + alertTitle = "\n\nError!" + alertMessage = "\nUser has no attendee profile." + self.respondingToQRCodeFound = true + case "invalidHTTPReponse(code: 404, description: \"not found\")": + alertTitle = "\n\nError!" + alertMessage = "\nItem with itemId not found or already purchased." + self.respondingToQRCodeFound = true + case "invalidHTTPReponse(code: 400, description: \"bad request\")": + alertTitle = "\n\nError!" + alertMessage = "\nYou have insufficient funds." + self.respondingToQRCodeFound = true + default: + alertTitle = "\n\nError!" + alertMessage = "\nSomething isn't quite right." + self.respondingToQRCodeFound = true + } + // Create custom alert for points shop + let alert = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert) + let titleFont = UIFont(name: "MontserratRoman-Bold", size: 22) + let messageFont = UIFont(name: "MontserratRoman-Medium", size: 16) + let titleColor = #colorLiteral(red: 0.337254902, green: 0.1411764706, blue: 0.06666666667, alpha: 1) + let messageColor = #colorLiteral(red: 0.337254902, green: 0.1411764706, blue: 0.06666666667, alpha: 1) + let attributedTitle = NSAttributedString(string: alertTitle, attributes: [NSAttributedString.Key.font: titleFont as Any, NSAttributedString.Key.foregroundColor: titleColor]) + let attributedMessage = NSAttributedString(string: alertMessage, attributes: [NSAttributedString.Key.font: messageFont as Any, NSAttributedString.Key.foregroundColor: messageColor]) + alert.setValue(attributedTitle, forKey: "attributedTitle") + alert.setValue(attributedMessage, forKey: "attributedMessage") + + // Create image view + let imageName = error ? "Prize Bag Sad" : "Prize Bag" + let imageView = UIImageView(image: UIImage(named: imageName)) + + imageView.contentMode = .scaleAspectFit + alert.view.addSubview(imageView) + imageView.translatesAutoresizingMaskIntoConstraints = false + imageView.centerXAnchor.constraint(equalTo: alert.view.centerXAnchor).isActive = true + imageView.centerYAnchor.constraint(equalTo: alert.view.topAnchor, constant: -5).isActive = true + + if alertTitle == "Success!" { + alert.addAction( + UIAlertAction(title: "OK", style: .default, handler: { _ in + self.dismiss(animated: true, completion: nil) + //Dismisses view controller + self.didSelectCloseButton(self.closeButton) + NotificationCenter.default.post(name: .qrCodeSuccessfulScan, object: nil) + })) + } else { + alert.addAction( + UIAlertAction(title: "OK", style: .default, handler: { _ in + self.registerForKeyboardNotifications() + })) + } + self.present(alert, animated: true, completion: nil) + } + + func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) { + guard respondingToQRCodeFound else { return } + let meta = metadataObjects.first as? AVMetadataMachineReadableCodeObject + let code = meta?.stringValue ?? "" + guard let user = HIApplicationStateController.shared.user else { return } + if let rangeItemId = code.range(of: "itemId="), let rangeInstance = code.range(of: "&instance=") { + let itemId = code[rangeItemId.upperBound.. [String: AnyObject]? { + let string = token.components(separatedBy: ".") + if string.count == 1 { return nil } + let toDecode = string[1] as String + var stringtoDecode: String = toDecode.replacingOccurrences(of: "-", with: "+") // 62nd char of encoding + stringtoDecode = stringtoDecode.replacingOccurrences(of: "_", with: "/") // 63rd char of encoding + switch stringtoDecode.utf16.count % 4 { + case 2: stringtoDecode = "\(stringtoDecode)==" + case 3: stringtoDecode = "\(stringtoDecode)=" + default: // nothing to do stringtoDecode can stay the same + print("") + } + let dataToDecode = Data(base64Encoded: stringtoDecode, options: []) + let base64DecodedString = NSString(data: dataToDecode!, encoding: String.Encoding.utf8.rawValue) + var values: [String: AnyObject]? + if let string = base64DecodedString { + if let data = string.data(using: String.Encoding.utf8.rawValue, allowLossyConversion: true) { + values = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: AnyObject] + } + } + return values + } +} diff --git a/HackIllinois/ViewControllers/HIScanQRCodeViewController.swift b/HackIllinois/ViewControllers/HIScanQRCodeViewController.swift index f5676305..8f139d6e 100644 --- a/HackIllinois/ViewControllers/HIScanQRCodeViewController.swift +++ b/HackIllinois/ViewControllers/HIScanQRCodeViewController.swift @@ -20,6 +20,7 @@ import HIAPI import SwiftUI class HIScanQRCodeViewController: HIBaseViewController { + private let cornerGuideLayer = CAShapeLayer() private var captureSession: AVCaptureSession? private let containerView = HIView { $0.translatesAutoresizingMaskIntoConstraints = false @@ -40,7 +41,7 @@ class HIScanQRCodeViewController: HIBaseViewController { $0.baseImage = #imageLiteral(resourceName: "CloseButton") } private let errorView = HIErrorView(style: .codePopup) - private var selectedEventID = "" + private var selectedEventId = "" private var cancellables = Set() var currentUserID = "" var currentUserName = "" @@ -68,8 +69,8 @@ extension HIScanQRCodeViewController { setupCaptureSession() if user.roles.contains(.STAFF) { let observable = HIStaffButtonViewObservable() - observable.$selectedEventId.sink { eventID in - self.selectedEventID = eventID + observable.$selectedEventId.sink { eventId in + self.selectedEventId = eventId }.store(in: &cancellables) let staffButtonController = UIHostingController(rootView: HIStaffButtonView(observable: observable)) addChild(staffButtonController) @@ -101,7 +102,12 @@ extension HIScanQRCodeViewController { } } } - + + override func viewDidLoad() { + super.viewDidLoad() + setupCornerGuides() + } + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) } @@ -128,6 +134,49 @@ extension HIScanQRCodeViewController { errorView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true errorView.heightAnchor.constraint(equalToConstant: 100).isActive = true } + + // Add corner guides? + func setupCornerGuides() { + // Set up the layer for corner guides + cornerGuideLayer.strokeColor = UIColor.white.cgColor + cornerGuideLayer.lineWidth = 2.0 + cornerGuideLayer.fillColor = UIColor.clear.cgColor + + // Add the layer to the preview view's layer + previewView.layer.addSublayer(cornerGuideLayer) + + // Update the corner guides when the preview view size changes + NotificationCenter.default.addObserver(self, selector: #selector(updateCornerGuides), name: UIDevice.orientationDidChangeNotification, object: nil) + + // Initial update + updateCornerGuides() + } + + @objc func updateCornerGuides() { + guard let previewLayer = previewLayer else { return } + + // Calculate the corner guide size + let cornerGuideSize: CGFloat = 20.0 + let cornerGuideLineWidth: CGFloat = 2.0 + + // Calculate the offset for corner guides to make them visible within the preview view + let offset: CGFloat = 10.0 + + // Create a rectangle path for each corner + let topLeftRect = CGRect(x: offset, y: offset, width: cornerGuideSize, height: cornerGuideLineWidth) + let topRightRect = CGRect(x: previewLayer.frame.width - cornerGuideSize - offset, y: offset, width: cornerGuideSize, height: cornerGuideLineWidth) + let bottomLeftRect = CGRect(x: offset, y: previewLayer.frame.height - cornerGuideLineWidth - offset, width: cornerGuideSize, height: cornerGuideLineWidth) + let bottomRightRect = CGRect(x: previewLayer.frame.width - cornerGuideSize - offset, y: previewLayer.frame.height - cornerGuideLineWidth - offset, width: cornerGuideSize, height: cornerGuideLineWidth) + + // Create a path combining all four rectangles + let path = UIBezierPath(rect: topLeftRect) + path.append(UIBezierPath(rect: topRightRect)) + path.append(UIBezierPath(rect: bottomLeftRect)) + path.append(UIBezierPath(rect: bottomRightRect)) + + // Set the path to the layer + cornerGuideLayer.path = path.cgPath + } } // MARK: - Actions @@ -203,36 +252,52 @@ extension HIScanQRCodeViewController: AVCaptureMetadataOutputObjectsDelegate { } func handleCheckInAlert(status: String, newPoints: Int) { - var alertTitle = "" - var alertMessage = "" + var alertTitle = ""; var alertMessage = "" switch status { case "Success": - alertTitle = "Success!" - alertMessage = "You received \(newPoints) points!" + alertTitle = "\n\nSuccess!" + alertMessage = "\nYou received \(newPoints) points!" case "InvalidCode": - alertTitle = "Error!" - alertMessage = "This code doesn't seem to be correct." + alertTitle = "\n\nError!" + alertMessage = "\nThis code doesn't seem to be correct." self.respondingToQRCodeFound = true case "InvalidTime": - alertTitle = "Error!" - alertMessage = "Make sure you have the right time." + alertTitle = "\n\nError!" + alertMessage = "\nMake sure you have the right time." self.respondingToQRCodeFound = true case "AlreadyCheckedIn": - alertTitle = "Error!" - alertMessage = "Looks like you're already checked in." + alertTitle = "\n\nError!" + alertMessage = "\nLooks like you're already checked in." self.respondingToQRCodeFound = true default: - alertTitle = "Error!" - alertMessage = "Something isn't quite right." + alertTitle = "\n\nError!" + alertMessage = "\nSomething isn't quite right." self.respondingToQRCodeFound = true } + // Create custom alert for attendee check in functionality let alert = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert) + let titleFont = UIFont(name: "MontserratRoman-Bold", size: 22) + let messageFont = UIFont(name: "MontserratRoman-Medium", size: 16) + let titleColor = #colorLiteral(red: 0.337254902, green: 0.1411764706, blue: 0.06666666667, alpha: 1) + let messageColor = #colorLiteral(red: 0.337254902, green: 0.1411764706, blue: 0.06666666667, alpha: 1) + let attributedTitle = NSAttributedString(string: alertTitle, attributes: [NSAttributedString.Key.font: titleFont as Any, NSAttributedString.Key.foregroundColor: titleColor]) + let attributedMessage = NSAttributedString(string: alertMessage, attributes: [NSAttributedString.Key.font: messageFont as Any, NSAttributedString.Key.foregroundColor: messageColor]) + alert.setValue(attributedTitle, forKey: "attributedTitle") + alert.setValue(attributedMessage, forKey: "attributedMessage") + + // Create image view + let imageView = UIImageView(image: UIImage(named: "Treasure Chest")) + imageView.contentMode = .scaleAspectFit + alert.view.addSubview(imageView) + imageView.translatesAutoresizingMaskIntoConstraints = false + imageView.centerXAnchor.constraint(equalTo: alert.view.centerXAnchor).isActive = true + imageView.centerYAnchor.constraint(equalTo: alert.view.topAnchor, constant: -5).isActive = true if alertTitle == "Success!" { alert.addAction( UIAlertAction(title: "OK", style: .default, handler: { _ in - self.dismiss(animated: true, completion: nil) + //self.dismiss(animated: true, completion: nil) //Dismisses view controller - self.didSelectCloseButton(self.closeButton) + //self.didSelectCloseButton(self.closeButton) NotificationCenter.default.post(name: .qrCodeSuccessfulScan, object: nil) })) } else { @@ -244,14 +309,13 @@ extension HIScanQRCodeViewController: AVCaptureMetadataOutputObjectsDelegate { self.present(alert, animated: true, completion: nil) } - func handleStaffCheckInAlert(status: String) { - var alertTitle = "" - var alertMessage = "" + var alertTitle = ""; var alertMessage = ""; print("status is", status) switch status { case "Success": alertTitle = "Success!" - alertMessage = "Name: \(currentUserName)\n Diet: \(dietaryString)" + alertMessage = "Dietary Restrictions: \(dietaryString)" + //alertMessage = "Name: \(currentUserName)\n Diet: \(dietaryString)" case "InvalidEventId": alertTitle = "Error!" alertMessage = "Invalid Event ID" @@ -264,6 +328,10 @@ extension HIScanQRCodeViewController: AVCaptureMetadataOutputObjectsDelegate { alertTitle = "Error!" alertMessage = "Looks like you're already checked in." self.respondingToQRCodeFound = true + case "The operation couldn’t be completed. (APIManager.APIRequestError error 0.)": + alertTitle = "Error!" + alertMessage = "Your QR code is either invalid/expired or you have already checked into this event." + self.respondingToQRCodeFound = true default: alertTitle = "Error!" alertMessage = "Something isn't quite right." @@ -273,9 +341,9 @@ extension HIScanQRCodeViewController: AVCaptureMetadataOutputObjectsDelegate { if alertTitle == "Success!" { alert.addAction( UIAlertAction(title: "OK", style: .default, handler: { _ in - self.dismiss(animated: true, completion: nil) + //self.dismiss(animated: true, completion: nil) //Dismisses view controller - self.didSelectCloseButton(self.closeButton) + //self.didSelectCloseButton(self.closeButton) NotificationCenter.default.post(name: .qrCodeSuccessfulScan, object: nil) })) } else { @@ -316,46 +384,65 @@ extension HIScanQRCodeViewController: AVCaptureMetadataOutputObjectsDelegate { let meta = metadataObjects.first as? AVMetadataMachineReadableCodeObject let code = meta?.stringValue ?? "" guard let user = HIApplicationStateController.shared.user else { return } + let staffToken = user.token + //print("staff token is:", staffToken, "event id is:", selectedEventId) + print("qr code info is", code) if user.roles.contains(.STAFF) { - if selectedEventID != "" { + if selectedEventId != "" { if let range = code.range(of: "userToken=") { let userToken = code[range.upperBound...] respondingToQRCodeFound = false - HIAPI.EventService.staffCheckIn(userToken: String(userToken), eventId: selectedEventID) + HIAPI.StaffService.recordUserAttendance(userToken: String(userToken), staffToken: String(staffToken), eventId: selectedEventId) .onCompletion { result in do { let (codeResult, _) = try result.get() DispatchQueue.main.async { [self] in - if let qrInfo = self.decode(code) { - if let userId = qrInfo["userId"] { - currentUserID = userId as? String ?? "" - staffCheckIn(userID: currentUserID, status: codeResult.status) + print(codeResult.dietaryRestrictions) + // Parse dietary string + dietaryString = "" + if let dietaryRestrictions = codeResult.dietaryRestrictions, !dietaryRestrictions.isEmpty { + for (index, diet) in dietaryRestrictions.enumerated() { + dietaryString += diet + if index < dietaryRestrictions.count - 1 { + dietaryString += ", " + } } + } else { + dietaryString = "None" } + self.handleStaffCheckInAlert(status: "Success") } } catch { print(error, error.localizedDescription) + DispatchQueue.main.async { [self] in + self.handleStaffCheckInAlert(status: error.localizedDescription) + } } sleep(2) + self.respondingToQRCodeFound = true } .authorize(with: HIApplicationStateController.shared.user) .launch() + } } - } } else { respondingToQRCodeFound = false - HIAPI.EventService.checkIn(code: code) + HIAPI.UserService.userScanEvent(userToken: user.token, eventID: code) .onCompletion { result in do { let (codeResult, _) = try result.get() let status = codeResult.status DispatchQueue.main.async { - self.handleCheckInAlert(status: codeResult.status, newPoints: codeResult.newPoints) + self.handleCheckInAlert(status: "Success", newPoints: codeResult.points ?? 0) } } catch { print(error, error.localizedDescription) + DispatchQueue.main.async { [self] in + self.handleCheckInAlert(status: error.localizedDescription, newPoints: 0) + } } sleep(2) + self.respondingToQRCodeFound = true // Set respondingToQRCodeFound back to true } .authorize(with: HIApplicationStateController.shared.user) .launch() diff --git a/HackIllinois/ViewControllers/HIScheduleViewController.swift b/HackIllinois/ViewControllers/HIScheduleViewController.swift index cac1a5b0..503b57b6 100644 --- a/HackIllinois/ViewControllers/HIScheduleViewController.swift +++ b/HackIllinois/ViewControllers/HIScheduleViewController.swift @@ -18,6 +18,9 @@ import HIAPI class HIScheduleViewController: HIEventListViewController { // MARK: - Properties var staffShifts: [Staff] = [] + private var labelColor: UIColor = .white // Default color + var hasSelectedShift = false + var segmentedControl: HIScheduleSegmentedControl! lazy var fetchedResultsController: NSFetchedResultsController = { let fetchRequest: NSFetchRequest = Event.fetchRequest() @@ -85,6 +88,10 @@ class HIScheduleViewController: HIEventListViewController { // MARK: - Actions extension HIScheduleViewController { @objc func didSelectTab(_ sender: HISegmentedControl) { + if hasSelectedShift { + removeStaffShiftContainerViews() + setUpShiftCells() + } currentTab = sender.selectedIndex updatePredicate() animateReload() @@ -94,7 +101,7 @@ extension HIScheduleViewController { onlyFavorites = !onlyFavorites sender.image = onlyFavorites ? #imageLiteral(resourceName: "Big Selected Bookmark") : #imageLiteral(resourceName: "Big Unselected Bookmark") if UIDevice.current.userInterfaceIdiom == .pad { - sender.image = onlyFavorites ? #imageLiteral(resourceName: "FavoritedPad") : #imageLiteral(resourceName: "UnFavoritedPad") + sender.image = onlyFavorites ? #imageLiteral(resourceName: "BookmarkSelected") : #imageLiteral(resourceName: "BookmarkUnselected") } if sender.image == #imageLiteral(resourceName: "Big Selected Bookmark") { super.setCustomTitle(customTitle: "SAVED EVENTS") @@ -137,7 +144,7 @@ extension HIScheduleViewController { super.loadView() let items = dataStore.map { $0.displayText } - let segmentedControl = HIScheduleSegmentedControl(titles: items, nums: [23, 24, 25]) + segmentedControl = HIScheduleSegmentedControl(titles: items, nums: [23, 24, 25]) segmentedControl.addTarget(self, action: #selector(didSelectTab(_:)), for: .valueChanged) segmentedControl.translatesAutoresizingMaskIntoConstraints = false view.addSubview(segmentedControl) @@ -155,9 +162,11 @@ extension HIScheduleViewController { let now = Date() if now > HITimeDataSource.shared.eventTimes.sundayStart { segmentedControl.selectedIndex = 2 + currentTab = 2 } else if now > HITimeDataSource.shared.eventTimes.saturdayStart { segmentedControl.selectedIndex = 1 + currentTab = 1 } let tableView = HITableView() @@ -191,9 +200,10 @@ extension HIScheduleViewController { } // MARK: - Staff Shifts Control Setup + extension HIScheduleViewController { @objc func setStaffShiftsControl() { - let customFontSize = UIDevice.current.userInterfaceIdiom == .pad ? 44 : 24 + let customFontSize = UIDevice.current.userInterfaceIdiom == .pad ? 48 : 24 let customFont = UIFont(name: "MontserratRoman-Bold", size: CGFloat(customFontSize)) // Create flexible space items to add space to the left @@ -202,7 +212,7 @@ extension HIScheduleViewController { let flexibleSpaceLeft3 = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) let scheduleButton = UIBarButtonItem(title: "SCHEDULE", style: .plain, target: self, action: #selector(scheduleButtonTapped(_:))) - scheduleButton.setTitleTextAttributes([NSAttributedString.Key.font: customFont], for: .normal) + scheduleButton.setTitleTextAttributes([NSAttributedString.Key.font: customFont, NSAttributedString.Key.foregroundColor: labelColor], for: .normal) // Add the flexible space items and custom button to the leftBarButtonItems array navigationItem.leftBarButtonItems = [flexibleSpaceLeft1, flexibleSpaceLeft2, flexibleSpaceLeft3, scheduleButton] @@ -213,27 +223,45 @@ extension HIScheduleViewController { // Create custom right bar button item let customButton = UIBarButtonItem(title: "SHIFTS", style: .plain, target: self, action: #selector(shiftsButtonTapped(_:))) - customButton.setTitleTextAttributes([NSAttributedString.Key.font: customFont], for: .normal) + customButton.setTitleTextAttributes([NSAttributedString.Key.font: customFont, NSAttributedString.Key.foregroundColor: labelColor], for: .normal) // Add the flexible space items and custom button to the rightBarButtonItems array navigationItem.rightBarButtonItems = [flexibleSpaceRight1, flexibleSpaceRight2, customButton] self.navigationItem.leftItemsSupplementBackButton = true } - + + func removeStaffShiftContainerViews() { + // Iterate through all subviews and remove container views for staff shifts + for subview in self.view.subviews { + if let containerView = subview as? UIView, containerView.backgroundColor == #colorLiteral(red: 1, green: 0.9803921569, blue: 0.8, alpha: 1) { + containerView.removeFromSuperview() + } + } + } + // Actions for left and right buttons @objc func scheduleButtonTapped(_ sender: UIButton) { if onlyShifts { onlyShifts = false + backgroundView.image = #imageLiteral(resourceName: "PurpleBackground") + labelColor = .white // Set label color to brown + setStaffShiftsControl() + // Call removeStaffShiftContainerViews to remove container views for staff shifts + hasSelectedShift = false + removeStaffShiftContainerViews() updatePredicate() animateReload() } } - @objc func shiftsButtonTapped(_ sender: UIButton) { if !onlyShifts { onlyShifts = !onlyShifts + backgroundView.image = #imageLiteral(resourceName: "Pink Background") + hasSelectedShift = true + labelColor = #colorLiteral(red: 0.337254902, green: 0.1411764706, blue: 0.06666666667, alpha: 1) // Set label color to brown + setStaffShiftsControl() guard let user = HIApplicationStateController.shared.user else { return } @@ -245,9 +273,13 @@ extension HIScheduleViewController { print("Staff shifts: ", self.staffShifts) DispatchQueue.main.async { + // Set up shift cells + self.setUpShiftCells() + // Update predicate and animate reload self.updatePredicate() self.animateReload() } + } catch { print("An error has occurred in getting staff shifts \(error)") } @@ -255,6 +287,103 @@ extension HIScheduleViewController { .launch() } } + + func setUpShiftCells() { + // Get filtered events by date + let sundayStart = HITimeDataSource.shared.eventTimes.sundayStart + let saturdayStart = HITimeDataSource.shared.eventTimes.saturdayStart + var padding = 0.0 + // Iterate through each staff shift and add a label to the container view + for (index, staffShift) in self.staffShifts.enumerated() { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z" + let dateString = staffShift.startTime + let calendar = Calendar.current + let dayComponent = calendar.component(.day, from: dateString) + var curr_idx = segmentedControl.selectedIndex + if curr_idx == 0 && dayComponent != 23 { + continue + } else if curr_idx == 1 && dayComponent != 24 { + continue + } else if curr_idx == 2 && dayComponent != 25 { + continue + } + // Set fixed width and height for the container view + let containerViewWidth: CGFloat = UIScreen.main.bounds.width > 850 ? 820 : ((UIDevice.current.userInterfaceIdiom == .pad) ? 620 : 340.0) + let containerViewHeight: CGFloat = UIScreen.main.bounds.width > 850 ? 250 : ((UIDevice.current.userInterfaceIdiom == .pad) ? 200 : 130.0) + + // Create a container view with a yellow background + let containerView = UIView() + containerView.translatesAutoresizingMaskIntoConstraints = false + containerView.backgroundColor = #colorLiteral(red: 1, green: 0.9803921569, blue: 0.8, alpha: 1) + containerView.layer.cornerRadius = 20.0 + containerView.layer.masksToBounds = true + + // Add the container view to the main view + self.view.addSubview(containerView) + + // Set up constraints for the fixed width and height + NSLayoutConstraint.activate([ + containerView.widthAnchor.constraint(equalToConstant: containerViewWidth), + containerView.heightAnchor.constraint(equalToConstant: containerViewHeight), + containerView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor), + containerView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: ((UIDevice.current.userInterfaceIdiom == .pad) ? 400 : 275) + padding) + ]) + let label = HILabel(style: .event) + label.text = staffShift.name + label.translatesAutoresizingMaskIntoConstraints = false + + // Add the label to the container view + containerView.addSubview(label) + + // Set up constraints for the labels within the container view + NSLayoutConstraint.activate([ + label.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 15.0), + label.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 20.0) + ]) + + // Add time, location, and description labels to shift cells + // Time label set up + var eventCellSpacing: CGFloat = 8.0 + let locationImageName = (UIDevice.current.userInterfaceIdiom == .pad) ? "VectorPad" : "LocationSign" + let timeImageName = (UIDevice.current.userInterfaceIdiom == .pad) ? "TimePad" : "Clock" + var locationImageView = UIImageView(image: #imageLiteral(resourceName: "\(locationImageName)")); var timeImageView = UIImageView(image: #imageLiteral(resourceName: "\(timeImageName)")) + let timeLabel = HILabel(style: .time) + timeLabel.text = Formatter.simpleTime.string(from: staffShift.startTime) + " - " + Formatter.simpleTime.string(from: staffShift.endTime) + containerView.addSubview(timeImageView) + timeImageView.translatesAutoresizingMaskIntoConstraints = false + timeImageView.leadingAnchor.constraint(equalTo: label.leadingAnchor).isActive = true + timeImageView.bottomAnchor.constraint(equalTo: label.bottomAnchor, constant: UIScreen.main.bounds.width > 850 ? 50 : ((UIDevice.current.userInterfaceIdiom == .pad) ? 40 : 25.0)).isActive = true + containerView.addSubview(timeLabel) + timeLabel.leadingAnchor.constraint(equalTo: timeImageView.trailingAnchor, constant: eventCellSpacing).isActive = true + timeLabel.centerYAnchor.constraint(equalTo: timeImageView.centerYAnchor).isActive = true + + // Location label set up + let locationLabel = HILabel(style: .newLocation) + if staffShift.locations.count > 0 { + locationLabel.text = staffShift.locations.map { $0.name }.joined(separator: ", ") + } else { + locationLabel.text = "No Location" + } + containerView.addSubview(locationImageView) + locationImageView.translatesAutoresizingMaskIntoConstraints = false + containerView.addSubview(locationLabel) + locationImageView.leadingAnchor.constraint(equalTo: timeImageView.leadingAnchor, constant: (UIDevice.current.userInterfaceIdiom == .pad ? 5.0 : 1.0)).isActive = true + locationImageView.bottomAnchor.constraint(equalTo: timeImageView.bottomAnchor, constant: UIScreen.main.bounds.width > 850 ? 50 : ((UIDevice.current.userInterfaceIdiom == .pad) ? 40 : 25.0)).isActive = true + locationLabel.leadingAnchor.constraint(equalTo: timeLabel.leadingAnchor).isActive = true + locationLabel.centerYAnchor.constraint(equalTo: locationImageView.centerYAnchor).isActive = true + + // Description label set up + let descriptionLabel = HILabel(style: .cellDescription) + descriptionLabel.numberOfLines = 1 + descriptionLabel.text = "\(staffShift.description)" + containerView.addSubview(descriptionLabel) + descriptionLabel.translatesAutoresizingMaskIntoConstraints = false + descriptionLabel.leadingAnchor.constraint(equalTo: locationImageView.leadingAnchor).isActive = true + descriptionLabel.bottomAnchor.constraint(equalTo: locationImageView.bottomAnchor, constant: UIScreen.main.bounds.width > 850 ? 50 : ((UIDevice.current.userInterfaceIdiom == .pad) ? 40 : 25.0)).isActive = true + padding += UIScreen.main.bounds.width > 850 ? 300 : ((UIDevice.current.userInterfaceIdiom == .pad) ? 240 : 150.0) + } + } } @@ -265,7 +394,7 @@ extension HIScheduleViewController { if !HIApplicationStateController.shared.isGuest { navigationItem.rightBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "MenuUnfavorited"), style: .plain, target: self, action: #selector(didSelectFavoritesIcon(_:))) if UIDevice.current.userInterfaceIdiom == .pad { - navigationItem.rightBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "UnFavoritedPad"), style: .plain, target: self, action: #selector(didSelectFavoritesIcon(_:))) + navigationItem.rightBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "BookmarkUnselected"), style: .plain, target: self, action: #selector(didSelectFavoritesIcon(_:))) } } } diff --git a/HackIllinois/ViewControllers/HIStaffButtonView.swift b/HackIllinois/ViewControllers/HIStaffButtonView.swift index d9201dbd..4f2f44f1 100644 --- a/HackIllinois/ViewControllers/HIStaffButtonView.swift +++ b/HackIllinois/ViewControllers/HIStaffButtonView.swift @@ -10,29 +10,37 @@ import SwiftUI import HIAPI struct HIStaffButtonView: View { - @State var events = [HIAPI.StaffEvent]() + @State var events = [HIAPI.Event]() @State var highlightedID = "" @ObservedObject var observable: HIStaffButtonViewObservable - let columns = [GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible())] + + let columns = [ + GridItem(.flexible(minimum: 0)), + GridItem(.flexible(minimum: 0)) + ] + var body: some View { ScrollView { - LazyVGrid(columns: columns, spacing: 10) { + LazyVGrid(columns: columns, spacing: 30) { ForEach(events, id: \.id) { event in Button(action: { highlightedID = event.id observable.selectedEventId = event.id }) { - Text(event.name) - .foregroundColor(event.id == highlightedID ? .white : Color((\HIAppearance.profileBaseText).value)) - .font(Font(HIAppearance.Font.QRCheckInFont ?? .systemFont(ofSize: 14))) - .padding(4) - .cornerRadius(10) - .background( - RoundedRectangle(cornerRadius: 10) - .stroke(Color.white, lineWidth: 2) - .background(event.id == highlightedID ? Color((\HIAppearance.profileBaseText).value) : Color.white) - .cornerRadius(10) - ) + VStack { + Text(event.name) + .lineLimit(2) // Set lineLimit + .foregroundColor(event.id == highlightedID ? .white : Color((\HIAppearance.profileBaseText).value)) + .font(Font(HIAppearance.Font.QRCheckInFont ?? .systemFont(ofSize: 14))) + .padding(5) + .frame(maxWidth: .infinity, minHeight: 50) + .background( + RoundedRectangle(cornerRadius: 20) + .stroke(Color.white, lineWidth: 2) + .background(event.id == highlightedID ? Color((\HIAppearance.profileBaseText).value) : Color.white.opacity(0.50)) + .cornerRadius(20) + ) + } } } } @@ -41,13 +49,15 @@ struct HIStaffButtonView: View { getStaffEvents() } } + func getStaffEvents() { - HIAPI.EventService.getStaffCheckInEvents(authToken: HIApplicationStateController.shared.user?.token ?? "") + guard let user = HIApplicationStateController.shared.user else { return } + HIAPI.EventService.getStaffCheckInEvents(authToken: user.token) .onCompletion { result in do { let (containedEvents, _) = try result.get() DispatchQueue.main.async { - self.events = containedEvents.events + self.events = containedEvents.events.filter { $0.displayOnStaffCheckIn == true } } } catch { print("An error has occurred \(error)")