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
31 43
32 - profileImage.image = UIImage(named: "profile_pic_default", in: Bundle.frameworkResourceBundle, compatibleWith: nil) 44 + selectionStyle = .none
33 45
34 - profileButton.addTarget(self, action: #selector(profileButtonTapped), for: .touchUpInside) 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"
35 51
36 - tagView1.backgroundColor = UIColor(rgb: 0x09914E) 52 + // Gear button: render image as-is (no tint) so the gray circle style is preserved
37 - tagView1.layer.cornerRadius = 4.0 53 + let gearImage = UIImage(named: "settings_circle", in: Bundle.frameworkResourceBundle, compatibleWith: nil)?
38 - tagLabel1.font = UIFont(name: "PingLCG-Regular", size: 17) 54 + .withRenderingMode(.alwaysOriginal)
39 - tagLabel1.textColor = UIColor(rgb: 0xFFFFFF) 55 + profileButton.setImage(gearImage, for: .normal)
56 + profileButton.setTitle("", for: .normal)
57 + profileButton.addTarget(self, action: #selector(profileButtonTapped), for: .touchUpInside)
40 58
41 - tagView2.backgroundColor = UIColor(rgb: 0xFC9F25) 59 + // Tags scroll view
42 - tagView2.layer.cornerRadius = 4.0 60 + tagsScrollView.showsHorizontalScrollIndicator = false
43 - tagLabel2.font = UIFont(name: "PingLCG-Regular", size: 17) 61 + tagsScrollView.showsVerticalScrollIndicator = false
44 - tagLabel2.textColor = UIColor(rgb: 0xFFFFFF)
45 62
63 + // Tags stack view
64 + tagsStackView.axis = .horizontal
65 + tagsStackView.spacing = 9
66 + tagsStackView.alignment = .center
67 + tagsStackView.distribution = .fill
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
109 + clearTags()
110 + }
105 111
106 - // Keep tags visible even in default state 112 + // MARK: - Tag Helpers
107 - tagView1.isHidden = false
108 - tagView2.isHidden = false
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() }
111 } 117 }
112 118
113 - private func loadProfileImage(from urlString: String) { 119 + /// Creates one pill view per segment string and adds them to the stack view.
114 - // For now, use default image - can be enhanced later with URL loading 120 + private func populateTags(segments: [String]) {
115 - profileImage.image = UIImage(named: "profile_pic_default", in: Bundle.frameworkResourceBundle, compatibleWith: nil) 121 + clearTags()
122 +
123 + for segment in segments where !segment.isEmpty {
124 + let pill = makePillView(text: segment)
125 + tagsStackView.addArrangedSubview(pill)
126 + }
127 + }
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 }
......