|
@@ -628,15 +628,15 @@ public class EditorGroup: UIViewController, CLLocationManagerDelegate {
|
|
Database.shared.database?.inTransaction({ fmdb, rollback in
|
|
Database.shared.database?.inTransaction({ fmdb, rollback in
|
|
if let c = Database().getRecords(fmdb: fmdb, query: "select first_name || ' ' || last_name, image_id from BUDDY where f_pin = '\(f_pin)'"), c.next() {
|
|
if let c = Database().getRecords(fmdb: fmdb, query: "select first_name || ' ' || last_name, image_id from BUDDY where f_pin = '\(f_pin)'"), c.next() {
|
|
data["name"] = c.string(forColumnIndex: 0)!.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
data["name"] = c.string(forColumnIndex: 0)!.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
- data["image_id"] = c.string(forColumnIndex: 1)!
|
|
|
|
|
|
+ data["image_id"] = c.string(forColumnIndex: 1) ?? ""
|
|
c.close()
|
|
c.close()
|
|
}
|
|
}
|
|
else if let c = Database().getRecords(fmdb: fmdb, query: "select first_name || ' ' || last_name, thumb_id from GROUPZ_MEMBER where f_pin = '\(f_pin)' AND group_id = '\(dataGroup["group_id"]!!)'"), c.next() {
|
|
else if let c = Database().getRecords(fmdb: fmdb, query: "select first_name || ' ' || last_name, thumb_id from GROUPZ_MEMBER where f_pin = '\(f_pin)' AND group_id = '\(dataGroup["group_id"]!!)'"), c.next() {
|
|
data["name"] = c.string(forColumnIndex: 0)!.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
data["name"] = c.string(forColumnIndex: 0)!.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
- data["image_id"] = c.string(forColumnIndex: 1)!
|
|
|
|
|
|
+ data["image_id"] = c.string(forColumnIndex: 1) ?? ""
|
|
c.close()
|
|
c.close()
|
|
} else if let c = Database().getRecords(fmdb: fmdb, query: "select f_display_name from MESSAGE where message_id = '\(message_id)'"), c.next() {
|
|
} else if let c = Database().getRecords(fmdb: fmdb, query: "select f_display_name from MESSAGE where message_id = '\(message_id)'"), c.next() {
|
|
- data["name"] = c.string(forColumnIndex: 0)!
|
|
|
|
|
|
+ data["name"] = c.string(forColumnIndex: 0) ?? ""
|
|
data["image_id"] = ""
|
|
data["image_id"] = ""
|
|
c.close()
|
|
c.close()
|
|
} else if f_pin == "-997" {
|
|
} else if f_pin == "-997" {
|
|
@@ -1716,7 +1716,7 @@ public class EditorGroup: UIViewController, CLLocationManagerDelegate {
|
|
controller.data = sender.message_id
|
|
controller.data = sender.message_id
|
|
controller.flag = .me
|
|
controller.flag = .me
|
|
navigationController?.show(controller, sender: nil)
|
|
navigationController?.show(controller, sender: nil)
|
|
- } else {
|
|
|
|
|
|
+ } else if sender.message_id != "-999" && sender.message_id != "-997" {
|
|
let data = User.getDataCanNil(pin: sender.message_id)
|
|
let data = User.getDataCanNil(pin: sender.message_id)
|
|
if data != nil {
|
|
if data != nil {
|
|
let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "profileView") as! ProfileViewController
|
|
let controller = AppStoryBoard.Palio.instance.instantiateViewController(withIdentifier: "profileView") as! ProfileViewController
|
|
@@ -1937,12 +1937,19 @@ public class EditorGroup: UIViewController, CLLocationManagerDelegate {
|
|
reff_id = reffId!
|
|
reff_id = reffId!
|
|
}
|
|
}
|
|
var message_text = message_text
|
|
var message_text = message_text
|
|
- let bulletPoint = " • "
|
|
|
|
- let numberPattern = #" \d+\.\ "#
|
|
|
|
- let firstLine = message_text.components(separatedBy: .newlines).first ?? ""
|
|
|
|
|
|
+ message_text = message_text.replacingOccurrences(of: "\n •", with: "\n•")
|
|
|
|
+ if message_text.hasPrefix(" •") {
|
|
|
|
+ message_text = message_text.replacingOccurrences(of: " •", with: "•")
|
|
|
|
+ }
|
|
|
|
+ let regex = try! NSRegularExpression(pattern: #"(?m)^\s{2}([0-9]+\.)"#)
|
|
|
|
+ message_text = regex.stringByReplacingMatches(in: message_text,
|
|
|
|
+ options: [],
|
|
|
|
+ range: NSRange(location: 0, length: message_text.utf16.count),
|
|
|
|
+ withTemplate: "$1")
|
|
|
|
+
|
|
|
|
|
|
// Check if text contains bullet points or numbered list using regex
|
|
// Check if text contains bullet points or numbered list using regex
|
|
- if !message_text.isEmpty && !firstLine.contains(bulletPoint) && firstLine.range(of: numberPattern, options: .regularExpression) == nil {
|
|
|
|
|
|
+ if !message_text.isEmpty {
|
|
message_text = message_text.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
message_text = message_text.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
}
|
|
}
|
|
let idMe = User.getMyPin() as String?
|
|
let idMe = User.getMyPin() as String?
|
|
@@ -3039,28 +3046,27 @@ extension EditorGroup: UITextViewDelegate, CustomTextViewPasteDelegate {
|
|
|
|
|
|
//indention code:
|
|
//indention code:
|
|
let text = textView.text ?? ""
|
|
let text = textView.text ?? ""
|
|
- let cursorPositionIndent = textView.selectedRange.location
|
|
|
|
|
|
+ let cursorLocation = textView.selectedRange.location
|
|
|
|
|
|
- // Prevent moving cursor before the 2-space indent
|
|
|
|
- var adjustedCursorPosition = cursorPositionIndent
|
|
|
|
-
|
|
|
|
- for line in lines {
|
|
|
|
- if let range = text.range(of: line), NSRange(range, in: text).contains(cursorPositionIndent) {
|
|
|
|
- if line.hasPrefix(" •") || line.range(of: #"^\s{2}\d+\."#, options: .regularExpression) != nil {
|
|
|
|
- let startOfLine = text.distance(from: text.startIndex, to: range.lowerBound)
|
|
|
|
- let minCursorPosition = startOfLine + 2 // Prevent cursor before indentation
|
|
|
|
-
|
|
|
|
- if cursorPositionIndent < minCursorPosition {
|
|
|
|
- adjustedCursorPosition = minCursorPosition
|
|
|
|
|
|
+ // Find current line range where cursor is
|
|
|
|
+ if let lineRange = (text as NSString).lineRange(for: NSRange(location: cursorLocation, length: 0)) as NSRange? {
|
|
|
|
+ let line = (text as NSString).substring(with: lineRange)
|
|
|
|
+
|
|
|
|
+ // Detect bullet (" •") or numbered (" 1.") list
|
|
|
|
+ if line.hasPrefix(" •") || line.range(of: #"^\s{2}\d+\."#, options: .regularExpression) != nil {
|
|
|
|
+ var bulletEnd = lineRange.location + 2
|
|
|
|
+ if !line.hasPrefix(" •") {
|
|
|
|
+ bulletEnd = lineRange.location + 3
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Prevent cursor before bullet/number
|
|
|
|
+ if cursorLocation < bulletEnd {
|
|
|
|
+ DispatchQueue.main.async {
|
|
|
|
+ textView.selectedRange = NSRange(location: bulletEnd, length: 0)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- break
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
- if adjustedCursorPosition != cursorPositionIndent {
|
|
|
|
- textView.selectedRange = NSRange(location: adjustedCursorPosition, length: 0)
|
|
|
|
- }
|
|
|
|
}
|
|
}
|
|
|
|
|
|
func extractFromAtIfSymbolsBefore(_ text: String) -> String? {
|
|
func extractFromAtIfSymbolsBefore(_ text: String) -> String? {
|
|
@@ -3135,7 +3141,9 @@ extension EditorGroup: UITextViewDelegate, CustomTextViewPasteDelegate {
|
|
|
|
|
|
let newCursorPosition = cursorPosition + 2 // Adjust cursor position
|
|
let newCursorPosition = cursorPosition + 2 // Adjust cursor position
|
|
textView.text = replacedText
|
|
textView.text = replacedText
|
|
- textView.selectedRange = NSRange(location: newCursorPosition, length: 0)
|
|
|
|
|
|
+ DispatchQueue.main.async {
|
|
|
|
+ textView.selectedRange = NSRange(location: newCursorPosition, length: 0)
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -3144,12 +3152,11 @@ extension EditorGroup: UITextViewDelegate, CustomTextViewPasteDelegate {
|
|
if let match = text.range(of: numberPattern, options: .regularExpression) {
|
|
if let match = text.range(of: numberPattern, options: .regularExpression) {
|
|
let matchedText = text[match]
|
|
let matchedText = text[match]
|
|
|
|
|
|
- if let spaceIndex = matchedText.firstIndex(of: " ") {
|
|
|
|
- let firstLetter = matchedText[matchedText.index(after: spaceIndex)...]
|
|
|
|
- let replacedText = text.replacingOccurrences(of: matchedText, with: " \(matchedText)", range: match)
|
|
|
|
|
|
+ let replacedText = text.replacingOccurrences(of: matchedText, with: " \(matchedText)", range: match)
|
|
|
|
|
|
- let newCursorPosition = cursorPosition + 2 // Adjust cursor
|
|
|
|
- textView.text = replacedText
|
|
|
|
|
|
+ let newCursorPosition = cursorPosition + 2 // Adjust cursor
|
|
|
|
+ textView.text = replacedText
|
|
|
|
+ DispatchQueue.main.async {
|
|
textView.selectedRange = NSRange(location: newCursorPosition, length: 0)
|
|
textView.selectedRange = NSRange(location: newCursorPosition, length: 0)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -3521,17 +3528,6 @@ extension EditorGroup: UITextViewDelegate, CustomTextViewPasteDelegate {
|
|
guard affectedLineIndex >= 0, affectedLineIndex < lines.count else { return true }
|
|
guard affectedLineIndex >= 0, affectedLineIndex < lines.count else { return true }
|
|
|
|
|
|
let affectedLine = lines[affectedLineIndex]
|
|
let affectedLine = lines[affectedLineIndex]
|
|
-
|
|
|
|
- // Prevent deleting two-space indentation before bullet/number
|
|
|
|
- if affectedLine.hasPrefix(" •") || affectedLine.range(of: #"^\s{2}\d+\."#, options: .regularExpression) != nil {
|
|
|
|
- if let lineStart = textView.text.range(of: affectedLine)?.lowerBound,
|
|
|
|
- let startIndex = textView.text.distance(of: lineStart) {
|
|
|
|
- if range.location == startIndex || range.location == startIndex + 1 {
|
|
|
|
- return false
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
// Auto-indent new lines based on previous line
|
|
// Auto-indent new lines based on previous line
|
|
if text == "\n" {
|
|
if text == "\n" {
|
|
let previousLine = lines[affectedLineIndex]
|
|
let previousLine = lines[affectedLineIndex]
|
|
@@ -3539,7 +3535,9 @@ extension EditorGroup: UITextViewDelegate, CustomTextViewPasteDelegate {
|
|
if previousLine.hasPrefix(" •") {
|
|
if previousLine.hasPrefix(" •") {
|
|
let newBullet = "\n • "
|
|
let newBullet = "\n • "
|
|
textView.text = nsText.replacingCharacters(in: range, with: newBullet)
|
|
textView.text = nsText.replacingCharacters(in: range, with: newBullet)
|
|
- textView.selectedRange = NSRange(location: range.location + newBullet.utf16.count, length: 0)
|
|
|
|
|
|
+ DispatchQueue.main.async {
|
|
|
|
+ textView.selectedRange = NSRange(location: range.location + newBullet.utf16.count, length: 0)
|
|
|
|
+ }
|
|
return false
|
|
return false
|
|
}
|
|
}
|
|
|
|
|
|
@@ -3549,7 +3547,9 @@ extension EditorGroup: UITextViewDelegate, CustomTextViewPasteDelegate {
|
|
|
|
|
|
let newNumber = "\n \(number + 1). "
|
|
let newNumber = "\n \(number + 1). "
|
|
textView.text = nsText.replacingCharacters(in: range, with: newNumber)
|
|
textView.text = nsText.replacingCharacters(in: range, with: newNumber)
|
|
- textView.selectedRange = NSRange(location: range.location + newNumber.utf16.count, length: 0)
|
|
|
|
|
|
+ DispatchQueue.main.async {
|
|
|
|
+ textView.selectedRange = NSRange(location: range.location + newNumber.utf16.count, length: 0)
|
|
|
|
+ }
|
|
return false
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -3558,25 +3558,31 @@ extension EditorGroup: UITextViewDelegate, CustomTextViewPasteDelegate {
|
|
if text.isEmpty && affectedLine.trimmingCharacters(in: .whitespaces) == "•" {
|
|
if text.isEmpty && affectedLine.trimmingCharacters(in: .whitespaces) == "•" {
|
|
lines[affectedLineIndex] = "- " // Replace " • " with "- "
|
|
lines[affectedLineIndex] = "- " // Replace " • " with "- "
|
|
textView.text = lines.joined(separator: "\n")
|
|
textView.text = lines.joined(separator: "\n")
|
|
- textView.selectedRange = NSRange(location: range.location - 1, length: 0)
|
|
|
|
|
|
+ DispatchQueue.main.async {
|
|
|
|
+ textView.selectedRange = NSRange(location: range.location - 1, length: 0)
|
|
|
|
+ }
|
|
|
|
+ return false
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Handle Backspace on bullet
|
|
|
|
+ if text.isEmpty, newText.hasPrefix(" •"), newText.substring(with: NSRange(location: range.location - 1, length: 2)) == " •" {
|
|
return false
|
|
return false
|
|
}
|
|
}
|
|
|
|
|
|
// Handle Backspace on Numbered List
|
|
// Handle Backspace on Numbered List
|
|
- if text.isEmpty, affectedLine.range(of: #"^\s{2}(\d+)\.$"#, options: .regularExpression) != nil {
|
|
|
|
|
|
+ if text.isEmpty, newText.hasPrefix(" "), newText.substring(with: NSRange(location: range.location - 2, length: 2)) == " " {
|
|
lines[affectedLineIndex] = affectedLine.trimmingCharacters(in: .whitespaces)
|
|
lines[affectedLineIndex] = affectedLine.trimmingCharacters(in: .whitespaces)
|
|
textView.text = lines.joined(separator: "\n")
|
|
textView.text = lines.joined(separator: "\n")
|
|
- textView.selectedRange = NSRange(location: range.location - 1, length: 0)
|
|
|
|
|
|
+ DispatchQueue.main.async {
|
|
|
|
+ textView.selectedRange = NSRange(location: range.location - 2, length: 0)
|
|
|
|
+ }
|
|
return false
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
return true
|
|
}
|
|
}
|
|
|
|
|
|
private func handleRichText(_ textView: UITextView) {
|
|
private func handleRichText(_ textView: UITextView) {
|
|
- textView.preserveCursorPosition(withChanges: { _ in
|
|
|
|
- textView.attributedText = textView.text.richText(isEditing: true, group_id: self.dataGroup["group_id"] as? String ?? "", listMentionInTextField: self.listMentionInTextField)
|
|
|
|
- return .preserveCursor
|
|
|
|
- })
|
|
|
|
|
|
+ textView.attributedText = textView.text.richText(isEditing: true, group_id: self.dataGroup["group_id"] as? String ?? "", listMentionInTextField: self.listMentionInTextField)
|
|
}
|
|
}
|
|
|
|
|
|
public func textView(_ textView: UITextView, shouldInteractWith URL: URL?, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
|
|
public func textView(_ textView: UITextView, shouldInteractWith URL: URL?, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
|
|
@@ -6811,7 +6817,7 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource, AVAudioPlayer
|
|
containerReply.clipsToBounds = true
|
|
containerReply.clipsToBounds = true
|
|
|
|
|
|
if (thumbChat != "" || fileChat != "") && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as? String ?? "" != "1") {
|
|
if (thumbChat != "" || fileChat != "") && (dataMessages[indexPath.row]["lock"] == nil || dataMessages[indexPath.row]["lock"] as? String ?? "" != "1") {
|
|
- topMarginText = messageText.topAnchor.constraint(greaterThanOrEqualTo: containerMessage.topAnchor, constant: topMarginText.constant + 50 + (self.offset()*3))
|
|
|
|
|
|
+ topMarginText = messageText.topAnchor.constraint(equalTo: containerMessage.topAnchor, constant: topMarginText.constant + 50 + (self.offset()*3))
|
|
}
|
|
}
|
|
|
|
|
|
let leftReply = UIView()
|
|
let leftReply = UIView()
|
|
@@ -7627,11 +7633,40 @@ extension EditorGroup: UITableViewDelegate, UITableViewDataSource, AVAudioPlayer
|
|
if stringURl.lowercased().starts(with: "www.") {
|
|
if stringURl.lowercased().starts(with: "www.") {
|
|
stringURl = "https://" + stringURl.replacingOccurrences(of: "www.", with: "")
|
|
stringURl = "https://" + stringURl.replacingOccurrences(of: "www.", with: "")
|
|
}
|
|
}
|
|
- if Nexilis.checkingAccess(key: "secure_browser") {
|
|
|
|
- APIS.openUrl(url: stringURl)
|
|
|
|
- } else {
|
|
|
|
- guard let url = URL(string: stringURl) else { return }
|
|
|
|
- UIApplication.shared.open(url)
|
|
|
|
|
|
+ let app: UIApplication = UIApplication.shared
|
|
|
|
+ var appURL: URL? = nil
|
|
|
|
+ if let url = URL(string: stringURl) {
|
|
|
|
+ if url.host?.contains("instagram.com") == true {
|
|
|
|
+ // Convert to Instagram deep link
|
|
|
|
+ if let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
|
|
|
|
+ let path = components.path.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) {
|
|
|
|
+ appURL = URL(string: "instagram://\(path)")
|
|
|
|
+ }
|
|
|
|
+ } else if url.host?.contains("x.com") == true || url.host?.contains("twitter.com") == true {
|
|
|
|
+ if let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
|
|
|
|
+ let path = components.path.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) {
|
|
|
|
+ appURL = URL(string: "twitter://\(path)")
|
|
|
|
+ }
|
|
|
|
+ } else if url.host?.contains("youtube.com") == true || url.host?.contains("youtu.be") == true {
|
|
|
|
+ appURL = URL(string: "youtube://\(url.absoluteString)")
|
|
|
|
+ }
|
|
|
|
+ if let appURL = appURL, app.canOpenURL(appURL) {
|
|
|
|
+ app.open(appURL, options: [:]) { success in
|
|
|
|
+ if !success {
|
|
|
|
+ if Nexilis.checkingAccess(key: "secure_browser") {
|
|
|
|
+ APIS.openUrl(url: stringURl)
|
|
|
|
+ } else {
|
|
|
|
+ app.open(url)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ if Nexilis.checkingAccess(key: "secure_browser") {
|
|
|
|
+ APIS.openUrl(url: stringURl)
|
|
|
|
+ } else {
|
|
|
|
+ app.open(url)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|