mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Wallet: limit amount input to Int64.max
This commit is contained in:
parent
a8628602dd
commit
d0d98ba15a
@ -106,7 +106,7 @@ public func qrCode(string: String, color: UIColor, backgroundColor: UIColor? = n
|
|||||||
let _ = try? drawSvgPath(c, path: "M24.1237113,50.5927835 L40.8762887,50.5927835 L40.8762887,60.9793814 L32.5,64.0928525 L24.1237113,60.9793814 Z")
|
let _ = try? drawSvgPath(c, path: "M24.1237113,50.5927835 L40.8762887,50.5927835 L40.8762887,60.9793814 L32.5,64.0928525 L24.1237113,60.9793814 Z")
|
||||||
case let .custom(image):
|
case let .custom(image):
|
||||||
if let image = image {
|
if let image = image {
|
||||||
let fittedSize = image.size.fitted(clipRect.size)
|
let fittedSize = image.size.aspectFitted(clipRect.size)
|
||||||
let fittedRect = CGRect(origin: CGPoint(x: fittedRect.midX - fittedSize.width / 2.0, y: fittedRect.midY - fittedSize.height / 2.0), size: fittedSize)
|
let fittedRect = CGRect(origin: CGPoint(x: fittedRect.midX - fittedSize.width / 2.0, y: fittedRect.midY - fittedSize.height / 2.0), size: fittedSize)
|
||||||
c.translateBy(x: fittedRect.midX, y: fittedRect.midY)
|
c.translateBy(x: fittedRect.midX, y: fittedRect.midY)
|
||||||
c.scaleBy(x: 1.0, y: -1.0)
|
c.scaleBy(x: 1.0, y: -1.0)
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Binary file not shown.
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 17 KiB |
@ -13,14 +13,16 @@ class WalletInfoEmptyItem: ListViewItem {
|
|||||||
let theme: PresentationTheme
|
let theme: PresentationTheme
|
||||||
let strings: PresentationStrings
|
let strings: PresentationStrings
|
||||||
let address: String
|
let address: String
|
||||||
|
let displayAddressContextMenu: (ASDisplayNode, CGRect) -> Void
|
||||||
|
|
||||||
let selectable: Bool = false
|
let selectable: Bool = false
|
||||||
|
|
||||||
init(account: Account, theme: PresentationTheme, strings: PresentationStrings, address: String) {
|
init(account: Account, theme: PresentationTheme, strings: PresentationStrings, address: String, displayAddressContextMenu: @escaping (ASDisplayNode, CGRect) -> Void) {
|
||||||
self.account = account
|
self.account = account
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
self.address = address
|
self.address = address
|
||||||
|
self.displayAddressContextMenu = displayAddressContextMenu
|
||||||
}
|
}
|
||||||
|
|
||||||
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
|
||||||
@ -62,6 +64,8 @@ final class WalletInfoEmptyItemNode: ListViewItemNode {
|
|||||||
private let textNode: TextNode
|
private let textNode: TextNode
|
||||||
private let addressNode: TextNode
|
private let addressNode: TextNode
|
||||||
|
|
||||||
|
private var item: WalletInfoEmptyItem?
|
||||||
|
|
||||||
init(account: Account) {
|
init(account: Account) {
|
||||||
self.offsetContainer = ASDisplayNode()
|
self.offsetContainer = ASDisplayNode()
|
||||||
|
|
||||||
@ -88,6 +92,32 @@ final class WalletInfoEmptyItemNode: ListViewItemNode {
|
|||||||
self.addSubnode(self.offsetContainer)
|
self.addSubnode(self.offsetContainer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func didLoad() {
|
||||||
|
super.didLoad()
|
||||||
|
|
||||||
|
let recognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.tapLongTapOrDoubleTapGesture(_:)))
|
||||||
|
recognizer.tapActionAtPoint = { [weak self] point in
|
||||||
|
return .waitForSingleTap
|
||||||
|
}
|
||||||
|
self.addressNode.view.addGestureRecognizer(recognizer)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func tapLongTapOrDoubleTapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
|
||||||
|
switch recognizer.state {
|
||||||
|
case .ended:
|
||||||
|
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
|
||||||
|
switch gesture {
|
||||||
|
case .longTap:
|
||||||
|
self.item?.displayAddressContextMenu(self, self.addressNode.frame)
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) {
|
override func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) {
|
||||||
let layout = self.asyncLayout()
|
let layout = self.asyncLayout()
|
||||||
let (_, apply) = layout(item as! WalletInfoEmptyItem, params)
|
let (_, apply) = layout(item as! WalletInfoEmptyItem, params)
|
||||||
@ -139,6 +169,7 @@ final class WalletInfoEmptyItemNode: ListViewItemNode {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
strongSelf.item = item
|
||||||
|
|
||||||
strongSelf.offsetContainer.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
strongSelf.offsetContainer.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
||||||
|
|
||||||
|
@ -80,11 +80,11 @@ public final class WalletInfoScreen: ViewController {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.push(walletTransactionInfoController(context: strongSelf.context, tonContext: strongSelf.tonContext, walletInfo: strongSelf.walletInfo, walletTransaction: transaction))
|
strongSelf.push(walletTransactionInfoController(context: strongSelf.context, tonContext: strongSelf.tonContext, walletInfo: strongSelf.walletInfo, walletTransaction: transaction))
|
||||||
}, present: { [weak self] c in
|
}, present: { [weak self] c, a in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.present(c, in: .window(.root))
|
strongSelf.present(c, in: .window(.root), with: a)
|
||||||
})
|
})
|
||||||
|
|
||||||
self.displayNodeDidLoad()
|
self.displayNodeDidLoad()
|
||||||
@ -403,10 +403,12 @@ private enum WalletInfoListEntry: Equatable, Comparable, Identifiable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, action: @escaping (WalletTransaction) -> Void) -> ListViewItem {
|
func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, action: @escaping (WalletTransaction) -> Void, displayAddressContextMenu: @escaping (ASDisplayNode, CGRect) -> Void) -> ListViewItem {
|
||||||
switch self {
|
switch self {
|
||||||
case let .empty(address):
|
case let .empty(address):
|
||||||
return WalletInfoEmptyItem(account: account, theme: theme, strings: strings, address: address)
|
return WalletInfoEmptyItem(account: account, theme: theme, strings: strings, address: address, displayAddressContextMenu: { node, frame in
|
||||||
|
displayAddressContextMenu(node, frame)
|
||||||
|
})
|
||||||
case let .transaction(_, transaction):
|
case let .transaction(_, transaction):
|
||||||
return WalletInfoTransactionItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, walletTransaction: transaction, action: {
|
return WalletInfoTransactionItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, walletTransaction: transaction, action: {
|
||||||
action(transaction)
|
action(transaction)
|
||||||
@ -415,12 +417,12 @@ private enum WalletInfoListEntry: Equatable, Comparable, Identifiable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func preparedTransition(from fromEntries: [WalletInfoListEntry], to toEntries: [WalletInfoListEntry], account: Account, presentationData: PresentationData, action: @escaping (WalletTransaction) -> Void) -> WalletInfoListTransaction {
|
private func preparedTransition(from fromEntries: [WalletInfoListEntry], to toEntries: [WalletInfoListEntry], account: Account, presentationData: PresentationData, action: @escaping (WalletTransaction) -> Void, displayAddressContextMenu: @escaping (ASDisplayNode, CGRect) -> Void) -> WalletInfoListTransaction {
|
||||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||||
|
|
||||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||||
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, action: action), directionHint: nil) }
|
let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, action: action, displayAddressContextMenu: displayAddressContextMenu), directionHint: nil) }
|
||||||
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, action: action), directionHint: nil) }
|
let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, action: action, displayAddressContextMenu: displayAddressContextMenu), directionHint: nil) }
|
||||||
|
|
||||||
return WalletInfoListTransaction(deletions: deletions, insertions: insertions, updates: updates)
|
return WalletInfoListTransaction(deletions: deletions, insertions: insertions, updates: updates)
|
||||||
}
|
}
|
||||||
@ -433,7 +435,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
|||||||
private let address: String
|
private let address: String
|
||||||
|
|
||||||
private let openTransaction: (WalletTransaction) -> Void
|
private let openTransaction: (WalletTransaction) -> Void
|
||||||
private let present: (ViewController) -> Void
|
private let present: (ViewController, Any?) -> Void
|
||||||
|
|
||||||
private let hapticFeedback = HapticFeedback()
|
private let hapticFeedback = HapticFeedback()
|
||||||
|
|
||||||
@ -463,7 +465,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
|||||||
|
|
||||||
private var updateTimestampTimer: SwiftSignalKit.Timer?
|
private var updateTimestampTimer: SwiftSignalKit.Timer?
|
||||||
|
|
||||||
init(account: Account, tonContext: TonContext, presentationData: PresentationData, walletInfo: WalletInfo, address: String, sendAction: @escaping () -> Void, receiveAction: @escaping () -> Void, openTransaction: @escaping (WalletTransaction) -> Void, present: @escaping (ViewController) -> Void) {
|
init(account: Account, tonContext: TonContext, presentationData: PresentationData, walletInfo: WalletInfo, address: String, sendAction: @escaping () -> Void, receiveAction: @escaping () -> Void, openTransaction: @escaping (WalletTransaction) -> Void, present: @escaping (ViewController, Any?) -> Void) {
|
||||||
self.account = account
|
self.account = account
|
||||||
self.tonContext = tonContext
|
self.tonContext = tonContext
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
@ -740,7 +742,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
|||||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: "An Error Occurred", text: "The wallet state can not be retrieved at this time. Please try again later.", actions: [
|
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: "An Error Occurred", text: "The wallet state can not be retrieved at this time. Please try again later.", actions: [
|
||||||
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||||
})
|
})
|
||||||
], actionLayout: .vertical))
|
], actionLayout: .vertical), nil)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -821,6 +823,21 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.openTransaction(transaction)
|
strongSelf.openTransaction(transaction)
|
||||||
|
}, displayAddressContextMenu: { [weak self] node, frame in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let address = strongSelf.address
|
||||||
|
let contextMenuController = ContextMenuController(actions: [ContextMenuAction(content: .text(title: strongSelf.presentationData.strings.Conversation_ContextMenuCopy, accessibilityLabel: strongSelf.presentationData.strings.Conversation_ContextMenuCopy), action: {
|
||||||
|
UIPasteboard.general.string = address
|
||||||
|
})])
|
||||||
|
strongSelf.present(contextMenuController, ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self] in
|
||||||
|
if let strongSelf = self {
|
||||||
|
return (node, frame.insetBy(dx: 0.0, dy: -2.0), strongSelf, strongSelf.view.bounds)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}))
|
||||||
})
|
})
|
||||||
self.currentEntries = updatedEntries
|
self.currentEntries = updatedEntries
|
||||||
|
|
||||||
|
@ -100,14 +100,13 @@ public final class WalletQrScanScreen: ViewController {
|
|||||||
|
|
||||||
self.displayNodeDidLoad()
|
self.displayNodeDidLoad()
|
||||||
|
|
||||||
self.codeDisposable = (((self.displayNode as! WalletQrScanScreenNode).focusedCode.get()
|
self.codeDisposable = ((self.displayNode as! WalletQrScanScreenNode).focusedCode.get()
|
||||||
|> map { code -> String? in
|
|> map { code -> String? in
|
||||||
return code?.message
|
return code?.message
|
||||||
}
|
}
|
||||||
|> distinctUntilChanged
|
|> distinctUntilChanged
|
||||||
|> delay(2.5, queue: Queue.mainQueue()))
|
|
||||||
|> mapToSignal { code -> Signal<String?, NoError> in
|
|> mapToSignal { code -> Signal<String?, NoError> in
|
||||||
return .single(code)
|
return .single(code) |> delay(0.5, queue: Queue.mainQueue())
|
||||||
}).start(next: { [weak self] code in
|
}).start(next: { [weak self] code in
|
||||||
guard let strongSelf = self, let code = code else {
|
guard let strongSelf = self, let code = code else {
|
||||||
return
|
return
|
||||||
@ -270,13 +269,16 @@ private final class WalletQrScanScreenNode: ViewControllerTracingNode, UIScrollV
|
|||||||
|
|
||||||
let dimAlpha: CGFloat
|
let dimAlpha: CGFloat
|
||||||
let dimRect: CGRect
|
let dimRect: CGRect
|
||||||
|
let controlsAlpha: CGFloat
|
||||||
if let focusedRect = self.focusedRect {
|
if let focusedRect = self.focusedRect {
|
||||||
dimAlpha = 1.0
|
dimAlpha = 1.0
|
||||||
|
controlsAlpha = 0.0
|
||||||
let side = max(bounds.width * focusedRect.width, bounds.height * focusedRect.height) * 0.6
|
let side = max(bounds.width * focusedRect.width, bounds.height * focusedRect.height) * 0.6
|
||||||
let center = CGPoint(x: (1.0 - focusedRect.center.y) * bounds.width, y: focusedRect.center.x * bounds.height)
|
let center = CGPoint(x: (1.0 - focusedRect.center.y) * bounds.width, y: focusedRect.center.x * bounds.height)
|
||||||
dimRect = CGRect(x: center.x - side / 2.0, y: center.y - side / 2.0, width: side, height: side)
|
dimRect = CGRect(x: center.x - side / 2.0, y: center.y - side / 2.0, width: side, height: side)
|
||||||
} else {
|
} else {
|
||||||
dimAlpha = 0.625
|
dimAlpha = 0.625
|
||||||
|
controlsAlpha = 1.0
|
||||||
dimRect = CGRect(x: dimInset, y: dimHeight, width: layout.size.width - dimInset * 2.0, height: layout.size.height - dimHeight * 2.0)
|
dimRect = CGRect(x: dimInset, y: dimHeight, width: layout.size.width - dimInset * 2.0, height: layout.size.height - dimHeight * 2.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,6 +298,10 @@ private final class WalletQrScanScreenNode: ViewControllerTracingNode, UIScrollV
|
|||||||
transition.updateFrame(node: self.galleryButtonNode, frame: CGRect(origin: CGPoint(x: floor(layout.size.width / 2.0) - buttonSize.width - 28.0, y: dimHeight + frameSide + 50.0), size: buttonSize))
|
transition.updateFrame(node: self.galleryButtonNode, frame: CGRect(origin: CGPoint(x: floor(layout.size.width / 2.0) - buttonSize.width - 28.0, y: dimHeight + frameSide + 50.0), size: buttonSize))
|
||||||
transition.updateFrame(node: self.torchButtonNode, frame: CGRect(origin: CGPoint(x: floor(layout.size.width / 2.0) + 28.0, y: dimHeight + frameSide + 50.0), size: buttonSize))
|
transition.updateFrame(node: self.torchButtonNode, frame: CGRect(origin: CGPoint(x: floor(layout.size.width / 2.0) + 28.0, y: dimHeight + frameSide + 50.0), size: buttonSize))
|
||||||
|
|
||||||
|
transition.updateAlpha(node: self.titleNode, alpha: controlsAlpha)
|
||||||
|
transition.updateAlpha(node: self.galleryButtonNode, alpha: controlsAlpha)
|
||||||
|
transition.updateAlpha(node: self.torchButtonNode, alpha: controlsAlpha)
|
||||||
|
|
||||||
let titleSize = self.titleNode.updateLayout(CGSize(width: layout.size.width - sideInset * 2.0, height: layout.size.height))
|
let titleSize = self.titleNode.updateLayout(CGSize(width: layout.size.width - sideInset * 2.0, height: layout.size.height))
|
||||||
let titleFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - titleSize.width) / 2.0), y: dimHeight - titleSize.height - titleSpacing), size: titleSize)
|
let titleFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - titleSize.width) / 2.0), y: dimHeight - titleSize.height - titleSpacing), size: titleSize)
|
||||||
transition.updateFrameAdditive(node: self.titleNode, frame: titleFrame)
|
transition.updateFrameAdditive(node: self.titleNode, frame: titleFrame)
|
||||||
|
@ -63,11 +63,41 @@ func isValidAmount(_ amount: String) -> Bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let string = amount.replacingOccurrences(of: ",", with: ".")
|
||||||
|
if let range = string.range(of: ".") {
|
||||||
|
let integralPart = String(string[..<range.lowerBound])
|
||||||
|
let fractionalPart = String(string[range.upperBound...])
|
||||||
|
let string = integralPart + fractionalPart + String(repeating: "0", count: max(0, 9 - fractionalPart.count))
|
||||||
|
if let _ = Int64(string) {
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else if !string.isEmpty {
|
||||||
|
if let integral = Int64(string), integral <= maxIntegral {
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private let maxIntegral: Int64 = Int64.max / 1000000000
|
||||||
|
|
||||||
func amountValue(_ string: String) -> Int64 {
|
func amountValue(_ string: String) -> Int64 {
|
||||||
return Int64((Double(string.replacingOccurrences(of: ",", with: ".")) ?? 0.0) * 1000000000.0)
|
let string = string.replacingOccurrences(of: ",", with: ".")
|
||||||
|
if let range = string.range(of: ".") {
|
||||||
|
let integralPart = String(string[..<range.lowerBound])
|
||||||
|
let fractionalPart = String(string[range.upperBound...])
|
||||||
|
let string = integralPart + fractionalPart + String(repeating: "0", count: max(0, 9 - fractionalPart.count))
|
||||||
|
return Int64(string) ?? 0
|
||||||
|
} else if let integral = Int64(string) {
|
||||||
|
if integral > maxIntegral {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return integral * 1000000000
|
||||||
|
}
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func normalizedStringForGramsString(_ string: String, decimalSeparator: String = ".") -> String {
|
func normalizedStringForGramsString(_ string: String, decimalSeparator: String = ".") -> String {
|
||||||
|
@ -2706,7 +2706,7 @@ private final class WalletWordCheckScreenNode: ViewControllerTracingNode, UIScro
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if node === strongSelf.inputNodes.last {
|
if node.isLast {
|
||||||
if done {
|
if done {
|
||||||
action()
|
action()
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user