forked from AcaMate/AcaMate_iOS
[✨] 회원가입 페이지 및 드롭다운 기능 추가중
This commit is contained in:
parent
5a2c237e3a
commit
f11d3b417a
Binary file not shown.
|
@ -9,12 +9,51 @@ import SwiftUI
|
|||
import WebKit
|
||||
|
||||
struct WebView: UIViewControllerRepresentable {
|
||||
@Binding var isLoding: Bool
|
||||
|
||||
|
||||
var url: URL
|
||||
@Binding var showWebView: Bool
|
||||
@Binding var isLoading: Bool
|
||||
var complete: ((Any) -> Void)?
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
return Coordinator(parent: self)
|
||||
}
|
||||
|
||||
class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
|
||||
var parent: WebView
|
||||
|
||||
init(parent: WebView) {
|
||||
self.parent = parent
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
||||
print("웹뷰 로딩 완료")
|
||||
parent.isLoading = false
|
||||
|
||||
}
|
||||
|
||||
func userContentController(_ userContentController: WKUserContentController,
|
||||
didReceive message: WKScriptMessage) {
|
||||
if let data = message.body as? [String: Any]
|
||||
{
|
||||
if let road = data["roadAddress"], let code = data["zonecode"] {
|
||||
printLog("도로명 주소: \(road) // ZIP CODE: \(code)")
|
||||
parent.complete?((road,code))
|
||||
}
|
||||
}
|
||||
parent.showWebView.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func updateUIViewController(_ uiViewController: WebViewController, context: Context) {
|
||||
}
|
||||
|
||||
func makeUIViewController(context: Context) -> WebViewController {
|
||||
return WebViewController()
|
||||
return WebViewController(url: url, isLoading: $isLoading,
|
||||
complete: complete, coordinator: context.coordinator)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -22,24 +61,41 @@ struct WebView: UIViewControllerRepresentable {
|
|||
|
||||
class WebViewController: UIViewController, WKUIDelegate {
|
||||
|
||||
var url: URL
|
||||
@Binding var isLoading: Bool
|
||||
var complete: ((Any) -> Void)?
|
||||
var coordinator: WebView.Coordinator
|
||||
|
||||
init(url: URL, isLoading: Binding<Bool>, complete: ((Any) -> Void)?, coordinator: WebView.Coordinator) {
|
||||
self.url = url
|
||||
_isLoading = isLoading
|
||||
self.complete = complete
|
||||
self.coordinator = coordinator
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
webView()
|
||||
}
|
||||
|
||||
func webView() {
|
||||
let url = URL(string: "https://sean-59.github.io/Kakao-Postcode/")!
|
||||
private func webView() {
|
||||
let request = URLRequest(url: url)
|
||||
|
||||
let configuration = WKWebViewConfiguration()
|
||||
let contentController = WKUserContentController()
|
||||
|
||||
contentController.add(self, name: "callBackHandler")
|
||||
contentController.add(coordinator, name: "callBackHandler")
|
||||
configuration.userContentController = contentController
|
||||
|
||||
let webview = WKWebView(frame: view.bounds, configuration: configuration)
|
||||
webview.uiDelegate = self
|
||||
webview.navigationDelegate = self
|
||||
webview.navigationDelegate = coordinator
|
||||
webview.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
|
||||
webview.isUserInteractionEnabled = true
|
||||
|
@ -51,28 +107,3 @@ class WebViewController: UIViewController, WKUIDelegate {
|
|||
|
||||
}
|
||||
}
|
||||
|
||||
extension WebViewController: WKNavigationDelegate {
|
||||
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
|
||||
print("웹뷰 로딩 시작")
|
||||
}
|
||||
|
||||
|
||||
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
||||
print("웹뷰 로딩 완료")
|
||||
// 여기서 추가 작업(예: 로딩 인디케이터 숨기기)을 수행할 수 있습니다.
|
||||
}
|
||||
}
|
||||
|
||||
extension WebViewController: WKScriptMessageHandler {
|
||||
func userContentController(_ userContentController: WKUserContentController,
|
||||
didReceive message: WKScriptMessage) {
|
||||
if let data = message.body as? [String: Any] {
|
||||
print(data)
|
||||
print(data["jibunAddress"] ?? "jibunAddress 없음")
|
||||
print(data["roadAddress"] ?? "roadAddress 없음")
|
||||
print(data["zonecode"] ?? "zonecode 없음")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
95
AcaMate/1. View/10. Common/DropDownView.swift
Normal file
95
AcaMate/1. View/10. Common/DropDownView.swift
Normal file
|
@ -0,0 +1,95 @@
|
|||
//
|
||||
// DropDownView.swift
|
||||
// AcaMate
|
||||
//
|
||||
// Created by TAnine on 3/25/25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
class DropdownManager: ObservableObject {
|
||||
static let shared = DropdownManager()
|
||||
|
||||
@Published var isPresented = false
|
||||
@Published var items: [String] = []
|
||||
@Published var anchor: CGRect = .zero
|
||||
var onSelect: ((String) -> Void)?
|
||||
|
||||
func show(items: [String], anchor: CGRect, onSelect: @escaping (String) -> Void) {
|
||||
self.items = items
|
||||
self.anchor = anchor
|
||||
self.onSelect = onSelect
|
||||
isPresented = true
|
||||
}
|
||||
|
||||
func dismiss() {
|
||||
isPresented = false
|
||||
}
|
||||
}
|
||||
|
||||
struct DropdownButton: View {
|
||||
let title: String
|
||||
let items: [String]
|
||||
|
||||
@State private var frame: CGRect = .zero
|
||||
|
||||
var body: some View {
|
||||
Button {
|
||||
DropdownManager.shared.show(items: items, anchor: frame) { selected in
|
||||
print("선택한 항목:", selected)
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
Text(title)
|
||||
Image(systemName: "chevron.down")
|
||||
}
|
||||
.padding()
|
||||
.background(RoundedRectangle(cornerRadius: 8).stroke(Color.gray))
|
||||
}
|
||||
.background(
|
||||
GeometryReader { geo in
|
||||
Color.clear
|
||||
.onAppear {
|
||||
frame = geo.frame(in: .global)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct GlobalDropdownOverlay: View {
|
||||
@ObservedObject var manager = DropdownManager.shared
|
||||
|
||||
var body: some View {
|
||||
if manager.isPresented {
|
||||
ZStack(alignment: .topLeading) {
|
||||
Color.black.opacity(0.001)
|
||||
.ignoresSafeArea()
|
||||
.onTapGesture {
|
||||
manager.dismiss()
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
ForEach(manager.items, id: \.self) { item in
|
||||
Button {
|
||||
manager.onSelect?(item)
|
||||
manager.dismiss()
|
||||
} label: {
|
||||
Text(item)
|
||||
.padding()
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.background(Color.white)
|
||||
}
|
||||
}
|
||||
.frame(width: manager.anchor.width)
|
||||
.background(RoundedRectangle(cornerRadius: 8).stroke(Color.gray))
|
||||
.shadow(radius: 5)
|
||||
.position(x: manager.anchor.midX, y: manager.anchor.maxY + 10)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -24,134 +24,188 @@ struct RegisterView: View {
|
|||
|
||||
@State private var scrollOffset: CGPoint = .zero
|
||||
@State private var showWebView = false
|
||||
@State private var isLoading = false
|
||||
|
||||
@State private var isSelectAddr: Bool = false
|
||||
|
||||
private let addressBtnID = UUID()
|
||||
private let registerBtnID = UUID()
|
||||
|
||||
let addressBtnID = UUID()
|
||||
let registerBtnID = UUID()
|
||||
|
||||
@State private var selected = ""
|
||||
let options = ["Swift", "Kotlin", "Dart", "JavaScript"]
|
||||
|
||||
var body: some View {
|
||||
// MARK: TO-DO
|
||||
// 회원가입 뷰 만들기
|
||||
// 이름, 번호, 이메일, 주소, 생년월일
|
||||
VStack(spacing: 0) {
|
||||
TopView(topVM: topVM)
|
||||
|
||||
OffsetObservableScrollView(showsIndicators: false, scrollOffset: $scrollOffset) { proxy in
|
||||
|
||||
// 이름
|
||||
HStack(spacing: 0){
|
||||
HStack(spacing: 0) {
|
||||
Text("이름")
|
||||
.font(.nps(size: 16))
|
||||
Text("*")
|
||||
.font(.nps(size: 16))
|
||||
.foregroundStyle(Color(.Other.red))
|
||||
}
|
||||
.frame(width: 60, alignment: .leading)
|
||||
ZStack {
|
||||
VStack(spacing: 0) {
|
||||
TopView(topVM: topVM)
|
||||
HStack(spacing: 0) {
|
||||
Spacer(minLength: 1)
|
||||
CustomTextField(placeholder: "최대 10글자", text: $registerVM.nameText)
|
||||
.frame(maxWidth: .infinity,maxHeight: 48)
|
||||
.padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20))
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 24)
|
||||
.foregroundStyle(Color(.Normal.light))
|
||||
}
|
||||
Text("*")
|
||||
.font(.nps(size: 12))
|
||||
.foregroundStyle(Color(.Other.red))
|
||||
Text("는 필수 입력 사항입니다.")
|
||||
.font(.nps(size: 12))
|
||||
}
|
||||
.padding()
|
||||
.padding(EdgeInsets(top: 8, leading: 0, bottom: 8, trailing: 16))
|
||||
|
||||
// 생년월일
|
||||
DatePicker("생일", selection: $registerVM.selectDate, displayedComponents: [.date])
|
||||
.datePickerStyle(.compact)
|
||||
.environment(\.locale, Locale(identifier: "ko_KR"))
|
||||
.font(.nps(size: 16))
|
||||
DropdownButton(title: "언어 선택", items: ["Swift", "Dart", "Kotlin"])
|
||||
|
||||
|
||||
OffsetObservableScrollView(showsIndicators: false, scrollOffset: $scrollOffset) { proxy in
|
||||
|
||||
// 이름
|
||||
HStack(spacing: 0){
|
||||
HStack(spacing: 0) {
|
||||
Text("이름")
|
||||
.font(.nps(size: 16))
|
||||
Text("*")
|
||||
.font(.nps(size: 16))
|
||||
.foregroundStyle(Color(.Other.red))
|
||||
}
|
||||
.frame(width: 60, alignment: .center)
|
||||
Spacer(minLength: 1)
|
||||
CustomTextField(placeholder: "최대 10글자 입력", text: $registerVM.nameText, alignment: .center)
|
||||
.frame(maxWidth: .infinity,maxHeight: 48)
|
||||
.padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20))
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 24)
|
||||
.foregroundStyle(Color(.Normal.light))
|
||||
}
|
||||
}
|
||||
.padding(EdgeInsets(top: 8, leading: 16, bottom: 16, trailing: 16))
|
||||
|
||||
// 생년월일
|
||||
HStack(spacing: 0){
|
||||
Text("생일")
|
||||
.font(.nps(size: 16))
|
||||
.frame(width: 60, alignment: .center)
|
||||
Spacer(minLength: 1)
|
||||
DatePicker("", selection: $registerVM.selectDate, displayedComponents: [.date])
|
||||
.datePickerStyle(.compact)
|
||||
.environment(\.locale, Locale(identifier: "ko_KR"))
|
||||
.font(.nps(size: 16))
|
||||
}
|
||||
.padding()
|
||||
|
||||
// E-Mail
|
||||
HStack(spacing: 0){
|
||||
Text("이메일")
|
||||
.font(.nps(size: 16))
|
||||
.frame(width: 60, alignment: .leading)
|
||||
Spacer(minLength: 1)
|
||||
CustomTextField(placeholder: "앞부분 입력", text: $registerVM.emailFrontText)
|
||||
.frame(maxWidth: .infinity,maxHeight: 48)
|
||||
.padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20))
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 24)
|
||||
.foregroundStyle(Color(.Normal.light))
|
||||
// E-Mail
|
||||
HStack(spacing: 0){
|
||||
HStack(spacing: 0) {
|
||||
Text("이메일")
|
||||
.font(.nps(size: 16))
|
||||
Text("*")
|
||||
.font(.nps(size: 16))
|
||||
.foregroundStyle(Color(.Other.red))
|
||||
}
|
||||
// Spacer(minLength: 1)
|
||||
Text("@")
|
||||
.font(.nps(font: .bold, size: 16))
|
||||
.padding([.leading, .trailing], 4)
|
||||
// Spacer(minLength: 1)
|
||||
CustomTextField(placeholder: "뒷부분 입력", text: $registerVM.emailTailText)
|
||||
.frame(maxWidth: .infinity,maxHeight: 48)
|
||||
.padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20))
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 24)
|
||||
.foregroundStyle(Color(.Normal.light))
|
||||
|
||||
Spacer(minLength: 1)
|
||||
CustomTextField(placeholder: "앞부분 입력", text: $registerVM.emailFrontText, alignment: .center)
|
||||
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20))
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 24)
|
||||
.foregroundStyle(Color(.Normal.light))
|
||||
}
|
||||
// Spacer(minLength: 1)
|
||||
Text("@")
|
||||
.font(.nps(size: 16))
|
||||
.padding([.leading, .trailing], 4)
|
||||
// Spacer(minLength: 1)
|
||||
CustomTextField(placeholder: "뒷부분 입력", text: $registerVM.emailTailText, alignment: .center)
|
||||
.frame(maxWidth: .infinity,maxHeight: 48)
|
||||
.padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20))
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 24)
|
||||
.foregroundStyle(Color(.Normal.light))
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
|
||||
// Phone
|
||||
HStack(spacing: 0){
|
||||
HStack(spacing: 0) {
|
||||
Text("연락처")
|
||||
.font(.nps(size: 16))
|
||||
Text("*")
|
||||
.font(.nps(size: 16))
|
||||
.foregroundStyle(Color(.Other.red))
|
||||
}
|
||||
|
||||
CustomTextField(placeholder: "000", text: $registerVM.nameText, alignment: .center)
|
||||
.frame(maxWidth: .infinity,maxHeight: 48)
|
||||
.padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20))
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 24)
|
||||
.foregroundStyle(Color(.Normal.light))
|
||||
}
|
||||
Text("-")
|
||||
.font(.nps(size: 16))
|
||||
.padding([.leading, .trailing], 4)
|
||||
CustomTextField(placeholder: "0000", text: $registerVM.nameText, alignment: .center)
|
||||
.frame(maxWidth: .infinity,maxHeight: 48)
|
||||
.padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20))
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 24)
|
||||
.foregroundStyle(Color(.Normal.light))
|
||||
}
|
||||
Text("-")
|
||||
.font(.nps(size: 16))
|
||||
.padding([.leading, .trailing], 4)
|
||||
CustomTextField(placeholder: "0000", text: $registerVM.nameText, alignment: .center)
|
||||
.frame(maxWidth: .infinity,maxHeight: 48)
|
||||
.padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20))
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 24)
|
||||
.foregroundStyle(Color(.Normal.light))
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
|
||||
HStack(spacing: 0){
|
||||
Text("주소")
|
||||
.font(.nps(size: 16))
|
||||
.frame(width: 60, alignment: .center)
|
||||
Spacer(minLength: 1)
|
||||
|
||||
VStack(spacing: 0) {
|
||||
SimpleBtnView(vm: btnVM, id: addressBtnID)
|
||||
.padding(.bottom, isSelectAddr ? 4:0)
|
||||
CustomTextField(placeholder: "상세 주소 입력", text: $registerVM.addrDetailText, alignment: .center)
|
||||
.frame(maxWidth: .infinity,
|
||||
maxHeight: isSelectAddr ? 48 : 0)
|
||||
.padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20))
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 24)
|
||||
.foregroundStyle(Color(.Normal.light))
|
||||
}
|
||||
.opacity(isSelectAddr ? 1.0 : 0.0)
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.padding()
|
||||
|
||||
// Phone
|
||||
HStack(spacing: 0){
|
||||
Text("연락처")
|
||||
.font(.nps(size: 16))
|
||||
.frame(width: 60, alignment: .leading)
|
||||
|
||||
CustomTextField(placeholder: "000", text: $registerVM.nameText)
|
||||
.frame(maxWidth: .infinity,maxHeight: 48)
|
||||
.padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20))
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 24)
|
||||
.foregroundStyle(Color(.Normal.light))
|
||||
}
|
||||
Text("-")
|
||||
.font(.nps(size: 16))
|
||||
.padding([.leading, .trailing], 4)
|
||||
CustomTextField(placeholder: "0000", text: $registerVM.nameText)
|
||||
.frame(maxWidth: .infinity,maxHeight: 48)
|
||||
.padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20))
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 24)
|
||||
.foregroundStyle(Color(.Normal.light))
|
||||
}
|
||||
Text("-")
|
||||
.font(.nps(size: 16))
|
||||
.padding([.leading, .trailing], 4)
|
||||
CustomTextField(placeholder: "0000", text: $registerVM.nameText)
|
||||
.frame(maxWidth: .infinity,maxHeight: 48)
|
||||
.padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20))
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 24)
|
||||
.foregroundStyle(Color(.Normal.light))
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
|
||||
HStack(spacing: 0){
|
||||
Text("연락처")
|
||||
.font(.nps(size: 16))
|
||||
.frame(width: 60, alignment: .leading)
|
||||
Spacer(minLength: 1)
|
||||
SimpleBtnView(vm: btnVM, id: addressBtnID)
|
||||
.padding(EdgeInsets(top: 4, leading: 20, bottom: 4, trailing: 20))
|
||||
|
||||
}
|
||||
.padding()
|
||||
|
||||
// address
|
||||
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.sheet(isPresented: $showWebView) {
|
||||
WebView(isLoding: $showWebView)
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
// .background(Color(.Normal.light))
|
||||
}
|
||||
GlobalDropdownOverlay()
|
||||
}
|
||||
.sheet(isPresented: $showWebView) {
|
||||
WebView(url: URL(string: "https://sean-59.github.io/Kakao-Postcode/")!,
|
||||
showWebView: $showWebView,
|
||||
isLoading: $isLoading) { result in
|
||||
if let result = result as? (String, String) {
|
||||
print(result.0)
|
||||
isSelectAddr = true
|
||||
// printLog(registerVM.addressText)
|
||||
registerVM.addressText = result.0
|
||||
|
||||
btnVM.setSize(for: addressBtnID, newWidth: .infinity, newHeight: .infinity)
|
||||
// printLog(registerVM.addressText)
|
||||
btnVM.setText(for: addressBtnID, newText: "\(registerVM.addressText)", newFont: .nps(size: 16))
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
topVM.titleName = "회원가입"
|
||||
|
@ -159,11 +213,12 @@ struct RegisterView: View {
|
|||
topVM.setRightBtn(size: CGPoint(x: 40, y: 40), action: rightAct)
|
||||
|
||||
btnVM.setSize(for: addressBtnID, newWidth: 80, newHeight: 24)
|
||||
btnVM.setText(for: addressBtnID, newText: "주소 입력", newFont: .nps(size: 16))
|
||||
btnVM.setText(for: addressBtnID, newText: "\(registerVM.addressText)", newFont: .nps(size: 16))
|
||||
btnVM.setAction(for: addressBtnID) {
|
||||
// self.appVM.isLoading.toggle()
|
||||
self.showWebView.toggle()
|
||||
}
|
||||
}
|
||||
.onChange(of: registerVM.addressText) { _, new in
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,8 @@ class RegisterViewModel: ObservableObject {
|
|||
@State var emailFrontText: String = ""
|
||||
@State var emailTailText: String = ""
|
||||
@State var phoneArray: [Int] = []
|
||||
@State var addressText: String = ""
|
||||
@Published var addressText: String = "주소 입력"
|
||||
@State var addrDetailText: String = ""
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ struct CustomTextField: UIViewRepresentable {
|
|||
|
||||
var textColor = UIColor(Color(.Text.detail))
|
||||
var font = UIFont(name: "NPS-font-Regular", size: 16)
|
||||
var alignment: NSTextAlignment = .left
|
||||
|
||||
// [필수] 초기화 시 UIView 생성
|
||||
func makeUIView(context: Context) -> UITextField {
|
||||
|
@ -37,6 +38,7 @@ struct CustomTextField: UIViewRepresentable {
|
|||
txf.autocapitalizationType = .none
|
||||
txf.smartInsertDeleteType = .no
|
||||
txf.textContentType = .oneTimeCode
|
||||
txf.textAlignment = self.alignment
|
||||
|
||||
txf.delegate = context.coordinator
|
||||
return txf
|
||||
|
|
Loading…
Reference in New Issue
Block a user