// // LNPostShareViewController.swift // Lanu // // Created by OneeChan on 2025/11/27. // import Foundation import UIKit import SnapKit import Combine extension UIView { func pushToPostShare() { let vc = LNPostShareViewController() navigationController?.pushViewController(vc, animated: true) } } class LNPostShareViewController: LNViewController { private let postView = UIImageView() private let skillStackView = UIStackView() private var selectedSkills: [LNGameMateSkillVO] = [] private var lastDesc: String? override func viewDidLoad() { super.viewDidLoad() setupViews() prefetchSkillIcon() loadLastOrder() selectedSkills = Array(myUserInfo.skills.prefix(3)) updateSkillsView() } } extension LNPostShareViewController { private func prefetchSkillIcon() { myUserInfo.skills.forEach { SDWebImageManager.shared.loadImage(with: URL(string: $0.icon), progress: nil) { _, _, _, _, _, _ in } } } private func loadLastOrder() { LNOrderManager.shared.getLastOrder { [weak self] res in guard let self else { return } guard let res else { return } lastDesc = .init(key: "A00220", Double(res.finishTime).formattedDate("."), res.nickname, res.bizCateGoryName) } } } extension LNPostShareViewController { private func updateSkillsView() { skillStackView.arrangedSubviews.forEach { skillStackView.removeArrangedSubview($0) $0.removeFromSuperview() } selectedSkills.prefix(3).forEach { let itemView = LNPostShareSkillItemView() itemView.icon.sd_setImage(with: URL(string: $0.icon)) itemView.nameLabel.text = $0.name skillStackView.addArrangedSubview(itemView) } } private func showSkillSelectPanel() { guard !myUserInfo.skills.isEmpty else { return } let panel = LNPostSkillSelectPanel() panel.setSkills(skills: myUserInfo.skills, selecteds: selectedSkills) panel.handler = { [weak self] selections in guard let self else { return } selectedSkills = selections updateSkillsView() } panel.popup(view) } private func setupViews() { title = .init(key: "A00221") view.backgroundColor = .primary_1 let topCover = UIImageView() topCover.image = .icHomeTopBg view.addSubview(topCover) topCover.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() } let scrollView = UIScrollView() scrollView.backgroundColor = .clear scrollView.showsHorizontalScrollIndicator = false scrollView.showsVerticalScrollIndicator = false view.addSubview(scrollView) scrollView.snp.makeConstraints { make in make.edges.equalToSuperview() } let fakeView = UIView() scrollView.addSubview(fakeView) fakeView.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview() make.top.equalToSuperview() make.width.equalToSuperview() make.height.equalTo(0) } let post = buildPost() scrollView.addSubview(post) post.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(16) make.top.equalToSuperview().offset(32) } let skill = buildSkillView() scrollView.addSubview(skill) skill.snp.makeConstraints { make in make.top.equalTo(post.snp.bottom) make.horizontalEdges.equalToSuperview().inset(16) make.height.equalTo(100) } let album = buildAlbum() scrollView.addSubview(album) album.snp.makeConstraints { make in make.leading.equalToSuperview().offset(16) make.top.equalTo(skill.snp.bottom).offset(20) make.trailing.equalToSuperview() make.bottom.equalToSuperview().offset(-view.safeBottomInset - 45) } let share = UIButton() share.setTitle(.init(key: "A00222"), for: .normal) share.setTitleColor(.text_1, for: .normal) share.setBackgroundImage(.primary_8, for: .normal) share.layer.cornerRadius = 23.5 share.clipsToBounds = true share.addAction(UIAction(handler: { [weak self] _ in guard let self else { return } guard let album = postView.image else { return } let generator = LNPostShareImageGenerator() generator.setOrderInfo(desc: lastDesc) generator.setAlbum(image: album) generator.setSkills(skills: selectedSkills) generator.setShareQRCode(url: .profileShareUrl + "?id=\(myUid)&share=app") let shareImage = generator.generate() shareImage.saveToLibrary { [weak self] success, err in guard let self else { return } if let err { showToast(err.localizedDescription) } else { showToast(.init(key: "A00157")) navigationController?.popViewController(animated: true) } } }), for: .touchUpInside) view.addSubview(share) share.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(16) make.bottom.equalTo(view.safeAreaLayoutGuide.snp.bottom).offset(-4) make.height.equalTo(47) } } private func buildPost() -> UIView { let container = UIView() container.backgroundColor = .fill container.layer.cornerRadius = 20 let info = buildUserInfo() container.addSubview(info) info.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(16) make.top.equalToSuperview().offset(12) } postView.contentMode = .scaleAspectFill postView.layer.cornerRadius = 20 postView.clipsToBounds = true container.addSubview(postView) postView.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(16) make.top.equalToSuperview().offset(44) make.height.equalTo(postView.snp.width).multipliedBy(317.0/311.0) } let bio = UILabel() bio.font = .body_m bio.textColor = .text_4 bio.numberOfLines = 0 bio.text = myUserInfo.intro container.addSubview(bio) bio.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(22) make.top.equalTo(postView.snp.bottom).offset(14) make.bottom.equalToSuperview().offset(-20) } let line = buildDashLine() container.layer.addSublayer(line) container.publisher(for: \.bounds).removeDuplicates().sink { [weak line] newValue in guard let line else { return } line.frame = .init( x: 14, y: newValue.height - 1, width: newValue.width - 28, height: 1) let path = UIBezierPath() path.move(to: CGPoint(x: 0, y: 0)) path.addLine(to: CGPoint(x: newValue.width - 28, y: 0)) line.path = path.cgPath }.store(in: &cancellables) return container } private func buildUserInfo() -> UIView { let container = UIView() container.snp.makeConstraints { make in make.height.equalTo(22) } let userNameLabel = UILabel() userNameLabel.text = myUserInfo.nickname userNameLabel.font = .heading_h2 userNameLabel.textColor = .text_5 container.addSubview(userNameLabel) userNameLabel.snp.makeConstraints { make in make.leading.centerY.equalToSuperview() } let gender = LNGenderView() gender.update(myUserInfo.gender, myUserInfo.age) container.addSubview(gender) gender.snp.makeConstraints { make in make.centerY.equalToSuperview() make.leading.equalTo(userNameLabel.snp.trailing).offset(4) } let starLabel = UILabel() starLabel.text = "\(myUserInfo.star)" starLabel.font = .heading_h2 starLabel.textColor = .text_5 container.addSubview(starLabel) starLabel.snp.makeConstraints { make in make.centerY.equalToSuperview() make.trailing.equalToSuperview() } let star = LNStarScoreView() star.score = 1.0 star.icSize = 18 container.addSubview(star) star.snp.makeConstraints { make in make.centerY.equalToSuperview() make.trailing.equalTo(starLabel.snp.leading).offset(-4) } return container } private func buildDashLine() -> CAShapeLayer { let lineLayer = CAShapeLayer() lineLayer.frame = .zero lineLayer.fillColor = UIColor.clear.cgColor lineLayer.strokeColor = UIColor.fill_3.cgColor lineLayer.lineWidth = 2.0 lineLayer.lineDashPattern = [NSNumber(value: 3), NSNumber(value: 3)] return lineLayer } private func buildSkillView() -> UIView { let container = UIView() container.backgroundColor = .fill container.layer.cornerRadius = 20 container.onTap { [weak self] in guard let self else { return } showSkillSelectPanel() } skillStackView.axis = .horizontal skillStackView.spacing = 10 container.addSubview(skillStackView) skillStackView.snp.makeConstraints { make in make.leading.equalToSuperview().offset(113) make.bottom.equalToSuperview().offset(-18) } let ic = UIImageView() ic.image = .icHomeTabSelected ic.alpha = 0.5 container.addSubview(ic) ic.snp.makeConstraints { make in make.leading.equalToSuperview().offset(20) make.top.equalToSuperview().offset(31) } let tipsLabel = UILabel() tipsLabel.text = .init(key: "A00219") tipsLabel.font = .heading_h4 tipsLabel.textColor = .text_5 tipsLabel.numberOfLines = 0 container.addSubview(tipsLabel) tipsLabel.snp.makeConstraints { make in make.centerY.equalToSuperview() make.leading.equalToSuperview().offset(30) make.width.equalTo(60) } let arrow = UIImageView.arrowImageView(size: 16) container.addSubview(arrow) arrow.snp.makeConstraints { make in make.centerY.equalToSuperview() make.trailing.equalToSuperview().offset(-8) } return container } private func buildAlbum() -> UIView { let scrollView = UIScrollView() scrollView.showsVerticalScrollIndicator = false scrollView.showsHorizontalScrollIndicator = false let fakeView = UIView() scrollView.addSubview(fakeView) fakeView.snp.makeConstraints { make in make.verticalEdges.equalToSuperview() make.height.equalToSuperview() make.leading.equalToSuperview() make.width.equalTo(0) } let stackView = UIStackView() stackView.axis = .horizontal stackView.spacing = 10 scrollView.addSubview(stackView) stackView.snp.makeConstraints { make in make.edges.equalToSuperview() } var albumItemViews: [LNSharePostAlbumItemView] = [] myUserInfo.photos.forEach { url in let album = LNSharePostAlbumItemView() album.imageView.sd_setImage(with: URL(string: url)) album.onTap { [weak self, weak album] in guard let self, let album else { return } albumItemViews.forEach { $0.isSelected = $0 == album } postView.sd_setImage(with: URL(string: url)) } album.isSelected = false stackView.addArrangedSubview(album) albumItemViews.append(album) } albumItemViews.first?.isSelected = true postView.sd_setImage(with: URL(string: myUserInfo.photos.first ?? "")) return scrollView } } private class LNSharePostAlbumItemView: UIView { let imageView = UIImageView() var isSelected: Bool = false { didSet { if isSelected { imageView.layer.cornerRadius = 11 imageView.snp.remakeConstraints { make in make.center.equalToSuperview() make.width.height.equalToSuperview().inset(2) } selectedBorder.isHidden = false alpha = 1.0 } else { imageView.layer.cornerRadius = 12 imageView.snp.remakeConstraints { make in make.center.equalToSuperview() make.width.height.equalToSuperview() } selectedBorder.isHidden = true alpha = 0.5 } } } private let selectedBorder = UIImageView() override init(frame: CGRect) { super.init(frame: frame) snp.makeConstraints { make in make.width.height.equalTo(90) } selectedBorder.image = .primary_7 selectedBorder.layer.cornerRadius = 12 selectedBorder.clipsToBounds = true selectedBorder.isHidden = true addSubview(selectedBorder) selectedBorder.snp.makeConstraints { make in make.edges.equalToSuperview() } imageView.clipsToBounds = true imageView.contentMode = .scaleAspectFill addSubview(imageView) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } #if DEBUG import SwiftUI struct LNSharePostViewControllerPreview: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> some UIViewController { LNPostShareViewController() } func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) { } } #Preview(body: { LNSharePostViewControllerPreview() }) #endif