diff --git a/HackIllinois/Assets.xcassets/flame-vector.imageset/Contents.json b/HackIllinois/Assets.xcassets/flame-vector.imageset/Contents.json new file mode 100644 index 00000000..df240cf1 --- /dev/null +++ b/HackIllinois/Assets.xcassets/flame-vector.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Vector.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Vector@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Vector@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/HackIllinois/Assets.xcassets/flame-vector.imageset/Vector.png b/HackIllinois/Assets.xcassets/flame-vector.imageset/Vector.png new file mode 100644 index 00000000..7e3f5b1a Binary files /dev/null and b/HackIllinois/Assets.xcassets/flame-vector.imageset/Vector.png differ diff --git a/HackIllinois/Assets.xcassets/flame-vector.imageset/Vector@2x.png b/HackIllinois/Assets.xcassets/flame-vector.imageset/Vector@2x.png new file mode 100644 index 00000000..75d396f7 Binary files /dev/null and b/HackIllinois/Assets.xcassets/flame-vector.imageset/Vector@2x.png differ diff --git a/HackIllinois/Assets.xcassets/flame-vector.imageset/Vector@3x.png b/HackIllinois/Assets.xcassets/flame-vector.imageset/Vector@3x.png new file mode 100644 index 00000000..7d72802a Binary files /dev/null and b/HackIllinois/Assets.xcassets/flame-vector.imageset/Vector@3x.png differ diff --git a/HackIllinois/Assets.xcassets/pillar-vector.imageset/Contents.json b/HackIllinois/Assets.xcassets/pillar-vector.imageset/Contents.json new file mode 100644 index 00000000..5c94eb99 --- /dev/null +++ b/HackIllinois/Assets.xcassets/pillar-vector.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Group 995.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Group 995@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Group 995@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/HackIllinois/Assets.xcassets/pillar-vector.imageset/Group 995.png b/HackIllinois/Assets.xcassets/pillar-vector.imageset/Group 995.png new file mode 100644 index 00000000..e12cd79c Binary files /dev/null and b/HackIllinois/Assets.xcassets/pillar-vector.imageset/Group 995.png differ diff --git a/HackIllinois/Assets.xcassets/pillar-vector.imageset/Group 995@2x.png b/HackIllinois/Assets.xcassets/pillar-vector.imageset/Group 995@2x.png new file mode 100644 index 00000000..507bfac8 Binary files /dev/null and b/HackIllinois/Assets.xcassets/pillar-vector.imageset/Group 995@2x.png differ diff --git a/HackIllinois/Assets.xcassets/pillar-vector.imageset/Group 995@3x.png b/HackIllinois/Assets.xcassets/pillar-vector.imageset/Group 995@3x.png new file mode 100644 index 00000000..f12ff7fd Binary files /dev/null and b/HackIllinois/Assets.xcassets/pillar-vector.imageset/Group 995@3x.png differ diff --git a/HackIllinois/Assets.xcassets/profile-orb.imageset/Contents.json b/HackIllinois/Assets.xcassets/profile-orb.imageset/Contents.json new file mode 100644 index 00000000..5c94eb99 --- /dev/null +++ b/HackIllinois/Assets.xcassets/profile-orb.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Group 995.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Group 995@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Group 995@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/HackIllinois/Assets.xcassets/profile-orb.imageset/Group 995.png b/HackIllinois/Assets.xcassets/profile-orb.imageset/Group 995.png new file mode 100644 index 00000000..4775c7a4 Binary files /dev/null and b/HackIllinois/Assets.xcassets/profile-orb.imageset/Group 995.png differ diff --git a/HackIllinois/Assets.xcassets/profile-orb.imageset/Group 995@2x.png b/HackIllinois/Assets.xcassets/profile-orb.imageset/Group 995@2x.png new file mode 100644 index 00000000..be00a82c Binary files /dev/null and b/HackIllinois/Assets.xcassets/profile-orb.imageset/Group 995@2x.png differ diff --git a/HackIllinois/Assets.xcassets/profile-orb.imageset/Group 995@3x.png b/HackIllinois/Assets.xcassets/profile-orb.imageset/Group 995@3x.png new file mode 100644 index 00000000..a6d3ba4b Binary files /dev/null and b/HackIllinois/Assets.xcassets/profile-orb.imageset/Group 995@3x.png differ diff --git a/HackIllinois/Assets.xcassets/profile-rank-label-background.imageset/Contents.json b/HackIllinois/Assets.xcassets/profile-rank-label-background.imageset/Contents.json new file mode 100644 index 00000000..f621aa14 --- /dev/null +++ b/HackIllinois/Assets.xcassets/profile-rank-label-background.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "SAVE.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "SAVE@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "SAVE@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/HackIllinois/Assets.xcassets/profile-rank-label-background.imageset/SAVE.png b/HackIllinois/Assets.xcassets/profile-rank-label-background.imageset/SAVE.png new file mode 100644 index 00000000..52b24e85 Binary files /dev/null and b/HackIllinois/Assets.xcassets/profile-rank-label-background.imageset/SAVE.png differ diff --git a/HackIllinois/Assets.xcassets/profile-rank-label-background.imageset/SAVE@2x.png b/HackIllinois/Assets.xcassets/profile-rank-label-background.imageset/SAVE@2x.png new file mode 100644 index 00000000..a7084934 Binary files /dev/null and b/HackIllinois/Assets.xcassets/profile-rank-label-background.imageset/SAVE@2x.png differ diff --git a/HackIllinois/Assets.xcassets/profile-rank-label-background.imageset/SAVE@3x.png b/HackIllinois/Assets.xcassets/profile-rank-label-background.imageset/SAVE@3x.png new file mode 100644 index 00000000..2e9de6c0 Binary files /dev/null and b/HackIllinois/Assets.xcassets/profile-rank-label-background.imageset/SAVE@3x.png differ diff --git a/HackIllinois/UI/HIAppearance.swift b/HackIllinois/UI/HIAppearance.swift index 69657340..3a23b7a4 100644 --- a/HackIllinois/UI/HIAppearance.swift +++ b/HackIllinois/UI/HIAppearance.swift @@ -383,7 +383,7 @@ struct HIAppearance: Equatable { static let loginSelection = UIFont(name: "MontserratRoman-SemiBold", size: UIDevice.current.userInterfaceIdiom == .pad ? 26: 16) // Profile-related fonts - static let profileName = UIFont(name: "MontserratRoman-Bold", size: UIDevice.current.userInterfaceIdiom == .pad ? 40 : 20) + static let profileName = UIFont(name: "MontserratRoman-SemiBold", size: UIDevice.current.userInterfaceIdiom == .pad ? 40 : 22) static let profileSubtitle = UIFont(name: "MontserratRoman-Bold", size: UIDevice.current.userInterfaceIdiom == .pad ? 24 : 12) static let profileDietaryRestrictions = UIFont(name: "MontserratRoman-SemiBold", size: UIDevice.current.userInterfaceIdiom == .pad ? 32 : 16) static let profileDietaryRestrictionsLabel = UIFont(name: "MontserratRoman-Bold", size: UIDevice.current.userInterfaceIdiom == .pad ? 24 : 12) @@ -391,6 +391,7 @@ struct HIAppearance: Equatable { static let profileNumberFigure = UIFont(name: "MontserratRoman-SemiBold", size: 24) static let profileUsername = UIFont(name: "MontserratRoman-Bold", size: 16) static let profileInterests = UIFont(name: "MontserratRoman-SemiBold", size: 14) + static let profileRank = UIFont(name: "MontserratRoman-Bold", size: 16) // QR code fonts static let QRCheckInFont = UIFont(name: "MontserratRoman-SemiBold", size: 14) diff --git a/HackIllinois/ViewControllers/HIProfileCardView.swift b/HackIllinois/ViewControllers/HIProfileCardView.swift index 01ba4229..a9b89c0b 100644 --- a/HackIllinois/ViewControllers/HIProfileCardView.swift +++ b/HackIllinois/ViewControllers/HIProfileCardView.swift @@ -62,89 +62,173 @@ struct HIProfileCardView: View { // Factors used to change frame to alter based on device let padFactor = UIScreen.main.bounds.height/1366 let phoneFactor = UIScreen.main.bounds.height/844 - + + // Screen size + let screenWidth = UIScreen.main.bounds.width + let screenHeight = UIScreen.main.bounds.height + + var body: some View { - ScrollView { - ZStack { - ZStack(alignment: .top) { - ZStack(alignment: .bottom) { - Image("ProfileCardBackground") - .resizable() - .frame(width: UIDevice.current.userInterfaceIdiom == .pad ? 566.93*padFactor : 338*phoneFactor, height: UIDevice.current.userInterfaceIdiom == .pad ? 777.2*padFactor : 463.36*phoneFactor) - ZStack { - Image("ProfileBanner") - .resizable() - .frame(width: UIDevice.current.userInterfaceIdiom == .pad ? 395.7*padFactor : 235.91*phoneFactor, height: UIDevice.current.userInterfaceIdiom == .pad ? 137.71*padFactor : 82.1*phoneFactor) - VStack(spacing: 0) { - Text("Your Ranking") - .foregroundColor(.white) - - .font(Font(HIAppearance.Font.profileSubtitle ?? .systemFont(ofSize: 20))) - HStack(alignment: .bottom, spacing: UIDevice.current.userInterfaceIdiom == .pad ? 5*padFactor : 5*phoneFactor) { - Image("RankSymbol") - Text("\(rank != 0 ? "\(rank)" : "...")") - .foregroundColor(.white) - .font(Font(HIAppearance.Font.profileSubtitle ?? .systemFont(ofSize: 20))) - .onAppear { - // Call getRank and update the rank when it's available - getRank { rank in - self.rank = rank - } - } - } - }.padding(.bottom, isIpad ? 40*padFactor : 25*phoneFactor) - }.alignmentGuide(.bottom) {dimensions in dimensions[.bottom] / 1.2 } - } - Image(uiImage: avatarUrl.load()) - .resizable() - .frame(width: isIpad ? 249.92*padFactor : 149*phoneFactor, height: isIpad ? 286.4*padFactor : 170.75*phoneFactor) - .alignmentGuide(.top) {dimensions in dimensions[VerticalAlignment.center] / 0.9 } + // Y coordinate position calculation placeholders so the compiler doesn't get mad at long arithmetic within views + let height_adjustment_factor = (40 / 841) * screenHeight + + let name_spacing = (screenHeight * (210 / 841)) / 2 + (15 / 841) * screenHeight // Scaled 15px spacing + + // Base Y coordinate + let base_y = (521 / 841) * screenHeight + + // Half heights for stacking calculations + let half_pillar_height = (screenHeight * (262 / 841)) / 2 + let half_flame_height = (screenHeight * (450 / 841)) / 2 + let half_orb_height = (screenHeight * (140.49 / 841)) / 2 + let half_qr_height = (screenHeight * (210 / 841)) / 2 + + let full_flame_height = (screenHeight * (450 / 841)) + let full_orb_height = ( 140.49 / 841) * screenHeight + + // Additional offsets + let flame_offset = (13 / 841) * screenHeight + let qr_offset = (210 / 841) * screenHeight + + // Calculated positions + let flame_y = base_y - half_pillar_height - half_flame_height + flame_offset + height_adjustment_factor + let pillar_y = base_y // Centered at the bottom + let orb_y = base_y - half_pillar_height - full_flame_height - half_orb_height + flame_offset + full_orb_height + height_adjustment_factor + let qr_y = base_y - half_pillar_height - half_flame_height - half_qr_height + qr_offset + let name_y = qr_y - name_spacing + let avatar_y = orb_y + + + + ZStack(alignment: .bottom) { + Image("flame-vector") + .resizable() + .scaledToFit() + .frame( + width: screenWidth * (371.01 / 393), // Scaled width + height: screenHeight * (450 / 841) // Scaled height + ) + .position( + x: screenWidth / 2, + y: flame_y + ) // Position at the top of the pillar + + // Pillar Image - Anchored at the bottom + Image("pillar-vector") + .resizable() + .scaledToFit() + .frame( + width: screenWidth * (361 / 393), // Scaled width + height: screenHeight * (262 / 841) // Scaled height + ) + .position( + x: screenWidth / 2, + y: pillar_y + ) + + // Profile Orb - Stacked on top of the flame + Image("profile-orb") + .resizable() + .scaledToFit() + .frame( + width: screenWidth * (174.99 / 393), // Scaled width + height: screenHeight * (140.49 / 841) // Scaled height + ) + .position( + x: screenWidth / 2, + y: orb_y + ) // Positioned on top of the flame + + // Avatar Image - Positioned in the center of the profile orb + Image(uiImage: avatarUrl.load()) + .resizable() + .scaledToFit() + .frame(width: screenWidth * (88.82 / 393), height: screenHeight * (86.91 / 841)) + .clipShape(Circle()) +// .overlay( +// Circle() +// .stroke(Color.red, lineWidth: 3) // Add a red outline to test the positioning of the avatar +// ) + .position(x: screenWidth / 2, y: avatar_y) + + + // Name Label - Positioned Above the QR Code + Text(displayName) + .font(Font(HIAppearance.Font.profileName ?? .systemFont(ofSize: 22))) // Adjust font size as needed + .foregroundColor(Color.black) // White text color + .multilineTextAlignment(.center) + .position( + x: screenWidth / 2, + y: name_y + ) + + // QR Code - Positioned inside the Flame + if let qrCodeData = getQRCodeDate(text: qrInfo), let qrCodeImage = UIImage(data: qrCodeData) { + Image(uiImage: qrCodeImage) + .resizable() + .scaledToFit() + .frame( + width: screenWidth * (208 / 393), // Scaled width + height: screenHeight * (210 / 841) // Scaled height + ) + .position( + x: screenWidth / 2, + y: qr_y + ) // Centered inside the flame + } + + + // VStack for User Information inside the pillar + VStack(spacing: screenHeight * (10 / 841)) { + // HStack for Role and Wave labels + HStack(spacing: screenWidth * (10 / 393)) { + Text(role) + .font(Font(HIAppearance.Font.profileTier ?? .systemFont(ofSize: 18))) + .foregroundColor(.white) + .frame(width: screenWidth * (120 / 393), height: screenHeight * (38 / 841)) + .background(Color(red: 217/255, green: 217/255, blue: 217/255).opacity(0.5)) + .cornerRadius(12) + + Text("Wave \(foodWave)") + .font(Font(HIAppearance.Font.profileTier ?? .systemFont(ofSize: 18))) + .foregroundColor(.white) + .frame(width: screenWidth * (120 / 393), height: screenHeight * (38 / 841)) + .background(Color(red: 217/255, green: 217/255, blue: 217/255).opacity(0.5)) + .cornerRadius(12) } - VStack(spacing: 0) { - Spacer().frame(height: UIDevice.current.userInterfaceIdiom == .pad ? 90*padFactor : (90*phoneFactor)) - Text(formatName()) - .font(Font(HIAppearance.Font.profileName ?? .systemFont(ofSize: 24))) - .foregroundColor(Color((\HIAppearance.countdownTextColor).value)) - .padding(isIpad ? 32*padFactor : 16*phoneFactor) - HStack(spacing: isIpad ? 16*padFactor : 8*phoneFactor) { - Rectangle() - .frame(width: isIpad ? 148*padFactor : 74*phoneFactor, height: isIpad ? 48*padFactor : 24*phoneFactor) - .cornerRadius(isIpad ? 40*padFactor : 20*phoneFactor) - .foregroundColor(Color(red: 226/255, green: 142/255, blue: 174/255)) - .overlay( - Text(role) - .font(Font(HIAppearance.Font.profileSubtitle ?? .systemFont(ofSize: 12))) - .foregroundColor(Color(red: 1, green: 248/255, blue: 245/255)) - ) - - Rectangle() - .frame(width: isIpad ? 136*padFactor : 68*phoneFactor, height: isIpad ? 48*padFactor : 24*phoneFactor) - .cornerRadius(isIpad ? 40*padFactor : 20*phoneFactor) - .foregroundColor( - (Color(red: 226/255, green: 142/255, blue: 174/255))) - .overlay( - Text("Wave \(foodWave)") - .font(Font(HIAppearance.Font.profileSubtitle ?? .systemFont(ofSize: 12))) - .foregroundColor((Color(red: 1, green: 248/255, blue: 245/255))) - ) - }.padding(.bottom) - Image(uiImage: UIImage(data: getQRCodeDate(text: qrInfo)!)!) - .resizable() - .frame(width: isIpad ? 371*padFactor : 221*phoneFactor, height: isIpad ? 371*padFactor : 221*phoneFactor) - .padding(.bottom, 20*phoneFactor) + + // HStack for Rank Label (Text + Rank Value) + HStack(spacing: screenWidth * (20 / 393)) { + Text("Rank") + .font(Font(HIAppearance.Font.profileTier ?? .systemFont(ofSize: 18))) + .foregroundColor(.white) + + Text("\(rank != 0 ? "\(rank)" : "...")") + .font(Font(HIAppearance.Font.profileRank ?? .systemFont(ofSize: 16))) + .foregroundColor(Color(red: 97/255, green: 37/255, blue: 71/255)) // Updated color + .frame(width: screenWidth * (84.5 / 393), height: screenHeight * (27.26 / 841)) + .background( + Image("profile-rank-label-background") // Use imported image + .resizable() + .scaledToFit() + .frame(width: screenWidth * (84.5 / 393), height: screenHeight * (27.26 / 841)) // Match dimensions + ) + .onAppear { + // Call getRank and update the rank when it's available + getRank { rank in + self.rank = rank + } + } } - .padding(.top, isIpad ? 50*padFactor : 0) + } - .padding(.top, 24) - } - .preferredColorScheme(.dark) - .onAppear { - startFetchingQR = true - QRFetchLoop() - } - .onDisappear { - startFetchingQR = false + .position( + x: screenWidth / 2, + y: (591 / 841) * screenHeight - (screenHeight * (262 / 841)) / 2 + (55 / 841) * screenHeight // Position based on Figma + ) } + .edgesIgnoringSafeArea(.bottom) // Extend to the bottom edge + .offset(y: 30 * (UIScreen.main.bounds.height/926)) } func formatName() -> String { @@ -171,8 +255,9 @@ struct HIProfileCardView: View { // Change color of QR code guard let colorFilter = CIFilter(name: "CIFalseColor") else { return nil } colorFilter.setValue(filter.outputImage, forKey: "inputImage") - colorFilter.setValue(CIColor(red: 1, green: 248/255, blue: 245/255), forKey: "inputColor1") // Background off-white - colorFilter.setValue(CIColor(red: 102/255, green: 43/255, blue: 19/255), forKey: "inputColor0") // Barcode brown + colorFilter.setValue(CIColor(red: 0, green: 0, blue: 0, alpha: 0), forKey: "inputColor1") // Background transparent + colorFilter.setValue(CIColor(red: 0.337254902, green: 0.1411764706, blue: 0.06666666667), forKey: "inputColor0") // Barcode brown + guard let ciimage = colorFilter.outputImage else { return nil } let transform = CGAffineTransform(scaleX: 10, y: 10) diff --git a/HackIllinois/ViewControllers/HIProfileCardView_old.swift b/HackIllinois/ViewControllers/HIProfileCardView_old.swift new file mode 100644 index 00000000..01ba4229 --- /dev/null +++ b/HackIllinois/ViewControllers/HIProfileCardView_old.swift @@ -0,0 +1,239 @@ +// +// HIProfileViewController.swift +// HackIllinois +// +// Created by HackIllinois Team on 11/30/22. +// Copyright © 2022 HackIllinois. All rights reserved. +// This file is part of the Hackillinois iOS App. +// The Hackillinois iOS App is open source software, released under the University of +// Illinois/NCSA Open Source License. You should have received a copy of +// this license in a file with the distribution. +// + +import SwiftUI +import URLImage +import HIAPI + +// Loads url data and converts into image +//extension String { +// func loadImage(completion: @escaping (UIImage?) -> Void) { +// guard let url = URL(string: self) else { +// completion(nil) +// return +// } +// +// URLSession.shared.dataTask(with: url) { data, _, error in +// if let data = data, let image = UIImage(data: data) { +// completion(image) +// } else { +// completion(nil) +// } +// }.resume() +// } +//} + +extension String { + func load() -> UIImage { + do { + guard let url = URL(string: self) else { + return UIImage() + } + let data: Data = try Data(contentsOf: url) + return UIImage(data: data) ?? UIImage() + } catch {} + return UIImage() + } +} + +struct HIProfileCardView: View { + @State private var rank: Int = 0 + let displayName: String + let points: Int + let tier: String + let foodWave: Int + let avatarUrl: String + let background = (\HIAppearance.profileCardBackground).value + let baseText = (\HIAppearance.profileBaseText).value + let userId: String + let isIpad = UIDevice.current.userInterfaceIdiom == .pad + let role: String + @State var startFetchingQR = false + @State var qrInfo = "hackillinois://user?userToken=11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111" +// Factors used to change frame to alter based on device + let padFactor = UIScreen.main.bounds.height/1366 + let phoneFactor = UIScreen.main.bounds.height/844 + + var body: some View { + ScrollView { + ZStack { + ZStack(alignment: .top) { + ZStack(alignment: .bottom) { + Image("ProfileCardBackground") + .resizable() + .frame(width: UIDevice.current.userInterfaceIdiom == .pad ? 566.93*padFactor : 338*phoneFactor, height: UIDevice.current.userInterfaceIdiom == .pad ? 777.2*padFactor : 463.36*phoneFactor) + ZStack { + Image("ProfileBanner") + .resizable() + .frame(width: UIDevice.current.userInterfaceIdiom == .pad ? 395.7*padFactor : 235.91*phoneFactor, height: UIDevice.current.userInterfaceIdiom == .pad ? 137.71*padFactor : 82.1*phoneFactor) + VStack(spacing: 0) { + Text("Your Ranking") + .foregroundColor(.white) + + .font(Font(HIAppearance.Font.profileSubtitle ?? .systemFont(ofSize: 20))) + HStack(alignment: .bottom, spacing: UIDevice.current.userInterfaceIdiom == .pad ? 5*padFactor : 5*phoneFactor) { + Image("RankSymbol") + Text("\(rank != 0 ? "\(rank)" : "...")") + .foregroundColor(.white) + .font(Font(HIAppearance.Font.profileSubtitle ?? .systemFont(ofSize: 20))) + .onAppear { + // Call getRank and update the rank when it's available + getRank { rank in + self.rank = rank + } + } + } + }.padding(.bottom, isIpad ? 40*padFactor : 25*phoneFactor) + }.alignmentGuide(.bottom) {dimensions in dimensions[.bottom] / 1.2 } + } + Image(uiImage: avatarUrl.load()) + .resizable() + .frame(width: isIpad ? 249.92*padFactor : 149*phoneFactor, height: isIpad ? 286.4*padFactor : 170.75*phoneFactor) + .alignmentGuide(.top) {dimensions in dimensions[VerticalAlignment.center] / 0.9 } + } + VStack(spacing: 0) { + Spacer().frame(height: UIDevice.current.userInterfaceIdiom == .pad ? 90*padFactor : (90*phoneFactor)) + Text(formatName()) + .font(Font(HIAppearance.Font.profileName ?? .systemFont(ofSize: 24))) + .foregroundColor(Color((\HIAppearance.countdownTextColor).value)) + .padding(isIpad ? 32*padFactor : 16*phoneFactor) + HStack(spacing: isIpad ? 16*padFactor : 8*phoneFactor) { + Rectangle() + .frame(width: isIpad ? 148*padFactor : 74*phoneFactor, height: isIpad ? 48*padFactor : 24*phoneFactor) + .cornerRadius(isIpad ? 40*padFactor : 20*phoneFactor) + .foregroundColor(Color(red: 226/255, green: 142/255, blue: 174/255)) + .overlay( + Text(role) + .font(Font(HIAppearance.Font.profileSubtitle ?? .systemFont(ofSize: 12))) + .foregroundColor(Color(red: 1, green: 248/255, blue: 245/255)) + ) + + Rectangle() + .frame(width: isIpad ? 136*padFactor : 68*phoneFactor, height: isIpad ? 48*padFactor : 24*phoneFactor) + .cornerRadius(isIpad ? 40*padFactor : 20*phoneFactor) + .foregroundColor( + (Color(red: 226/255, green: 142/255, blue: 174/255))) + .overlay( + Text("Wave \(foodWave)") + .font(Font(HIAppearance.Font.profileSubtitle ?? .systemFont(ofSize: 12))) + .foregroundColor((Color(red: 1, green: 248/255, blue: 245/255))) + ) + }.padding(.bottom) + Image(uiImage: UIImage(data: getQRCodeDate(text: qrInfo)!)!) + .resizable() + .frame(width: isIpad ? 371*padFactor : 221*phoneFactor, height: isIpad ? 371*padFactor : 221*phoneFactor) + .padding(.bottom, 20*phoneFactor) + } + .padding(.top, isIpad ? 50*padFactor : 0) + } + .padding(.top, 24) + } + .preferredColorScheme(.dark) + .onAppear { + startFetchingQR = true + QRFetchLoop() + } + .onDisappear { + startFetchingQR = false + } + } + + func formatName() -> String { + if displayName.count > 20 { + let names = displayName.split(separator: " ") + if names.count >= 2 { + let firstName = String(names[0]) + let lastName = String(names[1]) + let abbreviatedName = firstName + " " + String(lastName.prefix(1)) + "." + return abbreviatedName + } else { + return displayName + } + } else { + return displayName + } + } + + func getQRCodeDate(text: String) -> Data? { + guard let filter = CIFilter(name: "CIQRCodeGenerator") else { return nil } + let data = text.data(using: .ascii, allowLossyConversion: false) + filter.setValue(data, forKey: "inputMessage") + + // Change color of QR code + guard let colorFilter = CIFilter(name: "CIFalseColor") else { return nil } + colorFilter.setValue(filter.outputImage, forKey: "inputImage") + colorFilter.setValue(CIColor(red: 1, green: 248/255, blue: 245/255), forKey: "inputColor1") // Background off-white + colorFilter.setValue(CIColor(red: 102/255, green: 43/255, blue: 19/255), forKey: "inputColor0") // Barcode brown + + guard let ciimage = colorFilter.outputImage else { return nil } + let transform = CGAffineTransform(scaleX: 10, y: 10) + let scaledCIImage = ciimage.transformed(by: transform) + let uiimage = UIImage(ciImage: scaledCIImage) + return uiimage.pngData()! + } + + func QRFetchLoop() { + if startFetchingQR { + getQRInfo() + DispatchQueue.main.asyncAfter(deadline: .now() + 15) { + QRFetchLoop() + } + } + } + + func getQRInfo() { + guard let user = HIApplicationStateController.shared.user else { return } + HIAPI.UserService.getQR(userToken: user.token) + .onCompletion { result in + do { + let (qr, _) = try result.get() + DispatchQueue.main.async { + self.qrInfo = qr.qrInfo + } + } catch { + print("An error has occurred \(error)") + } + } + .authorize(with: user) + .launch() + } + + func getRank(completion: @escaping (Int) -> Void) { + guard let user = HIApplicationStateController.shared.user else { return } + + var rank = 0 + HIAPI.ProfileService.getUserRanking(userToken: user.token) + .onCompletion { result in + do { + let (userRanking, _) = try result.get() + rank = userRanking.ranking + completion(rank) + } catch { + print("An error has occurred in ranking \(error)") + } + } + .authorize(with: user) + .launch() + } + +} + +struct HIProfileCardView_Previews: PreviewProvider { + static var previews: some View { + HIProfileCardView(displayName: "first last", + points: 100, + tier: "Pro", + foodWave: 1, + avatarUrl: "https://raw.githubusercontent.com/HackIllinois/adonix-metadata/main/avatars/fishercat.png", userId: "https://www.hackillinois.org", role: "Pro" + ) + } +} diff --git a/HackIllinois/ViewControllers/HIProfileViewController.swift b/HackIllinois/ViewControllers/HIProfileViewController.swift index 9fd42985..1045158d 100644 --- a/HackIllinois/ViewControllers/HIProfileViewController.swift +++ b/HackIllinois/ViewControllers/HIProfileViewController.swift @@ -36,7 +36,7 @@ class HIProfileViewController: HIBaseViewController { @objc dynamic override func setUpBackgroundView() { super.setUpBackgroundView() - backgroundView.image = #imageLiteral(resourceName: "PurpleBackground") + backgroundView.image = #imageLiteral(resourceName: "ScheduleBackground") } private var tiers: [Tier] = [] private var ranking: Int = 0 @@ -55,7 +55,11 @@ extension HIProfileViewController { override func loadView() { super.loadView() - guard let user = HIApplicationStateController.shared.user else { return } + guard let user = HIApplicationStateController.shared.user else { + print("DEBUG: User is nil - Can't fetch profile") + return + } + print("DEBUG: User Token \(user.token)") if HIApplicationStateController.shared.isGuest || user.roles.contains(.STAFF) { layoutErrorView() } else { @@ -92,7 +96,7 @@ extension HIProfileViewController { guard let user = HIApplicationStateController.shared.user else { return } var role = "General" if user.roles.contains(.PRO) { - role = "Knight" + role = "Olympian" } profileCardController = UIHostingController(rootView: HIProfileCardView(displayName: profile.displayName, points: profile.points, @@ -179,7 +183,9 @@ extension HIProfileViewController { HIAPI.ProfileService.getUserProfile(userToken: user.token) .onCompletion { [weak self] result in do { + let (apiProfile, _) = try result.get() + print("DEBUG: Full Profile Response - \(apiProfile)") print(apiProfile) self?.profile.userId = apiProfile.userId self?.profile.displayName = apiProfile.displayName