Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ public final class MainViewController: BaseViewController, UICollectionViewDataS
private func showProfileOverlay() {
if profileVC != nil { return }

let vc = UserProfileViewController()
// ν˜„μž¬ μΊμ‹œλœ ν”„λ‘œν•„ 정보λ₯Ό λ„˜κ²¨μ€˜μ„œ λ·°κ°€ 열릴 λ•Œ κΈ°λ³Έ μ΄λ―Έμ§€λ‘œ κΉœλΉ‘μ΄λŠ” ν˜„μƒ λ°©μ§€
let vc = UserProfileViewController(initialProfile: profileViewModel.profileInfo)
profileVC = vc

addChild(vc)
Expand All @@ -180,6 +181,8 @@ public final class MainViewController: BaseViewController, UICollectionViewDataS
profile.view.removeFromSuperview()
profile.removeFromParent()
profileVC = nil

fetchData()
}

func selectHomeTab() {
Expand Down Expand Up @@ -415,7 +418,7 @@ public final class MainViewController: BaseViewController, UICollectionViewDataS
}

group.enter()
mainViewModel.getProfile { result in
mainViewModel.getProfile { _ in
group.leave()
}

Expand All @@ -424,12 +427,17 @@ public final class MainViewController: BaseViewController, UICollectionViewDataS
group.leave()
}

group.enter()
profileViewModel.loadProfileInfo { _, _ in
group.leave()
}
Comment thread
minsun-0209 marked this conversation as resolved.

group.notify(queue: .main) { [weak self] in
guard let self = self, self.isVisible else {
self?.refreshControl.endRefreshing()
return
}

self.setupViewComponents()
self.refreshControl.endRefreshing()
}
Expand Down Expand Up @@ -466,8 +474,12 @@ public final class MainViewController: BaseViewController, UICollectionViewDataS

if let urlString = profileViewModel.profileInfo?.profileImageUrl,
let url = URL(string: urlString) {
basicsProfileView.profileImageView.kf.setImage(with: url, placeholder: UIImage.image.profile.image, options: [.forceRefresh])
profileView.profileImageView.kf.setImage(with: url, placeholder: UIImage.image.profile.image, options: [.forceRefresh])
let options: KingfisherOptionsInfo = [
.keepCurrentImageWhileLoading,
.transition(.none)
]
basicsProfileView.profileImageView.kf.setImage(with: url, placeholder: UIImage.image.profile.image, options: options)
profileView.profileImageView.kf.setImage(with: url, placeholder: UIImage.image.profile.image, options: options)
} else {
basicsProfileView.profileImageView.image = .image.profile.image
profileView.profileImageView.image = .image.profile.image
Expand Down Expand Up @@ -574,7 +586,7 @@ public final class MainViewController: BaseViewController, UICollectionViewDataS
}

logo.snp.makeConstraints {
$0.leading.equalToSuperview().inset(20)
$0.leading.equalTo(view.safeAreaLayoutGuide.snp.leading).offset(6)
$0.top.equalTo(contentView.snp.top)
$0.height.equalTo(56)
$0.width.equalTo(135)
Expand Down Expand Up @@ -639,12 +651,6 @@ public final class MainViewController: BaseViewController, UICollectionViewDataS
$0.height.equalTo(84)
}

latecomerLabel.snp.remakeConstraints {
$0.top.equalTo(profileView.snp.bottom).offset(24)
$0.leading.equalToSuperview().inset(20)
$0.height.equalTo(32)
}

basicsProfileView.snp.remakeConstraints {
$0.leading.trailing.equalToSuperview().inset(20)
$0.height.equalTo(84)
Expand All @@ -654,9 +660,21 @@ public final class MainViewController: BaseViewController, UICollectionViewDataS
if isClockOn {
profileView.isHidden = false
basicsProfileView.isHidden = true

latecomerLabel.snp.remakeConstraints {
$0.top.equalTo(profileView.snp.bottom).offset(24)
$0.leading.equalToSuperview().inset(20)
$0.height.equalTo(32)
}
} else {
profileView.isHidden = true
basicsProfileView.isHidden = false

latecomerLabel.snp.remakeConstraints {
$0.top.equalTo(basicsProfileView.snp.bottom).offset(24)
$0.leading.equalToSuperview().inset(20)
$0.height.equalTo(32)
}
}
Comment thread
minsun-0209 marked this conversation as resolved.
view.layoutIfNeeded()
}
Expand Down
27 changes: 14 additions & 13 deletions Projects/Feature/Sources/Scene/Main/View/LateNilView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ final class LateNilView: UIView {
$0.font = .suit(size: 16, weight: .semibold)
}

private let stackView = UIStackView().then {
$0.axis = .horizontal
$0.alignment = .center
$0.distribution = .fill
$0.spacing = 6
}

// MARK: - Initializer
override init(frame: CGRect) {
super.init(frame: frame)
Expand All @@ -42,25 +49,19 @@ final class LateNilView: UIView {
fatalError("init(coder:) has not been implemented")
}


private func addView() {
let stack = UIStackView(arrangedSubviews: [icon, mainLabel])
stack.axis = .horizontal
stack.alignment = .center
stack.spacing = 6
self.addSubview(stack)
[icon, mainLabel].forEach { stackView.addArrangedSubview($0) }
self.addSubview(stackView)
}

stack.snp.makeConstraints {
$0.centerX.equalToSuperview()
$0.centerY.equalToSuperview().offset(10)
private func setLayout() {
stackView.snp.makeConstraints {
$0.centerX.centerY.equalToSuperview()
$0.top.bottom.equalToSuperview()
}

icon.snp.makeConstraints {
$0.width.height.equalTo(24)
}
}

private func setLayout() {
// Layout handled inside addView() with stack constraints
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ final class ProfileCardView: UIView {
profileImageView.kf.setImage(
with: url,
placeholder: UIImage.image.gomsBasicProfile.image,
options: [.transition(.fade(0.2))]
options: [.keepCurrentImageWhileLoading, .transition(.fade(0.2))]
)
} else {
profileImageView.image = .image.gomsBasicProfile.image
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Service
import Moya
import Combine
import UIKit
import Kingfisher

public struct MyRoleResponse: Decodable {
public let memberId: Int
Expand Down Expand Up @@ -165,6 +166,14 @@ public final class ProfileViewModel: BaseViewModel, ObservableObject {
case 200:
do {
let data = try JSONDecoder().decode(ProfileImageResponse.self, from: response.data)
// λ³€κ²½λœ 이미지 URL의 μΊμ‹œλ₯Ό μ œκ±°ν•΄μ„œ λ‹€μŒ λ‘œλ“œ μ‹œ μ΅œμ‹  이미지λ₯Ό λ°›μ•„μ˜€λ„λ‘ 처리
if let oldUrlString = self.profileInfo?.profileImageUrl,
let oldUrl = URL(string: oldUrlString) {
KingfisherManager.shared.cache.removeImage(forKey: oldUrl.absoluteString)
}
if !data.imageUrl.isEmpty, let newUrl = URL(string: data.imageUrl) {
KingfisherManager.shared.cache.removeImage(forKey: newUrl.absoluteString)
}
self.profileInfo = ProfileResponse(
name: self.profileInfo?.name ?? "",
grade: self.profileInfo?.grade ?? 0,
Expand Down Expand Up @@ -207,6 +216,11 @@ public final class ProfileViewModel: BaseViewModel, ObservableObject {
case .success(let response):
switch response.statusCode {
case 204:
// κΈ°λ³Έ ν”„λ‘œν•„λ‘œ λ³€κ²½ μ‹œ κΈ°μ‘΄ 이미지 μΊμ‹œ 제거
if let oldUrlString = self.profileInfo?.profileImageUrl,
let oldUrl = URL(string: oldUrlString) {
KingfisherManager.shared.cache.removeImage(forKey: oldUrl.absoluteString)
}
self.profileInfo = ProfileResponse(
name: self.profileInfo?.name ?? "",
grade: self.profileInfo?.grade ?? 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public class UserProfileViewController: BaseViewController, UIImagePickerControl
$0.layer.cornerRadius = 32
$0.clipsToBounds = true
$0.translatesAutoresizingMaskIntoConstraints = false
$0.image = UIImage.image.gomsBasicProfile.image // 졜초 κΈ°λ³Έ 이미지 μ§€μ •μœΌλ‘œ κΉœλΉ‘μž„ λ°©μ§€
}

let userProfilePencil = UIButton().then {
Expand All @@ -47,15 +48,19 @@ public class UserProfileViewController: BaseViewController, UIImagePickerControl
}

let userName = UILabel().then {
$0.text = "κΉ€μ€€ν‘œ"
$0.text = " "
$0.textColor = .color.mainText.color
$0.font = .suit(size: 18, weight: .bold)
$0.adjustsFontSizeToFitWidth = true
$0.minimumScaleFactor = 0.7
}

let userGradeDepartment = UILabel().then {
$0.text = "9κΈ° | IoT"
$0.text = " "
$0.textColor = .color.sub2.color
$0.font = .suit(size: 14, weight: .medium)
$0.adjustsFontSizeToFitWidth = true
$0.minimumScaleFactor = 0.7
}

let perceptionCount = UILabel().then {
Expand Down Expand Up @@ -186,6 +191,17 @@ public class UserProfileViewController: BaseViewController, UIImagePickerControl
$0.backgroundColor = .color.gomsDivider.color
}

public init(initialProfile: ProfileResponse? = nil) {
Comment thread
minsun-0209 marked this conversation as resolved.
super.init(nibName: nil, bundle: nil)
if let initialProfile = initialProfile {
self.profileViewModel.profileInfo = initialProfile
}
}

required init?(coder: NSCoder) {
super.init(coder: coder)
}

@objc func withdrawalButtonTapped() {
GOMSAlert.show(
in: self,
Expand Down Expand Up @@ -350,7 +366,7 @@ public class UserProfileViewController: BaseViewController, UIImagePickerControl
setNeedsStatusBarAppearanceUpdate()
}

@IBAction func ShowActionSheetProfilImageChange(_ sender: UIButton) {
@objc func ShowActionSheetProfilImageChange(_ sender: UIButton) {
updateImage(isActionSheetShowing: true)
let actionSheet = UIAlertController(title: "ν”„λ‘œν•„ 사진 선택", message: nil, preferredStyle: .actionSheet)

Expand Down Expand Up @@ -399,9 +415,16 @@ public class UserProfileViewController: BaseViewController, UIImagePickerControl
if let selectedImage = info[.originalImage] as? UIImage {
userProfile.image = selectedImage
if let jpegData = selectedImage.jpegData(compressionQuality: 0.5) {
// MARK: - 톡신 트리거 및 데이터 μƒˆλ‘œκ³ μΉ¨ 동기화
profileViewModel.updateProfileImage(imageData: jpegData)
.sink { completion in
if case .failure(let error) = completion { print("이미지 PATCH μ‹€νŒ¨: \(error)") }
.sink { [weak self] completion in
switch completion {
case .finished:
// μ—…λ‘œλ“œ 성곡 ν›„ μœ μ € ν”„λ‘œν•„ 정보λ₯Ό μ΅œμ‹  μƒνƒœλ‘œ μƒˆλ‘œκ³ μΉ¨
self?.profileViewModel.loadProfileInfo { _, _ in }
case .failure(let error):
print("이미지 PATCH μ‹€νŒ¨: \(error)")
}
} receiveValue: { [weak self] response in
if let url = URL(string: response.imageUrl) {
self?.userProfile.kf.setImage(with: url)
Expand All @@ -420,41 +443,57 @@ public class UserProfileViewController: BaseViewController, UIImagePickerControl
super.viewDidLoad()
applySavedTheme()
self.navigationController?.navigationBar.prefersLargeTitles = false

// ꡬ독 등둝 전에 이미 profileInfoκ°€ 있으면 이미지λ₯Ό μ¦‰μ‹œ μ„ΈνŒ…ν•΄μ„œ κΉœλΉ‘μž„ λ°©μ§€
if let initialProfile = profileViewModel.profileInfo {
if let urlString = initialProfile.profileImageUrl, let url = URL(string: urlString) {
userProfile.kf.setImage(with: url, placeholder: UIImage.image.gomsBasicProfile.image, options: [.keepCurrentImageWhileLoading])
} else {
userProfile.image = UIImage.image.gomsBasicProfile.image
}
}

profileViewModel.loadProfileInfo { _, _ in }

// ν† κΈ€ λ²„νŠΌλ“€μ˜ 초기 κ°’ ν• λ‹΅ (졜초 1회만 κ³ μ • μ‹€ν–‰λ˜λ„λ‘ 격리 μœ μ§€)
alarmsettingButton.isOn = UserDefaults.standard.bool(forKey: "isAlarmOn")
cameraNowOntoggleButton.isOn = UserDefaults.standard.bool(forKey: "isCameraOn")
clockToggleButton.isOn = UserDefaults.standard.bool(forKey: "isClockOn")

profileViewModel.$profileInfo.sink { [weak self] profileInfo in
guard let profileInfo = profileInfo else { return }
DispatchQueue.main.async {
self?.userName.text = profileInfo.name
self?.perceptionNum.text = String(describing: profileInfo.lateCount)

let majorText: String
switch profileInfo.department {
case Major.sw.rawValue:
majorText = "SW"
case Major.iot.rawValue:
majorText = "IoT"
default:
majorText = "AI"
}

self?.userGradeDepartment.text = "\(profileInfo.grade)κΈ° | \(majorText)"
if let urlString = profileInfo.profileImageUrl, let url = URL(string: urlString) {
self?.userProfile.kf.setImage(
with: url,
placeholder: self?.userProfile.image,
options: [.transition(.fade(0.2)), .keepCurrentImageWhileLoading]
)
} else {
self?.userProfile.image = UIImage.image.gomsBasicProfile.image
profileViewModel.$profileInfo
.compactMap { $0 } // nil λ°μ΄ν„°λŠ” μŠ€ν‚΅ν•˜μ—¬ λΆˆν•„μš”ν•œ λ ˆμ΄μ•„μ›ƒ κ°±μ‹  및 κΉœλΉ‘μž„ 차단
.sink { [weak self] profileInfo in
DispatchQueue.main.async {
// μ• λ‹ˆλ©”μ΄μ…˜ 없이 λ°μ΄ν„°λ§Œ λ§€λ„λŸ½κ²Œ λ³€κ²½ν•˜λ„λ‘ μ²˜λ¦¬ν•˜μ—¬ κΉœλΉ‘μž„ 제거
UIView.performWithoutAnimation {
self?.userName.text = profileInfo.name
self?.perceptionNum.text = String(describing: profileInfo.lateCount)

let majorText: String
switch profileInfo.department {
case Major.sw.rawValue:
majorText = "SW"
case Major.iot.rawValue:
majorText = "IoT"
default:
majorText = "AI"
}

self?.userGradeDepartment.text = "\(profileInfo.grade)κΈ° | \(majorText)"

if let urlString = profileInfo.profileImageUrl, let url = URL(string: urlString) {
self?.userProfile.kf.setImage(
with: url,
placeholder: self?.userProfile.image,
options: [.transition(.none), .keepCurrentImageWhileLoading] // transition λ¬΄νš¨ν™”λ‘œ λ‘œλ”© μ‹œ κΉœλΉ‘μž„ 차단
)
} else {
self?.userProfile.image = UIImage.image.gomsBasicProfile.image
}
}
}
}
}
.store(in: &cancellables)
.store(in: &cancellables)

view.backgroundColor = .color.background.color
imagePickerController.delegate = self
Expand Down Expand Up @@ -536,21 +575,20 @@ public class UserProfileViewController: BaseViewController, UIImagePickerControl
$0.top.equalTo(cameraNowOnDescription.snp.bottom).offset(24)
}
themeChangText.snp.makeConstraints {
$0.width.equalTo(93)
$0.height.equalTo(28)
$0.top.equalTo(themeTopLine.snp.top).offset(24)
$0.leading.equalToSuperview().inset(28)
$0.top.equalTo(themeTopLine.snp.top).offset(24)
$0.trailing.lessThanOrEqualTo(view.snp.trailing).offset(-28)
}
themeChangRec.snp.makeConstraints {
$0.height.equalTo(64)
$0.leading.trailing.equalToSuperview().inset(20)
$0.top.equalTo(themeChangText.snp.bottom).offset(8)
}
themeSettingText.snp.makeConstraints {
$0.width.equalTo(106)
$0.height.equalTo(28)
$0.top.equalTo(themeChangRec.snp.top).offset(18)
$0.leading.equalTo(themeChangRec.snp.leading).offset(12)
$0.trailing.lessThanOrEqualTo(themeSettingImg.snp.leading).offset(-8)
}
themeSettingImg.snp.makeConstraints {
$0.width.equalTo(24)
Expand Down Expand Up @@ -585,10 +623,9 @@ public class UserProfileViewController: BaseViewController, UIImagePickerControl
$0.centerY.equalTo(alarmText)
}
cameraNowOnText.snp.makeConstraints {
$0.width.equalTo(184)
$0.height.equalTo(28)
$0.leading.equalToSuperview().inset(28)
$0.top.equalTo(alarmDescription.snp.bottom).offset(25.5)
$0.trailing.lessThanOrEqualTo(cameraNowOntoggleButton.snp.leading).offset(-8)
}
cameraNowOnDescription.snp.makeConstraints {
$0.leading.equalToSuperview().inset(28)
Expand Down
Loading