Manos Chorianopoulos

MyRewardsProfileInfoTableViewCell redesign

...@@ -13,107 +13,144 @@ protocol MyRewardsProfileInfoTableViewCellDelegate: AnyObject { ...@@ -13,107 +13,144 @@ protocol MyRewardsProfileInfoTableViewCellDelegate: AnyObject {
13 13
14 @objc(MyRewardsProfileInfoTableViewCell) 14 @objc(MyRewardsProfileInfoTableViewCell)
15 public class MyRewardsProfileInfoTableViewCell: UITableViewCell { 15 public class MyRewardsProfileInfoTableViewCell: UITableViewCell {
16 - @IBOutlet weak var tagView1: UIView! 16 +
17 - @IBOutlet weak var tagLabel1: UILabel! 17 + // MARK: - IBOutlets
18 - @IBOutlet weak var tagView2: UIView! 18 +
19 - @IBOutlet weak var tagLabel2: UILabel! 19 + /// Bold "Rewards" title label (top-left)
20 - @IBOutlet weak var profileImage: UIImageView! 20 + @IBOutlet weak var titleLabel: UILabel!
21 +
22 + /// Horizontally scrollable container for tag pills
23 + @IBOutlet weak var tagsScrollView: UIScrollView!
24 +
25 + /// Horizontal stack view inside the scroll view; tag pill views are added here dynamically
26 + @IBOutlet weak var tagsStackView: UIStackView!
27 +
28 + /// Settings / gear button (top-right)
21 @IBOutlet weak var profileButton: UIButton! 29 @IBOutlet weak var profileButton: UIButton!
22 - 30 +
31 + // MARK: - Delegate
32 +
23 weak var delegate: MyRewardsProfileInfoTableViewCellDelegate? 33 weak var delegate: MyRewardsProfileInfoTableViewCellDelegate?
24 - 34 +
25 - // MARK: - Properties 35 + // MARK: - Private
36 +
26 private var profileModel: ProfileModel? 37 private var profileModel: ProfileModel?
27 38
39 + // MARK: - Lifecycle
40 +
28 public override func awakeFromNib() { 41 public override func awakeFromNib() {
29 super.awakeFromNib() 42 super.awakeFromNib()
30 - // Initialization code 43 +
31 - 44 + selectionStyle = .none
32 - profileImage.image = UIImage(named: "profile_pic_default", in: Bundle.frameworkResourceBundle, compatibleWith: nil) 45 +
33 - 46 + // Title
47 + titleLabel.font = UIFont(name: "PingLCG-Bold", size: 26)
48 + ?? UIFont.boldSystemFont(ofSize: 26)
49 + titleLabel.textColor = UIColor(rgb: 0x1D2023)
50 + titleLabel.text = "Rewards"
51 +
52 + // Gear button: render image as-is (no tint) so the gray circle style is preserved
53 + let gearImage = UIImage(named: "settings_circle", in: Bundle.frameworkResourceBundle, compatibleWith: nil)?
54 + .withRenderingMode(.alwaysOriginal)
55 + profileButton.setImage(gearImage, for: .normal)
56 + profileButton.setTitle("", for: .normal)
34 profileButton.addTarget(self, action: #selector(profileButtonTapped), for: .touchUpInside) 57 profileButton.addTarget(self, action: #selector(profileButtonTapped), for: .touchUpInside)
35 - 58 +
36 - tagView1.backgroundColor = UIColor(rgb: 0x09914E) 59 + // Tags scroll view
37 - tagView1.layer.cornerRadius = 4.0 60 + tagsScrollView.showsHorizontalScrollIndicator = false
38 - tagLabel1.font = UIFont(name: "PingLCG-Regular", size: 17) 61 + tagsScrollView.showsVerticalScrollIndicator = false
39 - tagLabel1.textColor = UIColor(rgb: 0xFFFFFF) 62 +
40 - 63 + // Tags stack view
41 - tagView2.backgroundColor = UIColor(rgb: 0xFC9F25) 64 + tagsStackView.axis = .horizontal
42 - tagView2.layer.cornerRadius = 4.0 65 + tagsStackView.spacing = 9
43 - tagLabel2.font = UIFont(name: "PingLCG-Regular", size: 17) 66 + tagsStackView.alignment = .center
44 - tagLabel2.textColor = UIColor(rgb: 0xFFFFFF) 67 + tagsStackView.distribution = .fill
45 -
46 } 68 }
47 69
48 public override func setSelected(_ selected: Bool, animated: Bool) { 70 public override func setSelected(_ selected: Bool, animated: Bool) {
49 super.setSelected(selected, animated: animated) 71 super.setSelected(selected, animated: animated)
72 + }
50 73
51 - // Configure the view for the selected state 74 + public override func prepareForReuse() {
75 + super.prepareForReuse()
76 + clearTags()
52 } 77 }
53 - 78 +
79 + // MARK: - Actions
80 +
54 @objc private func profileButtonTapped() { 81 @objc private func profileButtonTapped() {
55 delegate?.didTapProfileButton() 82 delegate?.didTapProfileButton()
56 } 83 }
57 - 84 +
58 - // MARK: - Configuration Methods 85 + // MARK: - Configuration
59 - 86 +
60 public func configureCell(data: SectionModel?) { 87 public func configureCell(data: SectionModel?) {
61 guard let data = data, 88 guard let data = data,
62 - let itemType = data.itemType, 89 + data.sectionType == .myRewardsProfileInfo else {
63 - itemType == .profile else {
64 configureDefaultState() 90 configureDefaultState()
65 return 91 return
66 } 92 }
67 - 93 +
68 - // Check if we have real profile data
69 if let metadata = data.metadata, 94 if let metadata = data.metadata,
70 let profile = metadata["profile"] as? ProfileModel { 95 let profile = metadata["profile"] as? ProfileModel {
71 - // Configure with real profile data
72 configureCell(profile: profile) 96 configureCell(profile: profile)
73 } else { 97 } else {
74 - // Configure with default state (no profile data)
75 configureDefaultState() 98 configureDefaultState()
76 } 99 }
77 } 100 }
78 - 101 +
79 public func configureCell(profile: ProfileModel) { 102 public func configureCell(profile: ProfileModel) {
80 self.profileModel = profile 103 self.profileModel = profile
81 - 104 + populateTags(segments: profile._user_segments)
82 - // TODO: Configure tag labels with profile data
83 - // We need to determine what data should populate tagView1 and tagView2
84 - // Possible options from ProfileModel:
85 - // - _user_points (loyalty points)
86 - // - _badge (user badge/tier)
87 - // - _verified (verification status)
88 - // - Custom business logic based on profile data
89 -
90 - // For now, keep tags visible with current static styling
91 - tagView1.isHidden = false
92 - tagView2.isHidden = false
93 -
94 - // Load profile image if available
95 - if !profile._image_url.isEmpty {
96 - loadProfileImage(from: profile._image_url)
97 - } else {
98 - profileImage.image = UIImage(named: "profile_pic_default", in: Bundle.frameworkResourceBundle, compatibleWith: nil)
99 - }
100 } 105 }
101 - 106 +
102 private func configureDefaultState() { 107 private func configureDefaultState() {
103 - // Default state: no profile data available
104 self.profileModel = nil 108 self.profileModel = nil
105 - 109 + clearTags()
106 - // Keep tags visible even in default state 110 + }
107 - tagView1.isHidden = false 111 +
108 - tagView2.isHidden = false 112 + // MARK: - Tag Helpers
109 - 113 +
110 - profileImage.image = UIImage(named: "profile_pic_default", in: Bundle.frameworkResourceBundle, compatibleWith: nil) 114 + /// Removes all tag pill views from the stack view.
115 + private func clearTags() {
116 + tagsStackView.arrangedSubviews.forEach { $0.removeFromSuperview() }
117 + }
118 +
119 + /// Creates one pill view per segment string and adds them to the stack view.
120 + private func populateTags(segments: [String]) {
121 + clearTags()
122 +
123 + for segment in segments where !segment.isEmpty {
124 + let pill = makePillView(text: segment)
125 + tagsStackView.addArrangedSubview(pill)
126 + }
111 } 127 }
112 -
113 - private func loadProfileImage(from urlString: String) {
114 - // For now, use default image - can be enhanced later with URL loading
115 - profileImage.image = UIImage(named: "profile_pic_default", in: Bundle.frameworkResourceBundle, compatibleWith: nil)
116 128
117 - // TODO: Add dynamic profile picture and tags 129 + /// Builds a rounded pill view with the given text.
130 + private func makePillView(text: String) -> UIView {
131 + let container = UIView()
132 + container.backgroundColor = UIColor(rgb: 0xF1F2F4)
133 + container.layer.cornerRadius = 13
134 + container.clipsToBounds = true
135 +
136 + let label = UILabel()
137 + label.text = text
138 + label.font = UIFont(name: "PingLCG-Regular", size: 12)
139 + ?? UIFont.systemFont(ofSize: 12)
140 + label.textColor = UIColor(rgb: 0x00111B)
141 + label.numberOfLines = 1
142 + label.translatesAutoresizingMaskIntoConstraints = false
143 +
144 + container.addSubview(label)
145 + container.translatesAutoresizingMaskIntoConstraints = false
146 +
147 + NSLayoutConstraint.activate([
148 + label.topAnchor.constraint(equalTo: container.topAnchor, constant: 6),
149 + label.bottomAnchor.constraint(equalTo: container.bottomAnchor, constant: -6),
150 + label.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 8),
151 + label.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: -8)
152 + ])
153 +
154 + return container
118 } 155 }
119 } 156 }
......