| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513 |
- //
- // LNEditProfileViewController.swift
- // Lanu
- //
- // Created by OneeChan on 2025/11/26.
- //
- import Foundation
- import UIKit
- import SnapKit
- extension UIView {
- func pushToEditProfile() {
- let vc = LNEditProfileViewController()
- navigationController?.pushViewController(vc, animated: true)
- }
- }
- class LNEditProfileViewController: LNViewController {
- private let saveButton = UIButton()
-
- private let profilePhoto = LNImageUploadView()
- private var curProfilePhoto = "" {
- didSet {
- profilePhoto.loadImage(url: curProfilePhoto)
- }
- }
-
- private let photoWall = LNEditProfilePhotoWallView()
-
- private let nameLabel = UILabel()
- private var curName = "" {
- didSet {
- nameLabel.text = curName
- }
- }
-
- private let genderLabel = UILabel()
- private var curGender: LNUserGender = .unknow {
- didSet {
- genderLabel.text = curGender.desc
- }
- }
-
- private let birthdayLabel = UILabel()
- private var curBirthday: Double = 0 {
- didSet {
- birthdayLabel.text = curBirthday.formattedFullDate("-")
- }
- }
-
- private let bioLabel = UILabel()
- private var curBio = "" {
- didSet {
- if curBio.isEmpty {
- bioLabel.text = nil
- bioLabel.attributedText = .init(string: .init(key: "A00200"), attributes: [.foregroundColor: UIColor.text_6])
- } else {
- bioLabel.attributedText = nil
- bioLabel.text = curBio
- }
- }
- }
-
- private let interestLabel = UILabel()
- private var curInterest: [LNUserInterestVO] = [] {
- didSet {
- if curInterest.isEmpty {
- interestLabel.text = nil
- interestLabel.attributedText = .init(string: .init(key: "A00201"), attributes: [.foregroundColor: UIColor.text_6])
- } else {
- interestLabel.attributedText = nil
- interestLabel.text = curInterest.map({ $0.name }).joined(separator: ", ")
- }
- }
- }
- private let voiceLabel = UILabel()
-
- override func viewDidLoad() {
- super.viewDidLoad()
-
- setupViews()
-
- loadUserInfo()
-
- checkSaveButton()
-
- LNEventDeliver.addObserver(self)
- }
- }
- extension LNEditProfileViewController {
- private func loadUserInfo() {
- curBio = myUserInfo.intro
- curInterest = myUserInfo.interests
- curProfilePhoto = myUserInfo.avatar
- curName = myUserInfo.nickname
- curGender = myUserInfo.gender
- curBirthday = Double(myUserInfo.birthday / 1_000)
-
- updateVoiceText()
- }
- }
- extension LNEditProfileViewController: LNProfileManagerNotify {
- func onUserVoiceBarInfoChanged() {
- updateVoiceText()
- }
-
- func onUserInfoChanged(userInfo: LNUserProfileVO) {
- guard userInfo.userNo.isMyUid else { return }
-
- updateVoiceText()
- }
-
- private func updateVoiceText() {
- if myVoiceBarInfo.status == .done {
- voiceLabel.attributedText = nil
- voiceLabel.text = .init(key: "B00002")
- } else if myVoiceBarInfo.status == .review {
- voiceLabel.attributedText = nil
- voiceLabel.text = .init(key: "B00003")
- } else if myUserInfo.voiceBar.isEmpty {
- voiceLabel.text = nil
- voiceLabel.attributedText = .init(string: .init(key: "B00001"), attributes: [.foregroundColor: UIColor.text_6])
- }
- }
- }
- extension LNEditProfileViewController: LNImageUploadViewDelegate {
- func onImageUploadView(view: LNImageUploadView, didUploadImage url: String) {
- checkSaveButton()
- }
-
- func onImageUploadViewDidClickDelete(view: LNImageUploadView) {
- checkSaveButton()
- }
- }
- extension LNEditProfileViewController: LNEditProfilePhotoWallViewDelegate {
- func onEditProfilePhotoWallViewDidChanged(view: LNEditProfilePhotoWallView) {
- checkSaveButton()
- }
- }
- extension LNEditProfileViewController {
- private func checkSaveButton() {
- let enable = profilePhoto.imageUrl != myUserInfo.avatar
- || photoWall.curPhotos != myUserInfo.photos
- || curName != myUserInfo.nickname
- || curGender != myUserInfo.gender
- || curBirthday != Double(myUserInfo.birthday / 1_000)
- || curBio != myUserInfo.intro
- || curInterest.map({ $0.code }) != myUserInfo.interests.map({ $0.code })
-
- if enable, !saveButton.isEnabled {
- saveButton.isEnabled = true
- saveButton.setBackgroundImage(.primary_8, for: .normal)
- } else if !enable, saveButton.isEnabled {
- saveButton.isEnabled = false
- saveButton.setBackgroundImage(nil, for: .normal)
- }
- }
-
- private func setupViews() {
- setupNavBar()
-
- let scrollView = UIScrollView()
- scrollView.showsVerticalScrollIndicator = false
- scrollView.showsHorizontalScrollIndicator = false
- scrollView.clipsToBounds = false
- scrollView.contentInset = .init(top: 0, left: 0, bottom: -view.commonBottomInset, right: 0)
- view.addSubview(scrollView)
- scrollView.snp.makeConstraints { make in
- make.top.equalToSuperview()
- make.horizontalEdges.equalToSuperview()
- make.bottom.equalToSuperview()
- }
-
- let stackView = UIStackView()
- stackView.axis = .vertical
- stackView.spacing = 24
- scrollView.addSubview(stackView)
- stackView.snp.makeConstraints { make in
- make.verticalEdges.equalToSuperview()
- make.horizontalEdges.equalToSuperview().inset(16)
- make.width.equalToSuperview().offset(-32)
- }
-
- stackView.addArrangedSubview(buildProfilePhoto())
- stackView.addArrangedSubview(buildPhotoWall())
- stackView.addArrangedSubview(buildUserInfo())
- stackView.addArrangedSubview(buildSkillInfo())
- }
-
- private func setupNavBar() {
- customBack = { [weak self] in
- guard let self else { return }
- if saveButton.isEnabled {
- LNCommonAlertView.showProfileEditEnsaveAlert { [weak self] in
- guard let self else { return }
- navigationController?.popViewController(animated: true)
- }
- } else {
- navigationController?.popViewController(animated: true)
- }
- }
- title = .init(key: "A00202")
-
- saveButton.backgroundColor = .fill_4
- saveButton.layer.cornerRadius = 16
- saveButton.setBackgroundImage(.primary_8, for: .normal)
- saveButton.clipsToBounds = true
- saveButton.addAction(UIAction(handler: { [weak self] _ in
- guard let self else { return }
- let config = LNProfileUpdateConfig()
- if profilePhoto.imageUrl != myUserInfo.avatar {
- config.avatar = profilePhoto.imageUrl
- }
- let photos = photoWall.curPhotos
- if photos != myUserInfo.photos {
- config.photos = photos
- }
- if curName != myUserInfo.nickname {
- config.nickName = curName
- }
- if curGender != myUserInfo.gender {
- config.gender = curGender
- }
- if curBirthday != Double(myUserInfo.birthday / 1_000) {
- config.birthday = curBirthday.formattedFullDate("-", normal: true)
- }
- if curBio != myUserInfo.intro {
- config.bio = curBio
- }
- if curInterest.map({ $0.code }) != myUserInfo.interests.map({ $0.code }) {
- config.interest = curInterest.map({ $0.code })
- }
- LNProfileManager.shared.modifyMyProfile(config: config) { [weak self] success in
- guard let self else { return }
- guard success else { return }
- navigationController?.popViewController(animated: true)
- }
- }), for: .touchUpInside)
- setRightButton(saveButton)
- saveButton.snp.makeConstraints { make in
- make.height.equalTo(32)
- }
-
- let saveLabel = UILabel()
- saveLabel.text = .init(key: "A00185")
- saveLabel.font = .heading_h4
- saveLabel.textColor = .text_1
- saveButton.addSubview(saveLabel)
- saveLabel.snp.makeConstraints { make in
- make.center.equalToSuperview()
- make.leading.equalToSuperview().offset(17)
- }
- }
-
- private func buildProfilePhoto() -> UIView {
- let container = UIView()
-
- let titleLabel = UILabel()
- titleLabel.font = .heading_h4
- titleLabel.textColor = .text_5
- titleLabel.text = .init(key: "A00203")
- container.addSubview(titleLabel)
- titleLabel.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview()
- make.top.equalToSuperview().offset(10)
- }
-
- profilePhoto.uploadType = .avatar
- profilePhoto.backgroundColor = .fill_2
- profilePhoto.layer.cornerRadius = 11
- profilePhoto.clipsToBounds = true
- profilePhoto.contentMode = .scaleAspectFill
- profilePhoto.showClearButton = false
- profilePhoto.showDefault = true
- profilePhoto.delegate = self
- profilePhoto.onTap { [weak self] in
- guard let self else { return }
- LNBottomSheetMenu.showImageSelectMenu(view: view, options: .init(allowEdit: true))
- { [weak self] image, _ in
- guard let self else { return }
- guard let image = image?.compress(type: .avatar) else { return }
- profilePhoto.uploadImage(image: image)
- }
- }
- container.addSubview(profilePhoto)
- profilePhoto.snp.makeConstraints { make in
- make.leading.equalToSuperview()
- make.top.equalTo(titleLabel.snp.bottom).offset(10)
- make.bottom.equalToSuperview()
- make.height.width.equalTo(110)
- }
-
- return container
- }
-
- private func buildPhotoWall() -> UIView {
- photoWall.delegate = self
- photoWall.loadImages(myUserInfo.photos)
-
- return photoWall
- }
-
- private func buildUserInfo() -> UIView {
- let container = UIView()
-
- let titleLabel = UILabel()
- titleLabel.font = .heading_h4
- titleLabel.textColor = .text_5
- titleLabel.text = .init(key: "A00204")
- container.addSubview(titleLabel)
- titleLabel.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview()
- make.top.equalToSuperview().offset(10)
- }
-
- let stackView = UIStackView()
- stackView.axis = .vertical
- stackView.spacing = 22
- container.addSubview(stackView)
- stackView.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview()
- make.top.equalTo(titleLabel.snp.bottom).offset(10)
- make.bottom.equalToSuperview()
- }
-
- let nameView = buildInfoLine(title: .init(key: "A00188"), label: nameLabel)
- nameView.onTap { [weak self] in
- guard let self else { return }
- let panel = LNEditNickNamePanel()
- panel.inputField.text = curName
- panel.handler = { [weak self] name in
- guard let self else { return }
- curName = name
- checkSaveButton()
- }
- panel.popup()
- }
- stackView.addArrangedSubview(nameView)
-
- let genderView = buildInfoLine(title: .init(key: "A00032"), label: genderLabel)
- genderView.onTap { [weak self] in
- guard let self else { return }
- let panel = LNEditGenderPanel()
- panel.curGender = curGender
- panel.handler = { [weak self] gender in
- guard let self else { return }
- curGender = gender
- checkSaveButton()
- }
- panel.popup()
- }
- stackView.addArrangedSubview(genderView)
-
- let birthdayView = buildInfoLine(title: .init(key: "A00205"), label: birthdayLabel)
- birthdayView.onTap { [weak self] in
- guard let self else { return }
- let panel = LNDatePickerPanel()
- panel.titleLabel.text = .init(key: "A00007")
- panel.datePicker.maximumDate = Date().yearAgo(-18)
- panel.setDefault(curBirthday)
- panel.handler = { [weak self] date in
- guard let self else { return }
- curBirthday = date
- checkSaveButton()
- }
- panel.popup()
- }
- stackView.addArrangedSubview(birthdayView)
-
- let bioView = buildInfoLine(title: .init(key: "A00184"), label: bioLabel)
- bioView.onTap { [weak self] in
- guard let self else { return }
- let panel = LNEditBioPanel()
- panel.update(curBio)
- panel.handler = { [weak self] bio in
- guard let self else { return }
- curBio = bio
- checkSaveButton()
- }
- panel.popup()
- }
- stackView.addArrangedSubview(bioView)
-
- let interestView = buildInfoLine(title: .init(key: "A00206"), label: interestLabel)
- interestView.onTap { [weak self] in
- guard let self else { return }
- let panel = LNEditInterestPanel(selecteds: curInterest.map({ $0.code }))
- panel.handler = { [weak self] items in
- guard let self else { return }
- curInterest = items.map({
- LNUserInterestVO(code: $0.code, name: $0.name)
- })
- checkSaveButton()
- }
- panel.popup()
- }
- stackView.addArrangedSubview(interestView)
-
- let line = buildLine()
- container.addSubview(line)
- line.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview().inset(16)
- make.top.equalToSuperview()
- }
-
- return container
- }
-
- private func buildSkillInfo() -> UIView {
- let container = UIView()
-
- let titleLabel = UILabel()
- titleLabel.font = .heading_h4
- titleLabel.textColor = .text_5
- titleLabel.text = .init(key: "B00004")
- container.addSubview(titleLabel)
- titleLabel.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview()
- make.top.equalToSuperview().offset(10)
- }
-
- let stackView = UIStackView()
- stackView.axis = .vertical
- stackView.spacing = 22
- container.addSubview(stackView)
- stackView.snp.makeConstraints { make in
- make.horizontalEdges.equalToSuperview()
- make.top.equalTo(titleLabel.snp.bottom).offset(10)
- make.bottom.equalToSuperview()
- }
-
- let voiceView = buildInfoLine(title: .init(key: "B00005"), label: voiceLabel)
- voiceView.onTap { [weak self] in
- guard let self else { return }
- let panel = LNEditVoicePanel()
- panel.popup()
- }
- stackView.addArrangedSubview(voiceView)
-
- return container
- }
-
- private func buildInfoLine(title: String, label: UILabel) -> UIView {
- let container = UIView()
-
- let titleLabel = UILabel()
- titleLabel.text = title
- titleLabel.font = .body_m
- titleLabel.textColor = .text_4
- titleLabel.setContentHuggingPriority(.required, for: .horizontal)
- titleLabel.setContentCompressionResistancePriority(.required, for: .horizontal)
- container.addSubview(titleLabel)
- titleLabel.snp.makeConstraints { make in
- make.verticalEdges.equalToSuperview()
- make.leading.equalToSuperview()
- }
-
- let arrow = UIImageView.arrowImageView(size: 14)
- arrow.tintColor = .text_4
- container.addSubview(arrow)
- arrow.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.trailing.equalToSuperview()
- }
-
- label.font = .body_m
- label.textColor = .text_3
- container.addSubview(label)
- label.snp.makeConstraints { make in
- make.centerY.equalToSuperview()
- make.trailing.equalTo(arrow.snp.leading).offset(-5)
- make.leading.greaterThanOrEqualTo(titleLabel.snp.trailing).offset(50)
- }
-
- return container
- }
-
- private func buildLine() -> UIView {
- let line = UIView()
- line.backgroundColor = .fill_2
- line.snp.makeConstraints { make in
- make.height.equalTo(1)
- }
-
- return line
- }
- }
- #if DEBUG
- import SwiftUI
- struct LNEditProfileViewControllerPreview: UIViewControllerRepresentable {
- func makeUIViewController(context: Context) -> some UIViewController {
- LNEditProfileViewController()
- }
-
- func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) { }
- }
- #Preview(body: {
- LNEditProfileViewControllerPreview()
- })
- #endif
|