// // LNLoginPanel.swift // Lanu // // Created by OneeChan on 2025/12/2. // import Foundation import UIKit import SnapKit private weak var curLoginPanel: LNLoginPanel? = nil class LNLoginPanel: LNPopupView { override init(frame: CGRect) { super.init(frame: frame) containerHeight = .percent(1.0) setupViews() LNEventDeliver.addObserver(self) } static func show(container: UIView?) { guard curLoginPanel == nil else { return } let panel = LNLoginPanel() panel.popup(container) curLoginPanel = panel } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } extension LNLoginPanel: LNAccountManagerNotify { func onUserLogin() { dismiss() } } extension LNLoginPanel { private func setupViews() { container.backgroundColor = .text_5.withAlphaComponent(0.7) let logo = UIImageView() logo.image = .icLoginLogo container.addSubview(logo) logo.snp.makeConstraints { make in make.centerX.equalToSuperview() make.centerY.equalToSuperview().multipliedBy(0.5) } let privacy = buildPrivacy() container.addSubview(privacy) privacy.snp.makeConstraints { make in make.centerX.equalToSuperview() make.width.equalToSuperview().offset(-50) make.bottom.equalToSuperview().offset(-safeBottomInset - 40) } let stackView = UIStackView() stackView.axis = .vertical stackView.spacing = 16 container.addSubview(stackView) stackView.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(38) make.bottom.equalTo(privacy.snp.top).offset(-50) } #if DEBUG let email = buildEmail() stackView.addArrangedSubview(email) #endif let phone = buildLoginItem(icon: .icLoginPhone, title: .init(key: "B00019"), textColor: .text_1, color: .primary_3) phone.addAction(UIAction(handler: { [weak self] _ in guard let self else { return } pushToLoginPhone() }), for: .touchUpInside) stackView.addArrangedSubview(phone) let apple = buildLoginItem(icon: .icApple, title: .init(key: "A00285"), textColor: .text_1, color: .init(hex: "#0B0B0A")) apple.addAction(UIAction(handler: { [weak self] _ in guard self != nil else { return } LNAccountManager.shared.doAppleLogin() }), for: .touchUpInside) stackView.addArrangedSubview(apple) let google = buildLoginItem(icon: .icGoogle, title: .init(key: "A00115"), textColor: .text_5, color: .fill) google.addAction(UIAction(handler: { [weak self] _ in guard let self else { return } guard let topVC = navigationController?.topViewController else { return } LNAccountManager.shared.doGoogleLogin(topVC) }), for: .touchUpInside) stackView.addArrangedSubview(google) let tipsLabel = UILabel() tipsLabel.text = .init(key: "A00114") tipsLabel.font = .body_m tipsLabel.textColor = .primary_3 tipsLabel.numberOfLines = 0 tipsLabel.textAlignment = .center container.addSubview(tipsLabel) tipsLabel.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(38) make.bottom.equalTo(stackView.snp.top).offset(-16) } let close = buildClose() container.addSubview(close) close.snp.makeConstraints { make in make.top.equalToSuperview().offset(UIView.statusBarHeight) make.trailing.equalToSuperview().offset(-20) } } private func buildLoginItem(icon: UIImage, title: String, textColor: UIColor, color: UIColor) -> UIButton { let button = UIButton() button.setBackgroundImage(UIImage.image(for: color), for: .normal) button.layer.cornerRadius = 24 button.clipsToBounds = true button.snp.makeConstraints { make in make.height.equalTo(48) } let ic = UIImageView() ic.image = icon button.addSubview(ic) ic.snp.makeConstraints { make in make.centerY.equalToSuperview() make.leading.equalToSuperview().offset(12) } let titleLabel = UILabel() titleLabel.font = .heading_h3 titleLabel.textColor = textColor titleLabel.text = title button.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.center.equalToSuperview() } return button } private func buildPrivacy() -> UIView { let text: String = .init(key: "A00116") let attrStr = NSMutableAttributedString(string: text) let agreementRange = (text.lowercased() as NSString).range(of: .init(key: "A00117").lowercased()) attrStr.addAttributes([ .link: String.serviceUrl, .underlineStyle: NSUnderlineStyle.single.rawValue, .underlineColor: UIColor.text_2 ], range: agreementRange) let privacyRange = (text.lowercased() as NSString).range(of: .init(key: "A00118").lowercased()) attrStr.addAttributes([ .link: String.privacyUrl, .underlineStyle: NSUnderlineStyle.single.rawValue, .underlineColor: UIColor.text_2, ], range: privacyRange) let privacyView = LNPrivacyTextView() privacyView.attributedText = attrStr privacyView.textAlignment = .center privacyView.backgroundColor = .clear privacyView.font = .systemFont(ofSize: 13.4) privacyView.textColor = .text_2 privacyView.linkTextAttributes = [.foregroundColor: UIColor.text_1] return privacyView } #if DEBUG private func buildEmail() -> UIView { let container = UIView() container.backgroundColor = .fill container.layer.cornerRadius = 24 container.snp.makeConstraints { make in make.height.equalTo(48) } let input = UITextField() let login = UIButton() login.setContentHuggingPriority(.defaultHigh, for: .horizontal) login.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal) login.setImage(.init(systemName: "arrow.forward"), for: .normal) login.addAction(UIAction(handler: { [weak self] _ in guard self != nil else { return } guard let text = input.text, !text.isEmpty else { return } LNAccountManager.shared.loginByEmail(email: text) { _ in } }), for: .touchUpInside) container.addSubview(login) login.snp.makeConstraints { make in make.centerY.equalToSuperview() make.trailing.equalToSuperview().offset(-16) } container.addSubview(input) input.snp.makeConstraints { make in make.verticalEdges.equalToSuperview() make.leading.equalToSuperview().offset(16) make.trailing.equalTo(login.snp.leading) } return container } #endif private func buildClose() -> UIView { let config = UIImage.SymbolConfiguration(pointSize: 24) let button = UIButton() button.setImage(.init(systemName: "xmark", withConfiguration: config), for: .normal) button.tintColor = .white button.addAction(UIAction(handler: { [weak self] _ in guard let self else { return } self.dismiss() }), for: .touchUpInside) return button } } #if DEBUG import SwiftUI import AuthenticationServices struct LNLoginPanelPreview: UIViewRepresentable { func makeUIView(context: Context) -> some UIView { let container = UIView() container.backgroundColor = .lightGray let view = LNLoginPanel() view.popup(container) return container } func updateUIView(_ uiView: UIViewType, context: Context) { } } #Preview(body: { LNLoginPanelPreview() }) #endif