another.im-ios/Monal/Classes/ContactResources.swift
2024-11-18 15:53:52 +01:00

160 lines
9.4 KiB
Swift

//
// ContactResources.swift
// Monal
//
// Created by Friedrich Altheide on 24.12.21.
// Copyright © 2021 Monal.im. All rights reserved.
//
import OrderedCollections
@ViewBuilder
func resourceRowElement(_ k: String, _ v: some View, space: CGFloat = 5) -> some View {
HStack {
Text(k).font(.headline)
Spacer()
v.foregroundColor(.secondary)
}
}
struct ContactResources: View {
@StateObject var contact: ObservableKVOWrapper<MLContact>
@State var contactVersionInfos: [String:ObservableKVOWrapper<MLContactSoftwareVersionInfo>]
@State private var showCaps: String?
init(contact: ObservableKVOWrapper<MLContact>, previewMock: [String:ObservableKVOWrapper<MLContactSoftwareVersionInfo>]? = nil) {
_contact = StateObject(wrappedValue: contact)
if previewMock != nil {
self.contactVersionInfos = previewMock!
} else {
var tmpInfos:[String:ObservableKVOWrapper<MLContactSoftwareVersionInfo>] = [:]
for ressourceName in DataLayer.sharedInstance().resources(for: contact.obj) {
// load already known software version info from database
if let softwareInfo = DataLayer.sharedInstance().getSoftwareVersionInfo(forContact:contact.obj.contactJid, resource:ressourceName, andAccount:contact.obj.accountID) {
tmpInfos[ressourceName] = ObservableKVOWrapper<MLContactSoftwareVersionInfo>(softwareInfo)
}
}
self.contactVersionInfos = tmpInfos
}
}
var body: some View {
List {
ForEach(self.contactVersionInfos.sorted(by:{ $0.0 < $1.0 }), id: \.key) { key, versionInfo in
Section {
VStack {
resourceRowElement("Resource:", Text(versionInfo.resource as String))
resourceRowElement("Client Name:", Text(versionInfo.appName as String? ?? ""))
resourceRowElement("Client Version:", Text(versionInfo.appVersion as String? ?? ""))
resourceRowElement("OS:", Text(versionInfo.platformOs as String? ?? ""))
if let lastInteraction = versionInfo.lastInteraction as Date? {
if lastInteraction.timeIntervalSince1970 == 0 {
resourceRowElement("Last Interaction:", Text("Currently Online"))
} else {
resourceRowElement("Last Interaction:", Text(lastInteraction.formatted(date:.numeric, time:.standard)))
}
} else {
resourceRowElement("Last Interaction:", Text("unsupported"))
}
}
.onTapGesture(count: 2, perform: {
showCaps = versionInfo.resource
})
}
}
}
.richAlert(isPresented:$showCaps, title:Text("XMPP Capabilities")) { resource in
VStack(alignment: .leading) {
Text("The resource '\(resource)' has the following capabilities:")
.font(Font.body.weight(.semibold))
Spacer()
.frame(height: 20)
Section {
let capsVer = DataLayer.sharedInstance().getVerForUser(self.contact.contactJid, andResource:resource, onAccountID:self.contact.accountID)
Text("Caps hash: \(String(describing:capsVer))")
Divider()
if let capsSet = DataLayer.sharedInstance().getCapsforVer(capsVer, onAccountID:contact.obj.accountID) as? Set<String> {
let caps = Array(capsSet)
VStack(alignment: .leading) {
ForEach(caps, id: \.self) { cap in
Text(cap)
.font(.system(.footnote, design:.monospaced))
if cap != caps.last {
Divider()
}
}
}
}
}
}
}
.onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMonalXmppUserSoftWareVersionRefresh")).receive(on: RunLoop.main)) { notification in
if let xmppAccount = notification.object as? xmpp, let softwareInfo = notification.userInfo?["versionInfo"] as? MLContactSoftwareVersionInfo {
DDLogVerbose("Got software version info from account \(xmppAccount)...")
if softwareInfo.fromJid == contact.obj.contactJid && xmppAccount.accountID == contact.obj.accountID {
DispatchQueue.main.async {
DDLogVerbose("Successfully matched software version info update to current contact: \(contact.obj)")
self.contactVersionInfos[softwareInfo.resource ?? ""] = ObservableKVOWrapper<MLContactSoftwareVersionInfo>(softwareInfo)
}
}
}
}
.onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMonalNewPresenceNotice")).receive(on: RunLoop.main)) { notification in
if let xmppAccount = notification.object as? xmpp, let jid = notification.userInfo?["jid"] as? String, let resource = notification.userInfo?["resource"] as? String, let available = notification.userInfo?["available"] as? NSNumber {
DDLogVerbose("Got presence update from account \(xmppAccount)...")
if jid == contact.obj.contactJid && xmppAccount.accountID == contact.obj.accountID {
DispatchQueue.main.async {
DDLogVerbose("Successfully matched presence update to current contact: \(contact.obj)")
if available.boolValue {
if let softwareInfo = DataLayer.sharedInstance().getSoftwareVersionInfo(forContact:contact.obj.contactJid, resource:resource, andAccount:contact.obj.accountID) {
self.contactVersionInfos[resource] = ObservableKVOWrapper<MLContactSoftwareVersionInfo>(softwareInfo)
}
// query software version from contact
MLXMPPManager.sharedInstance().getEntitySoftWareVersion(for:contact.obj, andResource:resource)
} else {
self.contactVersionInfos[resource] = nil
}
}
}
}
}
.onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("kMonalLastInteractionUpdatedNotice")).receive(on: RunLoop.main)) { notification in
if let xmppAccount = notification.object as? xmpp, let jid = notification.userInfo?["jid"] as? String, let resource = notification.userInfo?["resource"] as? String, notification.userInfo?["lastInteraction"] as? NSDate != nil {
DDLogVerbose("Got lastInteraction update from account \(xmppAccount)...")
if jid == contact.obj.contactJid && xmppAccount.accountID == contact.obj.accountID {
DispatchQueue.main.async {
DDLogVerbose("Successfully matched lastInteraction update to current contact: \(contact.obj)")
self.contactVersionInfos[resource]?.obj.lastInteraction = DataLayer.sharedInstance().lastInteraction(ofJid:self.contact.obj.contactJid, andResource:resource, forAccountID:contact.obj.accountID)
}
}
}
}
.onAppear {
DDLogVerbose("View will appear...")
let newTimeout = DispatchTime.now() + 1.0;
DispatchQueue.main.asyncAfter(deadline: newTimeout) {
DDLogVerbose("Refreshing software version info...")
for ressourceName in DataLayer.sharedInstance().resources(for: contact.obj) {
// query software version from contact
MLXMPPManager.sharedInstance().getEntitySoftWareVersion(for:contact.obj, andResource:ressourceName)
}
}
}
.navigationBarTitle(Text("Devices of \(contact.contactDisplayName as String)"), displayMode: .inline)
}
}
func previewMock() -> [String:ObservableKVOWrapper<MLContactSoftwareVersionInfo>] {
var previewMock:[String:ObservableKVOWrapper<MLContactSoftwareVersionInfo>] = [:]
previewMock["m1"] = ObservableKVOWrapper<MLContactSoftwareVersionInfo>(MLContactSoftwareVersionInfo.init(jid: "test1@monal.im", andRessource: "m1", andAppName: "Monal", andAppVersion: "1.1.1", andPlatformOS: "ios", andLastInteraction: Date()))
previewMock["m2"] = ObservableKVOWrapper<MLContactSoftwareVersionInfo>(MLContactSoftwareVersionInfo.init(jid: "test1@monal.im", andRessource: "m2", andAppName: "Monal", andAppVersion: "1.1.2", andPlatformOS: "macOS", andLastInteraction: Date()))
previewMock["m3"] = ObservableKVOWrapper<MLContactSoftwareVersionInfo>(MLContactSoftwareVersionInfo.init(jid: "test1@monal.im", andRessource: "m3", andAppName: "Monal", andAppVersion: "1.1.2", andPlatformOS: "macOS", andLastInteraction: Date()))
return previewMock
}
struct ContactResources_Previews: PreviewProvider {
static var previews: some View {
ContactResources(contact:ObservableKVOWrapper<MLContact>(MLContact.makeDummyContact(0)), previewMock:previewMock())
}
}