wip
This commit is contained in:
parent
4c42b21559
commit
49f1028d69
|
@ -11,11 +11,11 @@ import UniformTypeIdentifiers
|
||||||
|
|
||||||
struct AddContactMenu: View {
|
struct AddContactMenu: View {
|
||||||
var delegate: SheetDismisserProtocol
|
var delegate: SheetDismisserProtocol
|
||||||
static private let jidFaultyPattern = "^([^@]+@)?.+(\\..{2,})?$"
|
private static let jidFaultyPattern = "^([^@]+@)?.+(\\..{2,})?$"
|
||||||
|
|
||||||
@State private var enabledAccounts: [xmpp]
|
@State private var enabledAccounts: [xmpp]
|
||||||
@State private var selectedAccount: Int
|
@State private var selectedAccount: Int
|
||||||
@State private var scannedFingerprints: [NSNumber:Data]? = nil
|
@State private var scannedFingerprints: [NSNumber: Data]? = nil
|
||||||
@State private var importScannedFingerprints: Bool = false
|
@State private var importScannedFingerprints: Bool = false
|
||||||
@State private var toAdd: String = ""
|
@State private var toAdd: String = ""
|
||||||
|
|
||||||
|
@ -23,43 +23,43 @@ struct AddContactMenu: View {
|
||||||
@State private var showAlert = false
|
@State private var showAlert = false
|
||||||
// note: dismissLabel is not accessed but defined at the .alert() section
|
// note: dismissLabel is not accessed but defined at the .alert() section
|
||||||
@State private var alertPrompt = AlertPrompt(dismissLabel: Text("Close"))
|
@State private var alertPrompt = AlertPrompt(dismissLabel: Text("Close"))
|
||||||
@State private var invitationResult: [String:AnyObject]? = nil
|
@State private var invitationResult: [String: AnyObject]? = nil
|
||||||
|
|
||||||
@StateObject private var overlay = LoadingOverlayState()
|
@StateObject private var overlay = LoadingOverlayState()
|
||||||
|
|
||||||
@State private var showQRCodeScanner = false
|
@State private var showQRCodeScanner = false
|
||||||
@State private var success = false
|
@State private var success = false
|
||||||
@State private var newContact : MLContact?
|
@State private var newContact: MLContact?
|
||||||
|
|
||||||
@State private var isEditingJid = false
|
@State private var isEditingJid = false
|
||||||
|
|
||||||
private let dismissWithNewContact: (MLContact) -> ()
|
private let dismissWithNewContact: (MLContact) -> Void
|
||||||
private let preauthToken: String?
|
private let preauthToken: String?
|
||||||
|
|
||||||
init(delegate: SheetDismisserProtocol, dismissWithNewContact: @escaping (MLContact) -> (), prefillJid: String = "", preauthToken:String? = nil, prefillAccount:xmpp? = nil, omemoFingerprints: [NSNumber:Data]? = nil) {
|
init(delegate: SheetDismisserProtocol, dismissWithNewContact: @escaping (MLContact) -> Void, prefillJid: String = "", preauthToken: String? = nil, prefillAccount: xmpp? = nil, omemoFingerprints: [NSNumber: Data]? = nil) {
|
||||||
self.delegate = delegate
|
self.delegate = delegate
|
||||||
self.dismissWithNewContact = dismissWithNewContact
|
self.dismissWithNewContact = dismissWithNewContact
|
||||||
//self.toAdd = State(wrappedValue: prefillJid)
|
// self.toAdd = State(wrappedValue: prefillJid)
|
||||||
self.toAdd = prefillJid
|
toAdd = prefillJid
|
||||||
self.preauthToken = preauthToken
|
self.preauthToken = preauthToken
|
||||||
//only display omemo ui part if there are any fingerprints (the checks below test for nil, not for 0)
|
// only display omemo ui part if there are any fingerprints (the checks below test for nil, not for 0)
|
||||||
if omemoFingerprints?.count ?? 0 > 0 {
|
if omemoFingerprints?.count ?? 0 > 0 {
|
||||||
self.scannedFingerprints = omemoFingerprints
|
scannedFingerprints = omemoFingerprints
|
||||||
}
|
}
|
||||||
|
|
||||||
let enabledAccounts = MLXMPPManager.sharedInstance().connectedXMPP as! [xmpp]
|
let enabledAccounts = MLXMPPManager.sharedInstance().connectedXMPP as! [xmpp]
|
||||||
self.enabledAccounts = enabledAccounts
|
self.enabledAccounts = enabledAccounts
|
||||||
self.selectedAccount = enabledAccounts.first != nil ? 0 : -1;
|
selectedAccount = enabledAccounts.first != nil ? 0 : -1
|
||||||
if let prefillAccount = prefillAccount {
|
if let prefillAccount = prefillAccount {
|
||||||
for index in enabledAccounts.indices {
|
for index in enabledAccounts.indices {
|
||||||
if enabledAccounts[index].accountID.isEqual(to:prefillAccount.accountID) {
|
if enabledAccounts[index].accountID.isEqual(to: prefillAccount.accountID) {
|
||||||
self.selectedAccount = index
|
selectedAccount = index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME duplicate code from WelcomeLogIn.swift, maybe move to SwiftuiHelpers
|
// FIXME: duplicate code from WelcomeLogIn.swift, maybe move to SwiftuiHelpers
|
||||||
private var toAddEmptyAlert: Bool {
|
private var toAddEmptyAlert: Bool {
|
||||||
alertPrompt.title = Text("No Empty Values!")
|
alertPrompt.title = Text("No Empty Values!")
|
||||||
alertPrompt.message = Text("Please make sure you have entered a valid jid.")
|
alertPrompt.message = Text("Please make sure you have entered a valid jid.")
|
||||||
|
@ -81,78 +81,78 @@ struct AddContactMenu: View {
|
||||||
private func successAlert(title: Text, message: Text) {
|
private func successAlert(title: Text, message: Text) {
|
||||||
alertPrompt.title = title
|
alertPrompt.title = title
|
||||||
alertPrompt.message = message
|
alertPrompt.message = message
|
||||||
self.success = true // < dismiss entire view on close
|
success = true // < dismiss entire view on close
|
||||||
showAlert = true
|
showAlert = true
|
||||||
}
|
}
|
||||||
|
|
||||||
private var toAddEmpty: Bool {
|
private var toAddEmpty: Bool {
|
||||||
return toAdd.isEmpty
|
toAdd.isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
private var toAddInvalid: Bool {
|
private var toAddInvalid: Bool {
|
||||||
return toAdd.range(of: AddContactMenu.jidFaultyPattern, options:.regularExpression) == nil
|
toAdd.range(of: AddContactMenu.jidFaultyPattern, options: .regularExpression) == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func trustFingerprints(_ fingerprints:[NSNumber:Data]?, for jid:String, on account:xmpp) {
|
func trustFingerprints(_ fingerprints: [NSNumber: Data]?, for jid: String, on account: xmpp) {
|
||||||
//we don't untrust other devices not included in here, because conversations only exports its own fingerprint
|
// we don't untrust other devices not included in here, because conversations only exports its own fingerprint
|
||||||
if let fingerprints = fingerprints {
|
if let fingerprints = fingerprints {
|
||||||
for (deviceId, fingerprint) in fingerprints {
|
for (deviceId, fingerprint) in fingerprints {
|
||||||
let address = SignalAddress.init(name:jid, deviceId:deviceId.int32Value)
|
let address = SignalAddress(name: jid, deviceId: deviceId.int32Value)
|
||||||
let knownDevices = Array(account.omemo.knownDevices(forAddressName:jid))
|
let knownDevices = Array(account.omemo.knownDevices(forAddressName: jid))
|
||||||
if !knownDevices.contains(deviceId) {
|
if !knownDevices.contains(deviceId) {
|
||||||
account.omemo.addIdentityManually(address, identityKey:fingerprint)
|
account.omemo.addIdentityManually(address, identityKey: fingerprint)
|
||||||
assert(account.omemo.getIdentityFor(address) == fingerprint, "The stored and created fingerprint should match")
|
assert(account.omemo.getIdentityFor(address) == fingerprint, "The stored and created fingerprint should match")
|
||||||
}
|
}
|
||||||
//trust device/fingerprint if fingerprints match
|
// trust device/fingerprint if fingerprints match
|
||||||
let knownFingerprintHex = HelperTools.signalHexKey(with:account.omemo.getIdentityFor(address))
|
let knownFingerprintHex = HelperTools.signalHexKey(with: account.omemo.getIdentityFor(address))
|
||||||
let addedFingerprintHex = HelperTools.signalHexKey(with:fingerprint)
|
let addedFingerprintHex = HelperTools.signalHexKey(with: fingerprint)
|
||||||
if knownFingerprintHex.uppercased() == addedFingerprintHex.uppercased() {
|
if knownFingerprintHex.uppercased() == addedFingerprintHex.uppercased() {
|
||||||
account.omemo.updateTrust(true, for:address)
|
account.omemo.updateTrust(true, for: address)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addJid(jid: String) {
|
func addJid(jid: String) {
|
||||||
let account = self.enabledAccounts[selectedAccount]
|
let account = enabledAccounts[selectedAccount]
|
||||||
let contact = MLContact.createContact(fromJid: jid, andAccountID: account.accountID)
|
let contact = MLContact.createContact(fromJid: jid, andAccountID: account.accountID)
|
||||||
if contact.isInRoster {
|
if contact.isInRoster {
|
||||||
self.newContact = contact
|
newContact = contact
|
||||||
//import omemo fingerprints as manually trusted, if requested
|
// import omemo fingerprints as manually trusted, if requested
|
||||||
trustFingerprints(self.importScannedFingerprints ? self.scannedFingerprints : [:], for:jid, on:account)
|
trustFingerprints(importScannedFingerprints ? scannedFingerprints : [:], for: jid, on: account)
|
||||||
//only alert of already known contact if we did not import the omemo fingerprints
|
// only alert of already known contact if we did not import the omemo fingerprints
|
||||||
if !self.importScannedFingerprints || self.scannedFingerprints?.count ?? 0 == 0 {
|
if !importScannedFingerprints || scannedFingerprints?.count ?? 0 == 0 {
|
||||||
if self.enabledAccounts.count > 1 {
|
if enabledAccounts.count > 1 {
|
||||||
self.success = true
|
success = true
|
||||||
successAlert(title: Text("Already present"), message: Text("This contact is already in the contact list of the selected account"))
|
successAlert(title: Text("Already present"), message: Text("This contact is already in the contact list of the selected account"))
|
||||||
} else {
|
} else {
|
||||||
self.success = true
|
success = true
|
||||||
successAlert(title: Text("Already present"), message: Text("This contact is already in your contact list"))
|
successAlert(title: Text("Already present"), message: Text("This contact is already in your contact list"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
showPromisingLoadingOverlay(overlay, headline:NSLocalizedString("Adding...", comment: ""), description:"") {
|
showPromisingLoadingOverlay(overlay, headline: NSLocalizedString("Adding...", comment: ""), description: "") {
|
||||||
account.checkJidType(jid)
|
account.checkJidType(jid)
|
||||||
}.done { type in
|
}.done { type in
|
||||||
let type = type as! String
|
let type = type as! String
|
||||||
if type == "account" {
|
if type == "account" {
|
||||||
let contact = MLContact.createContact(fromJid: jid, andAccountID: account.accountID)
|
let contact = MLContact.createContact(fromJid: jid, andAccountID: account.accountID)
|
||||||
self.newContact = contact
|
self.newContact = contact
|
||||||
MLXMPPManager.sharedInstance().add(contact, withPreauthToken:preauthToken)
|
MLXMPPManager.sharedInstance().add(contact, withPreauthToken: preauthToken)
|
||||||
//import omemo fingerprints as manually trusted, if requested
|
// import omemo fingerprints as manually trusted, if requested
|
||||||
trustFingerprints(self.importScannedFingerprints ? self.scannedFingerprints : [:], for:jid, on:account)
|
trustFingerprints(self.importScannedFingerprints ? self.scannedFingerprints : [:], for: jid, on: account)
|
||||||
successAlert(title: Text("Permission Requested"), message: Text("The new contact will be added to your contacts list when the person you've added has approved your request."))
|
successAlert(title: Text("Permission Requested"), message: Text("The new contact will be added to your contacts list when the person you've added has approved your request."))
|
||||||
} else if type == "muc" {
|
} else if type == "muc" {
|
||||||
showPromisingLoadingOverlay(overlay, headlineView:Text("Adding Group/Channel..."), descriptionView:Text("")) {
|
showPromisingLoadingOverlay(overlay, headlineView: Text("Adding Group/Channel..."), descriptionView: Text("")) {
|
||||||
promisifyMucAction(account:account, mucJid:jid) {
|
promisifyMucAction(account: account, mucJid: jid) {
|
||||||
account.joinMuc(jid)
|
account.joinMuc(jid)
|
||||||
}
|
}
|
||||||
}.done { _ in
|
}.done { _ in
|
||||||
self.newContact = MLContact.createContact(fromJid: jid, andAccountID: account.accountID)
|
self.newContact = MLContact.createContact(fromJid: jid, andAccountID: account.accountID)
|
||||||
successAlert(title: Text("Success!"), message: Text("Successfully joined group/channel \(jid)!"))
|
successAlert(title: Text("Success!"), message: Text("Successfully joined group/channel \(jid)!"))
|
||||||
}.catch { error in
|
}.catch { error in
|
||||||
errorAlert(title: Text("Error entering group/channel!"), message: Text("\(String(describing:error))"))
|
errorAlert(title: Text("Error entering group/channel!"), message: Text("\(String(describing: error))"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.catch { error in
|
}.catch { error in
|
||||||
|
@ -161,20 +161,18 @@ struct AddContactMenu: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
let account = self.enabledAccounts[selectedAccount]
|
let account = enabledAccounts[selectedAccount]
|
||||||
let splitJid = HelperTools.splitJid(account.connectionProperties.identity.jid)
|
let splitJid = HelperTools.splitJid(account.connectionProperties.identity.jid)
|
||||||
Form {
|
Form {
|
||||||
if enabledAccounts.isEmpty {
|
if enabledAccounts.isEmpty {
|
||||||
Text("Please make sure at least one account has connected before trying to add a contact or channel.")
|
Text("Please make sure at least one account has connected before trying to add a contact or channel.")
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
}
|
} else {
|
||||||
else
|
if !DataLayer.sharedInstance().allContactRequests().isEmpty {
|
||||||
{
|
|
||||||
if DataLayer.sharedInstance().allContactRequests().count > 0 {
|
|
||||||
ContactRequestsMenu()
|
ContactRequestsMenu()
|
||||||
}
|
}
|
||||||
|
|
||||||
Section(header:Text("Contact and Group/Channel Jids are usually in the format: name@domain.tld")) {
|
Section(header: Text("Contact and Group/Channel Jids are usually in the format: name@domain.tld")) {
|
||||||
if enabledAccounts.count > 1 {
|
if enabledAccounts.count > 1 {
|
||||||
Picker("Use account", selection: $selectedAccount) {
|
Picker("Use account", selection: $selectedAccount) {
|
||||||
ForEach(Array(self.enabledAccounts.enumerated()), id: \.element) { idx, account in
|
ForEach(Array(self.enabledAccounts.enumerated()), id: \.element) { idx, account in
|
||||||
|
@ -189,12 +187,12 @@ struct AddContactMenu: View {
|
||||||
.autocapitalization(.none)
|
.autocapitalization(.none)
|
||||||
.autocorrectionDisabled()
|
.autocorrectionDisabled()
|
||||||
.keyboardType(.emailAddress)
|
.keyboardType(.emailAddress)
|
||||||
.addClearButton(isEditing: isEditingJid, text:$toAdd)
|
.addClearButton(isEditing: isEditingJid, text: $toAdd)
|
||||||
.disabled(scannedFingerprints != nil)
|
.disabled(scannedFingerprints != nil)
|
||||||
.foregroundColor(scannedFingerprints != nil ? .secondary : .primary)
|
.foregroundColor(scannedFingerprints != nil ? .secondary : .primary)
|
||||||
.onChange(of: toAdd) { _ in toAdd = toAdd.replacingOccurrences(of: " ", with: "") }
|
.onChange(of: toAdd) { _ in toAdd = toAdd.replacingOccurrences(of: " ", with: "") }
|
||||||
|
|
||||||
if scannedFingerprints != nil && scannedFingerprints!.count > 0 {
|
if scannedFingerprints != nil && !scannedFingerprints!.isEmpty {
|
||||||
Section(header: Text("A contact was scanned through the QR code scanner")) {
|
Section(header: Text("A contact was scanned through the QR code scanner")) {
|
||||||
Toggle(isOn: $importScannedFingerprints) {
|
Toggle(isOn: $importScannedFingerprints) {
|
||||||
Text("Import and trust OMEMO fingerprints from QR code")
|
Text("Import and trust OMEMO fingerprints from QR code")
|
||||||
|
@ -237,7 +235,7 @@ struct AddContactMenu: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if DataLayer.sharedInstance().allContactRequests().count == 0 {
|
if DataLayer.sharedInstance().allContactRequests().isEmpty {
|
||||||
Section {
|
Section {
|
||||||
ContactRequestsMenu()
|
ContactRequestsMenu()
|
||||||
}
|
}
|
||||||
|
@ -246,7 +244,7 @@ struct AddContactMenu: View {
|
||||||
}
|
}
|
||||||
.padding()
|
.padding()
|
||||||
.alert(isPresented: $showAlert) {
|
.alert(isPresented: $showAlert) {
|
||||||
Alert(title: alertPrompt.title, message: alertPrompt.message, dismissButton:.default(Text("Close"), action: {
|
Alert(title: alertPrompt.title, message: alertPrompt.message, dismissButton: .default(Text("Close"), action: {
|
||||||
showAlert = false
|
showAlert = false
|
||||||
if self.success == true {
|
if self.success == true {
|
||||||
if self.newContact != nil {
|
if self.newContact != nil {
|
||||||
|
@ -257,7 +255,7 @@ struct AddContactMenu: View {
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
.richAlert(isPresented: $invitationResult, title:Text("Invitation for \(splitJid["host"]!) created")) { data in
|
.richAlert(isPresented: $invitationResult, title: Text("Invitation for \(splitJid["host"]!) created")) { data in
|
||||||
VStack {
|
VStack {
|
||||||
Image(uiImage: createQrCode(value: data["landing"] as! String))
|
Image(uiImage: createQrCode(value: data["landing"] as! String))
|
||||||
.interpolation(.none)
|
.interpolation(.none)
|
||||||
|
@ -266,15 +264,15 @@ struct AddContactMenu: View {
|
||||||
.aspectRatio(1, contentMode: .fit)
|
.aspectRatio(1, contentMode: .fit)
|
||||||
|
|
||||||
if let expires = data["expires"] as? Date {
|
if let expires = data["expires"] as? Date {
|
||||||
Text("This invitation will expire on \(expires.formatted(date:.numeric, time:.shortened))")
|
Text("This invitation will expire on \(expires.formatted(date: .numeric, time: .shortened))")
|
||||||
.font(.footnote)
|
.font(.footnote)
|
||||||
.multilineTextAlignment(.leading)
|
.multilineTextAlignment(.leading)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} buttons: { data in
|
} buttons: { data in
|
||||||
Button(action: {
|
Button(action: {
|
||||||
UIPasteboard.general.setValue(data["landing"] as! String, forPasteboardType:UTType.utf8PlainText.identifier as String)
|
UIPasteboard.general.setValue(data["landing"] as! String, forPasteboardType: UTType.utf8PlainText.identifier as String)
|
||||||
invitationResult = nil
|
invitationResult = nil
|
||||||
}) {
|
}) {
|
||||||
ShareLink("Share invitation link", item: URL(string: data["landing"] as! String)!)
|
ShareLink("Share invitation link", item: URL(string: data["landing"] as! String)!)
|
||||||
|
@ -309,17 +307,17 @@ struct AddContactMenu: View {
|
||||||
ToolbarItemGroup(placement: .navigationBarTrailing) {
|
ToolbarItemGroup(placement: .navigationBarTrailing) {
|
||||||
if account.connectionProperties.discoveredAdhocCommands["urn:xmpp:invite#invite"] != nil {
|
if account.connectionProperties.discoveredAdhocCommands["urn:xmpp:invite#invite"] != nil {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
DDLogVerbose("Trying to create invitation for: \(String(describing:splitJid["host"]!))")
|
DDLogVerbose("Trying to create invitation for: \(String(describing: splitJid["host"]!))")
|
||||||
showLoadingOverlay(overlay, headline: NSLocalizedString("Creating invitation...", comment: ""))
|
showLoadingOverlay(overlay, headline: NSLocalizedString("Creating invitation...", comment: ""))
|
||||||
account.createInvitation(completion: {
|
account.createInvitation(completion: {
|
||||||
let result = $0 as! [String:AnyObject]
|
let result = $0 as! [String: AnyObject]
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
hideLoadingOverlay(overlay)
|
hideLoadingOverlay(overlay)
|
||||||
DDLogVerbose("Got invitation result: \(String(describing:result))")
|
DDLogVerbose("Got invitation result: \(String(describing: result))")
|
||||||
if result["success"] as! Bool == true {
|
if result["success"] as! Bool == true {
|
||||||
invitationResult = result
|
invitationResult = result
|
||||||
} else {
|
} else {
|
||||||
errorAlert(title:Text("Failed to create invitation for \(splitJid["host"]!)"), message:Text(result["error"] as! String))
|
errorAlert(title: Text("Failed to create invitation for \(splitJid["host"]!)"), message: Text(result["error"] as! String))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -341,7 +339,7 @@ struct AddContactMenu: View {
|
||||||
struct AddContactMenu_Previews: PreviewProvider {
|
struct AddContactMenu_Previews: PreviewProvider {
|
||||||
static var delegate = SheetDismisserProtocol()
|
static var delegate = SheetDismisserProtocol()
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
AddContactMenu(delegate: delegate, dismissWithNewContact: { c in
|
AddContactMenu(delegate: delegate, dismissWithNewContact: { _ in
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,12 @@ import SwiftUI
|
||||||
struct ContactViewEntry: View {
|
struct ContactViewEntry: View {
|
||||||
private let contact: MLContact
|
private let contact: MLContact
|
||||||
@Binding private var selectedContactForContactDetails: ObservableKVOWrapper<MLContact>?
|
@Binding private var selectedContactForContactDetails: ObservableKVOWrapper<MLContact>?
|
||||||
private let dismissWithContact: (MLContact) -> ()
|
private let dismissWithContact: (MLContact) -> Void
|
||||||
|
|
||||||
@State private var shouldPresentRemoveContactAlert: Bool = false
|
@State private var shouldPresentRemoveContactAlert: Bool = false
|
||||||
|
|
||||||
private var removeContactButtonText: String {
|
private var removeContactButtonText: String {
|
||||||
if (!isDeletable) {
|
if !isDeletable {
|
||||||
return "Cannot delete notes to self"
|
return "Cannot delete notes to self"
|
||||||
}
|
}
|
||||||
return contact.isMuc ? "Remove Conversation" : "Remove Contact"
|
return contact.isMuc ? "Remove Conversation" : "Remove Contact"
|
||||||
|
@ -34,9 +34,9 @@ struct ContactViewEntry: View {
|
||||||
!contact.isSelfChat
|
!contact.isSelfChat
|
||||||
}
|
}
|
||||||
|
|
||||||
init (contact: MLContact, selectedContactForContactDetails: Binding<ObservableKVOWrapper<MLContact>?>, dismissWithContact: @escaping (MLContact) -> ()) {
|
init(contact: MLContact, selectedContactForContactDetails: Binding<ObservableKVOWrapper<MLContact>?>, dismissWithContact: @escaping (MLContact) -> Void) {
|
||||||
self.contact = contact
|
self.contact = contact
|
||||||
self._selectedContactForContactDetails = selectedContactForContactDetails
|
_selectedContactForContactDetails = selectedContactForContactDetails
|
||||||
self.dismissWithContact = dismissWithContact
|
self.dismissWithContact = dismissWithContact
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,26 +88,26 @@ struct ContactViewEntry: View {
|
||||||
struct ContactsView: View {
|
struct ContactsView: View {
|
||||||
@ObservedObject private var contacts: Contacts
|
@ObservedObject private var contacts: Contacts
|
||||||
private let delegate: SheetDismisserProtocol
|
private let delegate: SheetDismisserProtocol
|
||||||
private let dismissWithContact: (MLContact) -> ()
|
private let dismissWithContact: (MLContact) -> Void
|
||||||
|
|
||||||
@State private var searchText: String = ""
|
@State private var searchText: String = ""
|
||||||
@State private var selectedContactForContactDetails: ObservableKVOWrapper<MLContact>? = nil
|
@State private var selectedContactForContactDetails: ObservableKVOWrapper<MLContact>? = nil
|
||||||
|
|
||||||
init(contacts: Contacts, delegate: SheetDismisserProtocol, dismissWithContact: @escaping (MLContact) -> ()) {
|
init(contacts: Contacts, delegate: SheetDismisserProtocol, dismissWithContact: @escaping (MLContact) -> Void) {
|
||||||
self.contacts = contacts
|
self.contacts = contacts
|
||||||
self.delegate = delegate
|
self.delegate = delegate
|
||||||
self.dismissWithContact = dismissWithContact
|
self.dismissWithContact = dismissWithContact
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func shouldDisplayContact(contact: MLContact) -> Bool {
|
private static func shouldDisplayContact(contact: MLContact) -> Bool {
|
||||||
#if IS_QUICKSY
|
#if IS_QUICKSY
|
||||||
return true
|
return true
|
||||||
#endif
|
#endif
|
||||||
return contact.isSubscribedTo || contact.hasOutgoingContactRequest || contact.isSubscribedFrom
|
return contact.isSubscribedTo || contact.hasOutgoingContactRequest || contact.isSubscribedFrom
|
||||||
}
|
}
|
||||||
|
|
||||||
private var contactList: [MLContact] {
|
private var contactList: [MLContact] {
|
||||||
return contacts.contacts
|
contacts.contacts
|
||||||
.filter(ContactsView.shouldDisplayContact)
|
.filter(ContactsView.shouldDisplayContact)
|
||||||
.sorted { ContactsView.sortingCriteria($0) < ContactsView.sortingCriteria($1) }
|
.sorted { ContactsView.sortingCriteria($0) < ContactsView.sortingCriteria($1) }
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ struct ContactsView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func sortingCriteria(_ contact: MLContact) -> (String, String) {
|
private static func sortingCriteria(_ contact: MLContact) -> (String, String) {
|
||||||
return (contact.contactDisplayName.lowercased(), contact.contactJid.lowercased())
|
(contact.contactDisplayName.lowercased(), contact.contactJid.lowercased())
|
||||||
}
|
}
|
||||||
|
|
||||||
private func searchMatchesContact(contact: MLContact, search: String) -> Bool {
|
private func searchMatchesContact(contact: MLContact, search: String) -> Bool {
|
||||||
|
@ -164,7 +164,7 @@ struct ContactsView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.sheet(item: $selectedContactForContactDetails) { selectedContact in
|
.sheet(item: $selectedContactForContactDetails) { selectedContact in
|
||||||
AnyView(AddTopLevelNavigation(withDelegate: delegate, to: ContactDetails(delegate:delegate, contact:selectedContact)))
|
AnyView(AddTopLevelNavigation(withDelegate: delegate, to: ContactDetails(delegate: delegate, contact: selectedContact)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,20 +175,20 @@ class Contacts: ObservableObject {
|
||||||
private var subscriptions: Set<AnyCancellable> = Set()
|
private var subscriptions: Set<AnyCancellable> = Set()
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
self.contacts = Set(DataLayer.sharedInstance().contactList())
|
contacts = Set(DataLayer.sharedInstance().contactList())
|
||||||
self.requestCount = DataLayer.sharedInstance().allContactRequests().count
|
requestCount = DataLayer.sharedInstance().allContactRequests().count
|
||||||
subscriptions = [
|
subscriptions = [
|
||||||
NotificationCenter.default.publisher(for: NSNotification.Name("kMonalContactRemoved"))
|
NotificationCenter.default.publisher(for: NSNotification.Name("kMonalContactRemoved"))
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink() { _ in self.refreshContacts() },
|
.sink { _ in self.refreshContacts() },
|
||||||
NotificationCenter.default.publisher(for: NSNotification.Name("kMonalContactRefresh"))
|
NotificationCenter.default.publisher(for: NSNotification.Name("kMonalContactRefresh"))
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink() { _ in self.refreshContacts() }
|
.sink { _ in self.refreshContacts() }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
private func refreshContacts() {
|
private func refreshContacts() {
|
||||||
self.contacts = Set(DataLayer.sharedInstance().contactList())
|
contacts = Set(DataLayer.sharedInstance().contactList())
|
||||||
self.requestCount = DataLayer.sharedInstance().allContactRequests().count
|
requestCount = DataLayer.sharedInstance().allContactRequests().count
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,17 +123,7 @@ struct AddContactOrChannelScreen: View {
|
||||||
router.dismissModal()
|
router.dismissModal()
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
await wrapper.addContact(contactJid: contactJID, forAccountID: ownerAccount.id)
|
||||||
try await wrapper.addContact(contactJid: contactJID, forAccountID: ownerAccount.id)
|
router.dismissScreen()
|
||||||
router.dismissScreen()
|
|
||||||
} catch {
|
|
||||||
router.showAlert(
|
|
||||||
.alert,
|
|
||||||
title: L10n.Global.Error.title,
|
|
||||||
subtitle: L10n.Contacts.Add.serverError
|
|
||||||
) {
|
|
||||||
Button(L10n.Global.ok, role: .cancel) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ private struct ContactsScreenRow: View {
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
SharedListRow(
|
SharedListRow(
|
||||||
iconType: .charCircle(contact.name?.firstLetter ?? contact.contactJid.firstLetter),
|
iconType: .charCircle(contact.name ?? contact.contactJid),
|
||||||
text: contact.contactJid,
|
text: contact.contactJid,
|
||||||
controlType: .none
|
controlType: .none
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
enum AimErrors: Error {
|
enum AimErrors: Error {
|
||||||
case loginError
|
case loginError
|
||||||
case addContactError
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,6 @@ struct Contact: Identifiable {
|
||||||
init?(_ obj: MLContact) {
|
init?(_ obj: MLContact) {
|
||||||
ownerId = obj.accountID.intValue
|
ownerId = obj.accountID.intValue
|
||||||
contactJid = obj.contactJid
|
contactJid = obj.contactJid
|
||||||
name = obj.nickName
|
name = obj.nickName.isEmpty ? nil : obj.nickName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,15 +41,13 @@ extension MonalXmppWrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addContact(contactJid: String, forAccountID: Int) async throws {
|
func addContact(contactJid: String, forAccountID: Int) async {
|
||||||
_ = await Task { [weak self] in
|
await withCheckedContinuation { [weak self] cnt in
|
||||||
let result = self?.db.addContact(contactJid, forAccount: NSNumber(value: forAccountID), nickname: nil) ?? true
|
let contact = MLContact.createContact(fromJid: contactJid, andAccountID: NSNumber(value: forAccountID))
|
||||||
if result {
|
self?.xmpp.add(contact)
|
||||||
NotificationCenter.default.post(name: Notification.Name(kMonalContactRefresh), object: nil)
|
cnt.resume()
|
||||||
} else {
|
}
|
||||||
throw AimErrors.addContactError
|
NotificationCenter.default.post(name: Notification.Name(kMonalContactRefresh), object: nil)
|
||||||
}
|
|
||||||
}.result
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,6 +135,9 @@ private extension MonalXmppWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
func refreshContacts() {
|
func refreshContacts() {
|
||||||
contacts = db.contactList().compactMap { Contact($0) }
|
contacts = db.contactList()
|
||||||
|
.filter { $0.isSubscribedTo || $0.hasOutgoingContactRequest || $0.isSubscribedFrom }
|
||||||
|
.filter { !$0.isSelfChat } // removed for now
|
||||||
|
.compactMap { Contact($0) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue