Merge with siskin@7411133

This commit is contained in:
Matthew Wild 2021-02-23 12:39:01 +00:00
commit d566ab6cfe
8 changed files with 171 additions and 126 deletions

View file

@ -167,6 +167,7 @@ public class Payload: Decodable {
public var nickname: String?;
public var message: String?;
public var sid: String?;
public var media: [String]?;
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self);
@ -176,6 +177,7 @@ public class Payload: Decodable {
nickname = try container.decodeIfPresent(String.self, forKey: .nickname);
message = try container.decodeIfPresent(String.self, forKey: .message);
sid = try container.decodeIfPresent(String.self, forKey: .sid)
media = try container.decodeIfPresent([String].self, forKey: .media);
// -- and so on...
}
@ -193,5 +195,6 @@ public class Payload: Decodable {
case nickname
case message
case sid
case media
}
}

View file

@ -512,7 +512,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
print("got decrypted data:", String(data: decoded, encoding: .utf8) as Any);
if let payload = try? JSONDecoder().decode(Payload.self, from: decoded) {
print("decoded payload successfully!");
if let sid = payload.sid {
// we require `media` to be present (even empty) in incoming push for jingle session initiation,
// so we can assume that if `media` is `nil` then this is a push for call termination
if let sid = payload.sid, payload.media == nil {
guard CallManager.isAvailable, CallManager.instance?.currentCall?.state ?? .ringing == .ringing else {
return;
}

View file

@ -193,7 +193,7 @@
<!--Navigation Controller-->
<scene sceneID="CJK-x4-axt">
<objects>
<navigationController storyboardIdentifier="RoomViewNavigationController" automaticallyAdjustsScrollViewInsets="NO" toolbarHidden="NO" id="Q49-7n-7nT" sceneMemberID="viewController">
<navigationController storyboardIdentifier="RoomViewNavigationController" automaticallyAdjustsScrollViewInsets="NO" id="Q49-7n-7nT" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" translucent="NO" id="C6i-6F-QcS">
<rect key="frame" x="0.0" y="44" width="414" height="44"/>
@ -206,7 +206,6 @@
</navigationBar>
<nil name="viewControllers"/>
<toolbar key="toolbar" opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="g7C-gM-aQF">
<rect key="frame" x="0.0" y="813" width="414" height="49"/>
<autoresizingMask key="autoresizingMask"/>
</toolbar>
<connections>

View file

@ -1054,8 +1054,8 @@
</scene>
</scenes>
<inferredMetricsTieBreakers>
<segue reference="xZo-9l-Nez"/>
<segue reference="IbT-RI-igU"/>
<segue reference="XRy-yU-VZ5"/>
<segue reference="j5J-Tn-QfR"/>
</inferredMetricsTieBreakers>
<resources>
<image name="defaultAvatar" width="100" height="100"/>
@ -1071,9 +1071,6 @@
<systemColor name="groupTableViewBackgroundColor">
<color red="0.94901960784313721" green="0.94901960784313721" blue="0.96862745098039216" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="groupTableViewBackgroundColor">
<color red="0.94901960784313721" green="0.94901960784313721" blue="0.96862745098039216" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="secondaryLabelColor">
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>

View file

@ -43,7 +43,11 @@ class ChannelCreateViewController: UITableViewController, ChannelSelectAccountAn
}
var kind: ChannelKind = .adhoc;
private var components: [ChannelsHelper.Component] = [];
private var components: [ChannelsHelper.Component] = [] {
didSet {
updateJoinButtonStatus();
}
}
private var invitationOnly: Bool = true;
private var useMix: Bool = false;
private var needRefresh = false;
@ -129,6 +133,7 @@ class ChannelCreateViewController: UITableViewController, ChannelSelectAccountAn
@objc func mixSwitchChanged(_ sender: UISwitch) {
useMix = sender.isOn;
updateJoinButtonStatus();
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
@ -169,9 +174,13 @@ class ChannelCreateViewController: UITableViewController, ChannelSelectAccountAn
}
@IBAction func textFieldChanged(_ sender: Any) {
updateJoinButtonStatus();
}
private func updateJoinButtonStatus() {
let name = self.channelNameField.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "";
let channelId = self.channelIdField.text?.trimmingCharacters(in: .whitespacesAndNewlines) ?? "";
self.joinButton.isEnabled = (!name.isEmpty) && (kind == .adhoc || !channelId.isEmpty);
self.joinButton.isEnabled = (!name.isEmpty) && (kind == .adhoc || !channelId.isEmpty) && self.components.contains(where: { $0.type == (useMix ? .mix : .muc) });
}
private func refresh() {
@ -193,6 +202,7 @@ class ChannelCreateViewController: UITableViewController, ChannelSelectAccountAn
}
}
self.tableView.reloadData();
self.updateJoinButtonStatus();
self.operationEnded();
}
})

View file

@ -142,7 +142,10 @@ class DataFormController: UITableViewController {
if errors.firstIndex(where: { (idx)->Bool in
return idx.row == indexPath.row && idx.section == indexPath.section
}) != nil {
let backgroundColor = cell.backgroundColor;
var backgroundColor = UIColor.white;
if #available(iOS 13.0, *) {
backgroundColor = UIColor.systemBackground;
}
UIView.animate(withDuration: 0.5, animations: {
//cell.backgroundColor = UIColor(red: 1.0, green: 0.5, blue: 0.5, alpha: 1);
cell.backgroundColor = UIColor(hue: 0, saturation: 0.7, brightness: 0.8, alpha: 1)
@ -163,7 +166,7 @@ class DataFormController: UITableViewController {
for (index, fieldName) in form!.visibleFieldNames.enumerated() {
if let field = form!.getField(named: fieldName)! as? ValidatableField {
if !field.valid {
errors.append(IndexPath(row: index, section: 0));
errors.append(IndexPath(row: 0, section: index + 1));
}
}
}

View file

@ -21,6 +21,14 @@
import UIKit
extension unichar: ExpressibleByUnicodeScalarLiteral {
public typealias UnicodeScalarLiteralType = UnicodeScalar
public init(unicodeScalarLiteral value: UnicodeScalar) {
self.init(value.value);
}
}
class Markdown {
static let quoteParagraphStyle: NSParagraphStyle = {
@ -68,56 +76,63 @@ class Markdown {
}
}
static let NEW_LINE: unichar = "\n";
static let GT_SIGN: unichar = ">";
static let SPACE: unichar = " ";
static let ASTERISK: unichar = "*";
static let UNDERSCORE: unichar = "_";
static let GRAVE_ACCENT: unichar = "`";
static let CR_SIGN: unichar = "\r";
static func applyStyling(attributedString msg: NSMutableAttributedString, font defFont: UIFont, showEmoticons: Bool) {
let stylingColor = UIColor.init(white: 0.5, alpha: 1.0);
var message = msg.string;
var message = msg.string as NSString;
var boldStart: String.Index? = nil;
var italicStart: String.Index? = nil;
var underlineStart: String.Index? = nil;
// var codeStart: String.Index? = nil;
// var codeCount: Int = 0;
var quoteStart: String.Index? = nil;
var boldStart: Int? = nil;
var italicStart: Int? = nil;
var underlineStart: Int? = nil;
var quoteStart: Int? = nil;
var quoteLevel = 0;
var idx = message.startIndex;
var idx = 0;
var canStart = true;
var wordIdx: String.Index? = showEmoticons ? message.startIndex : nil;
var wordIdx: Int? = showEmoticons ? 0 : nil;
msg.removeAttribute(.paragraphStyle, range: NSRange(location: 0, length: msg.length));
msg.addAttribute(.font, value: defFont, range: NSRange(location: 0, length: msg.length));
while idx != message.endIndex {
let c = message[idx];
while idx < message.length {
let c = message.character(at: idx);
switch c {
case ">":
if quoteStart == nil && idx == message.startIndex || message[message.index(before: idx)] == "\n" {
case GT_SIGN:
if quoteStart == nil && (idx == 0 || message.character(at: idx-1) == NEW_LINE) {
let start = idx;
while idx != message.endIndex, message[idx] == ">" {
idx = message.index(after: idx);
while idx < message.length, message.character(at: idx) == GT_SIGN {
idx = idx + 1;
}
if idx != message.endIndex && message[idx] == " " {
if idx < message.length && message.character(at: idx) == SPACE {
quoteStart = start;
quoteLevel = message.distance(from: start, to: idx)
msg.addAttribute(.foregroundColor, value: stylingColor, range: NSRange(start..<idx, in: message));
quoteLevel = idx - start;
msg.addAttribute(.foregroundColor, value: stylingColor, range: NSRange(location: start, length: idx - start));
} else {
idx = message.index(before: idx);
idx = idx - 1;
}
}
case "*":
let nidx = message.index(after: idx);
if nidx != message.endIndex, message[nidx] == "*" {
case ASTERISK:
let nidx = idx + 1;
if nidx < message.length, message.character(at: nidx) == ASTERISK {
if boldStart == nil {
if canStart {
boldStart = idx;
}
} else {
msg.addAttribute(.foregroundColor, value: stylingColor, range: NSRange(boldStart!...message.index(after: boldStart!), in: message));
msg.addAttribute(.foregroundColor, value: stylingColor, range: NSRange(idx...nidx, in: message));
msg.addAttribute(.foregroundColor, value: stylingColor, range: NSRange(location: boldStart!, length: (nidx+1) - idx));
msg.addAttribute(.foregroundColor, value: stylingColor, range: NSRange(location: idx, length: (nidx+1) - idx));
msg.enumerateAttribute(.font, in: NSRange(boldStart!...nidx, in: message), options: .init()) { (attr, range: NSRange, stop) -> Void in
msg.enumerateAttribute(.font, in: NSRange(location: boldStart!, length: (nidx+1) - boldStart!), options: .init()) { (attr, range: NSRange, stop) -> Void in
let font = attr as? UIFont;
let boldFont = Markdown.bold(font: font ?? defFont);
msg.addAttribute(.font, value: boldFont, range: range);
@ -133,112 +148,120 @@ class Markdown {
italicStart = idx;
}
} else {
msg.addAttribute(.foregroundColor, value: stylingColor, range: NSRange(italicStart!...italicStart!, in: message));
msg.addAttribute(.foregroundColor, value: stylingColor, range: NSRange(idx...idx, in: message));
msg.addAttribute(.foregroundColor, value: stylingColor, range: NSRange(location: italicStart!, length: 1));
msg.addAttribute(.foregroundColor, value: stylingColor, range: NSRange(location: idx, length: 1));
msg.enumerateAttribute(.font, in: NSRange(italicStart!...idx, in: message), options: .init()) { (attr, range: NSRange, stop) -> Void in
msg.enumerateAttribute(.font, in: NSRange(location: italicStart!, length: (idx+1) - italicStart!), options: .init()) { (attr, range: NSRange, stop) -> Void in
let font = attr as? UIFont;
let italicFont = Markdown.italic(font: font ?? defFont);
msg.addAttribute(.font, value: italicFont, range: range);
}
italicStart = nil;
}
canStart = true;
}
case "_":
case UNDERSCORE:
if underlineStart == nil {
if canStart {
underlineStart = idx;
}
} else {
msg.addAttribute(.foregroundColor, value: stylingColor, range: NSRange(underlineStart!...underlineStart!, in: message));
msg.addAttribute(.foregroundColor, value: stylingColor, range: NSRange(idx...idx, in: message));
msg.addAttribute(.foregroundColor, value: stylingColor, range: NSRange(location: underlineStart!, length: 1));
msg.addAttribute(.foregroundColor, value: stylingColor, range: NSRange(location: idx, length: 1));
msg.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: NSRange(underlineStart!...idx, in: message));
msg.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue, range: NSRange(location: underlineStart!, length: idx - underlineStart!));
underlineStart = nil;
}
canStart = true;
case "`":
case GRAVE_ACCENT:
// if codeStart == nil {
if canStart {
let codeStart = idx;
let isBlock = message.startIndex == idx || (message[message.index(before: idx)] == "\n") || (message.distance(from: message.startIndex, to: idx) > 3 && message[message.index(idx, offsetBy: -1)] == " " && message[message.index(idx, offsetBy: -2)] == ">" && (message.startIndex == message.index(idx, offsetBy: -3) || message[message.index(idx, offsetBy: -3)] == "\n"));
wordIdx = nil;
while idx != message.endIndex, message[idx] == "`" {
idx = message.index(after: idx);
}
let codeCount = message.distance(from: codeStart, to: idx);
print("code tag count = ", codeCount);
var count = 0;
while idx != message.endIndex {
if message[idx] == "`" {
count = count + 1;
if count == codeCount {
let tmp = message.index(after: idx);
if tmp == message.endIndex || [" ", "\n"].contains(message[tmp]) {
break;
}
}
} else {
count = 0;
}
idx = message.index(after: idx);
}
if codeCount != count {
idx = message.index(before: idx);
} else {
msg.addAttribute(.foregroundColor, value: stylingColor, range: NSRange(codeStart...message.index(codeStart, offsetBy: codeCount-1), in: message));
msg.addAttribute(.foregroundColor, value: stylingColor, range: NSRange(message.index(idx, offsetBy: codeCount * -1)...idx, in: message));
let codeFont = Markdown.code(font: defFont);
msg.addAttribute(.font, value: codeFont, range: NSRange(codeStart...idx, in: message));
if isBlock {
msg.addAttribute(.paragraphStyle, value: codeParagraphStyle, range: NSRange(codeStart...idx, in: message));
if canStart {
let codeStart = idx;
let isBlock = 0 == idx || (message.character(at: idx-1) == NEW_LINE) || (idx > 3 && message.length > (idx + 1) && message.character(at: idx + 1) == SPACE && message.character(at: idx-2) == GT_SIGN && (0 == idx - 3 || message.character(at: idx - 3) == NEW_LINE));
wordIdx = nil;
while idx < message.length, message.character(at: idx) == "`" {
idx = idx + 1;
}
if message.distance(from: codeStart, to: idx) > 1 {
let clearRange = NSRange(message.index(codeStart, offsetBy: codeCount)...message.index(idx, offsetBy: codeCount * -1), in: message);
msg.removeAttribute(.underlineStyle, range: clearRange);
//msg.addAttribute(.foregroundColor, value: textColor ?? NSColor.textColor, range: clearRange);
let codeCount = idx - codeStart;
print("code tag count = ", codeCount);
var count = 0;
while idx < message.length {
if message.character(at: idx) == GRAVE_ACCENT {
count = count + 1;
if count == codeCount {
let tmp = idx + 1;
if tmp == message.length || [" ", "\n"].contains(message.character(at: tmp)) {
break;
}
}
} else {
count = 0;
}
idx = idx + 1;
}
if idx == message.endIndex {
wordIdx = message.endIndex;
if codeCount != count {
idx = codeStart + codeCount;
} else {
wordIdx = message.index(after: idx);
msg.addAttribute(.foregroundColor, value: stylingColor, range: NSRange(location: codeStart, length: codeCount));
msg.addAttribute(.foregroundColor, value: stylingColor, range: NSRange(location: (idx+1)-codeCount, length: codeCount));
let codeFont = Markdown.code(font: defFont);
msg.addAttribute(.font, value: codeFont, range: NSRange(location: codeStart, length: idx - codeStart));
if isBlock {
msg.addAttribute(.paragraphStyle, value: codeParagraphStyle, range: NSRange(location: codeStart, length: idx - codeStart));
}
if idx - codeStart > 1 {
let clearRange = NSRange(location: codeStart + codeCount, length: idx - (codeStart + (2*codeCount)));
//msg.removeAttribute(.foregroundColor, range: clearRange);
msg.removeAttribute(.underlineStyle, range: clearRange);
//msg.addAttribute(.foregroundColor, value: textColor ?? NSColor.textColor, range: clearRange);
}
if idx == message.length {
wordIdx = message.length;
} else {
wordIdx = idx + 1;
}
}
}
}
// } else {
// }
canStart = true;
case "\r", "\n", " ":
case CR_SIGN, NEW_LINE, SPACE:
if showEmoticons {
if wordIdx != nil && wordIdx! != idx {
// something is wrong, it looks like IDX points to replaced value!
if let emoji = String.emojis[String(message[wordIdx!..<idx])] {
msg.replaceCharacters(in: NSRange(wordIdx!..<idx, in: message), with: emoji);
let distance = message.distance(from: message.startIndex, to: wordIdx!);
message.replaceSubrange(wordIdx!..<idx, with: emoji);
// we are changing offset as length is changing!!
// idx = message.index(wordIdx!, offsetBy: emoji.lengthOfBytes(using: .utf8)-3);
idx = message.index(after: message.index(message.startIndex, offsetBy: distance));
let range = NSRange(location: wordIdx!, length: idx - wordIdx!);
if let emoji = String.emojis[message.substring(with: range)] {
let len = message.length;
print("replacing:", range, "for:", emoji, "in:", msg, "range:", NSRange(location: 0, length: msg.length));
msg.replaceCharacters(in: range, with: emoji);
message = msg.string as NSString;
let diff = message.length - len;
idx = idx + diff;
}
}
if idx != message.endIndex {
wordIdx = message.index(after: idx);
if idx < message.length {
wordIdx = idx + 1;
} else {
wordIdx = message.endIndex;
wordIdx = message.length;
}
}
if "\n" == c {
if NEW_LINE == c {
boldStart = nil;
underlineStart = nil;
italicStart = nil
if (quoteStart != nil) {
msg.addAttribute(.paragraphStyle, value: Markdown.quoteParagraphStyle, range: NSRange(quoteStart!..<idx, in: message));
print("quote level:", quoteLevel);
if idx < message.length {
let range = NSRange(location: quoteStart!, length: idx - quoteStart!);
print("message possibly causing a crash:", message, "range:", range, "length:", message.length);
msg.addAttribute(.paragraphStyle, value: Markdown.quoteParagraphStyle, range: range);
}
quoteStart = nil;
}
}
@ -247,20 +270,21 @@ class Markdown {
canStart = false;
break;
}
if idx != message.endIndex {
idx = message.index(after: idx);
if idx < message.length {
idx = idx + 1;
}
}
if (quoteStart != nil) {
msg.addAttribute(.paragraphStyle, value: Markdown.quoteParagraphStyle, range: NSRange(quoteStart!..<idx, in: message));
msg.addAttribute(.paragraphStyle, value: Markdown.quoteParagraphStyle, range: NSRange(location: quoteStart!, length: idx - quoteStart!));
quoteStart = nil;
}
if showEmoticons && wordIdx != nil && wordIdx! != idx {
if let emoji = String.emojis[String(message[wordIdx!..<idx])] {
msg.replaceCharacters(in: NSRange(wordIdx!..<idx, in: message), with: emoji);
message.replaceSubrange(wordIdx!..<idx, with: emoji);
let range = NSRange(location: wordIdx!, length: idx - wordIdx!);
if let emoji = String.emojis[message.substring(with: range)] {
msg.replaceCharacters(in: range, with: emoji);
message = msg.string as NSString;
}
}

View file

@ -918,18 +918,25 @@ extension CallManager: PKPushRegistryDelegate {
print("got decrypted voip data:", String(data: decoded, encoding: .utf8) as Any);
if let payload = try? JSONDecoder().decode(VoIPPayload.self, from: decoded) {
print("decoded voip payload successfully!");
if let sender = payload.sender, let media = payload.media, let client = XmppService.instance.getClient(for: BareJID(account)) {
let session = JingleManager.instance.open(for: client.sessionObject, with: sender, sid: payload.sid, role: .responder, initiationType: .message);
let call = Call(account: BareJID(account), with: sender.bareJid, sid: payload.sid, direction: .incoming, media: media, sessionId: session?.id);
self.reportIncomingCall(call, completionHandler: { result in
switch result {
case .success(_):
break;
case .failure(_):
_ = session?.decline();
}
completion();
})
if let sender = payload.sender, let client = XmppService.instance.getClient(for: BareJID(account)) {
// we require `media` to be present (even empty) in incoming push for jingle session initiation
if let media = payload.media {
let session = JingleManager.instance.open(for: client.sessionObject, with: sender, sid: payload.sid, role: .responder, initiationType: .message);
let call = Call(account: BareJID(account), with: sender.bareJid, sid: payload.sid, direction: .incoming, media: media, sessionId: session?.id);
self.reportIncomingCall(call, completionHandler: { result in
switch result {
case .success(_):
break;
case .failure(_):
_ = session?.decline();
}
completion();
});
} else {
self.endCall(on: account, sid: payload.sid, completionHandler: {
print("ended call");
})
}
return;
}
}