Files
ichmagmaus111andClaude Opus 5.5 0622057a4f Ghostgram: open-source release
Ghostgram for iOS, rebuilt from scratch on Telegram iOS 13.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 17:57:09 +01:00

163 lines
5.5 KiB
Swift

import Foundation
enum WalletSendInputMode: Equatable {
case gram
case fiat
}
func walletSendNormalizedDigits(_ text: String) -> String {
return String(text.unicodeScalars.map { scalar -> Character in
let character = Character(String(scalar))
if CharacterSet.decimalDigits.contains(scalar), let digit = character.wholeNumberValue {
return Character(String(digit))
}
return character
})
}
func walletSendNanograms(
text: String,
mode: WalletSendInputMode,
rate: Double?,
decimalSeparator: String
) -> Int64? {
guard !text.isEmpty else {
return 0
}
guard !decimalSeparator.isEmpty else { return nil }
let normalizedText = walletSendNormalizedDigits(text).replacingOccurrences(of: decimalSeparator, with: ".")
let parts = normalizedText.split(separator: ".", omittingEmptySubsequences: false)
guard parts.count <= 2,
parts.allSatisfy({ $0.utf8.allSatisfy({ (48 ... 57).contains($0) }) }) else {
return nil
}
switch mode {
case .gram:
let wholeText = parts.first.map(String.init) ?? ""
let whole: Int64
if wholeText.isEmpty {
whole = 0
} else if let value = Int64(wholeText) {
whole = value
} else {
return nil
}
guard whole >= 0 else {
return nil
}
let scale: Int64 = 1_000_000_000
let (scaledWhole, didOverflow) = whole.multipliedReportingOverflow(by: scale)
guard !didOverflow else {
return nil
}
var fractionalText = parts.count == 2 ? String(parts[1]) : ""
guard fractionalText.count <= 9 else {
return nil
}
fractionalText = fractionalText.padding(toLength: 9, withPad: "0", startingAt: 0)
guard let fractional = Int64(fractionalText) else { return nil }
let (result, didAddOverflow) = scaledWhole.addingReportingOverflow(fractional)
return didAddOverflow ? nil : result
case .fiat:
guard let rate, rate.isFinite, rate > 0.0,
let fiatValue = Double(normalizedText), fiatValue.isFinite, fiatValue >= 0.0 else {
return nil
}
let nanograms = fiatValue / rate * 1_000_000_000.0
let roundedNanograms = nanograms.rounded()
guard roundedNanograms.isFinite,
roundedNanograms >= 0.0,
roundedNanograms < Double(Int64.max) else {
return nil
}
return Int64(roundedNanograms)
}
}
func walletSendGroupedAmountText(_ text: String, decimalSeparator: String, groupingSeparator: String) -> String {
guard !groupingSeparator.isEmpty else {
return text
}
let integralEnd = text.range(of: decimalSeparator)?.lowerBound ?? text.endIndex
let integralPart = text[..<integralEnd]
let integralLength = integralPart.count
guard integralLength > 3 else {
return text
}
var result = ""
for (index, character) in integralPart.enumerated() {
if index > 0 && (integralLength - index) % 3 == 0 {
result.append(contentsOf: groupingSeparator)
}
result.append(character)
}
result.append(contentsOf: text[integralEnd...])
return result
}
struct WalletSendAmountEdit: Equatable {
let text: String
let selection: NSRange
}
func walletSendReplacingAmountText(
_ text: String,
range: NSRange,
replacement string: String,
mode: WalletSendInputMode,
rate: Double?,
decimalSeparator: String
) -> WalletSendAmountEdit? {
guard !decimalSeparator.isEmpty else { return nil }
var replacement = walletSendNormalizedDigits(string)
if replacement == "." || replacement == "," {
replacement = decimalSeparator
}
let previousText = text as NSString
guard range.location != NSNotFound, range.location >= 0, range.location <= previousText.length,
range.length >= 0, range.length <= previousText.length - range.location else { return nil }
var updatedText = previousText.replacingCharacters(in: range, with: replacement)
var selectionOffset = range.location + replacement.utf16.count
let allowedCharacters = CharacterSet(charactersIn: "0123456789" + decimalSeparator)
guard updatedText.unicodeScalars.allSatisfy({ allowedCharacters.contains($0) }) else {
return nil
}
guard updatedText.components(separatedBy: decimalSeparator).count <= 2 else {
return nil
}
let maximumFractionalDigits = mode == .gram ? 9 : 2
if let range = updatedText.range(of: decimalSeparator) {
let fractionalCount = updatedText[range.upperBound...].count
guard fractionalCount <= maximumFractionalDigits else {
return nil
}
}
if updatedText == decimalSeparator {
updatedText = "0" + decimalSeparator
selectionOffset += 1
}
if updatedText.count > 1 && updatedText.hasPrefix("0") && !updatedText.hasPrefix("0" + decimalSeparator) {
updatedText.removeFirst()
selectionOffset = max(0, selectionOffset - 1)
}
let shouldAppendDecimalSeparator = !replacement.isEmpty && updatedText == "0"
if shouldAppendDecimalSeparator {
updatedText += decimalSeparator
selectionOffset = updatedText.utf16.count
}
guard walletSendNanograms(
text: updatedText,
mode: mode,
rate: rate,
decimalSeparator: decimalSeparator
) != nil else {
return nil
}
return WalletSendAmountEdit(text: updatedText, selection: NSRange(location: selectionOffset, length: 0))
}