diff --git a/.pyspelling.wordlist.txt b/.pyspelling.wordlist.txt index 39e0a129f..954e8b6c5 100644 --- a/.pyspelling.wordlist.txt +++ b/.pyspelling.wordlist.txt @@ -16,3 +16,4 @@ nextcloud CallKit Unban unban +Zammad diff --git a/NextcloudTalk.xcodeproj/project.pbxproj b/NextcloudTalk.xcodeproj/project.pbxproj index 5d7cde8b4..ef36bf82e 100644 --- a/NextcloudTalk.xcodeproj/project.pbxproj +++ b/NextcloudTalk.xcodeproj/project.pbxproj @@ -337,6 +337,8 @@ 1FF4DAAB2C0A114900C1B952 /* OcsResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FF4DAA92C0A114900C1B952 /* OcsResponse.swift */; }; 1FF4DAAC2C0A114900C1B952 /* OcsResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FF4DAA92C0A114900C1B952 /* OcsResponse.swift */; }; 1FF4DAAD2C0A114900C1B952 /* OcsResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FF4DAA92C0A114900C1B952 /* OcsResponse.swift */; }; + 1FFF41622C70937B00162F4D /* ReferenceZammadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FFF41612C70937B00162F4D /* ReferenceZammadView.swift */; }; + 1FFF41642C70938700162F4D /* ReferenceZammadView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1FFF41632C70938700162F4D /* ReferenceZammadView.xib */; }; 2C0574821EDD9E8E00D9E7F2 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C0574811EDD9E8E00D9E7F2 /* main.m */; }; 2C0574851EDD9E8E00D9E7F2 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C0574841EDD9E8E00D9E7F2 /* AppDelegate.m */; }; 2C05748E1EDD9E8E00D9E7F2 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2C05748C1EDD9E8E00D9E7F2 /* Main.storyboard */; }; @@ -815,6 +817,8 @@ 1FF4DAA52C08D81D00C1B952 /* UnitNCChatMessageTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnitNCChatMessageTest.swift; sourceTree = ""; }; 1FF4DAA72C08DE3A00C1B952 /* UnitNCRoomsManagerTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnitNCRoomsManagerTest.swift; sourceTree = ""; }; 1FF4DAA92C0A114900C1B952 /* OcsResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OcsResponse.swift; sourceTree = ""; }; + 1FFF41612C70937B00162F4D /* ReferenceZammadView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReferenceZammadView.swift; sourceTree = ""; }; + 1FFF41632C70938700162F4D /* ReferenceZammadView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ReferenceZammadView.xib; sourceTree = ""; }; 2C05747D1EDD9E8E00D9E7F2 /* NextcloudTalk.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NextcloudTalk.app; sourceTree = BUILT_PRODUCTS_DIR; }; 2C0574811EDD9E8E00D9E7F2 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 2C0574831EDD9E8E00D9E7F2 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; @@ -1323,6 +1327,8 @@ 1F46CE2A28E05B3C00E7D88E /* ReferenceDefaultView.xib */, 1F24B5A128E0648600654457 /* ReferenceGithubView.swift */, 1F24B5A328E0649200654457 /* ReferenceGithubView.xib */, + 1FFF41612C70937B00162F4D /* ReferenceZammadView.swift */, + 1FFF41632C70938700162F4D /* ReferenceZammadView.xib */, 1FEC459D2A02BCB900A636AA /* ReferenceGithubPermalinkView.swift */, 1FEC459B2A02BCAE00A636AA /* ReferenceGithubPermalinkView.xib */, 1FE0C56D2A0531270083576A /* ReferenceTalkView.swift */, @@ -2442,6 +2448,7 @@ 2C05748E1EDD9E8E00D9E7F2 /* Main.storyboard in Resources */, 1F785DDE2707865F00AC4B40 /* VoiceMessageTranscribeViewController.xib in Resources */, 2C4CDCCD269618240023F403 /* RoomDescriptionTableViewCell.xib in Resources */, + 1FFF41642C70938700162F4D /* ReferenceZammadView.xib in Resources */, 2CB6ACBF26385A3800D3D641 /* ShareLocationViewController.xib in Resources */, 2C5BFBFC2891598A00E75118 /* PollResultTableViewCell.xib in Resources */, 1FADECDA2B8227B1007AD94B /* FederationInvitationCell.xib in Resources */, @@ -2954,6 +2961,7 @@ 2C4446D32658147900DF1DBC /* TalkAccount.m in Sources */, 2CA1CCD61F1E664C002FE6A2 /* ContactsTableViewCell.m in Sources */, 1F77A6172AB9B161007B6037 /* ScreenCapturer.m in Sources */, + 1FFF41622C70937B00162F4D /* ReferenceZammadView.swift in Sources */, 1F98DF9C28E7484700E05174 /* ReferenceDeckView.swift in Sources */, DA66582F27B6B19C00B46B11 /* UserProfileTableViewController+Actions.swift in Sources */, 2C6E7449238C1A0800AE396C /* QuotedMessageView.m in Sources */, diff --git a/NextcloudTalk/Images.xcassets/zammad-comment.imageset/Contents.json b/NextcloudTalk/Images.xcassets/zammad-comment.imageset/Contents.json new file mode 100644 index 000000000..fa8be8a23 --- /dev/null +++ b/NextcloudTalk/Images.xcassets/zammad-comment.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "zammad-comment.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/NextcloudTalk/Images.xcassets/zammad-comment.imageset/zammad-comment.svg b/NextcloudTalk/Images.xcassets/zammad-comment.imageset/zammad-comment.svg new file mode 100644 index 000000000..401f8463c --- /dev/null +++ b/NextcloudTalk/Images.xcassets/zammad-comment.imageset/zammad-comment.svg @@ -0,0 +1,3 @@ + + + diff --git a/NextcloudTalk/Images.xcassets/zammad.imageset/Contents.json b/NextcloudTalk/Images.xcassets/zammad.imageset/Contents.json new file mode 100644 index 000000000..c8f08304e --- /dev/null +++ b/NextcloudTalk/Images.xcassets/zammad.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "zammad.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/NextcloudTalk/Images.xcassets/zammad.imageset/zammad.svg b/NextcloudTalk/Images.xcassets/zammad.imageset/zammad.svg new file mode 100644 index 000000000..7406cdf5d --- /dev/null +++ b/NextcloudTalk/Images.xcassets/zammad.imageset/zammad.svg @@ -0,0 +1,3 @@ + + + diff --git a/NextcloudTalk/ReferenceView.swift b/NextcloudTalk/ReferenceView.swift index 2ccb31e4d..2cb97e1cb 100644 --- a/NextcloudTalk/ReferenceView.swift +++ b/NextcloudTalk/ReferenceView.swift @@ -113,6 +113,16 @@ class ReferenceView: UIView { referenceView.addSubview(githubPermalinkView) foundReferenceView = true + } else if richObjectType == "integration_zammad", + let reference = firstReference["richObject"] as? [String: AnyObject] { + + let zammadView = ReferenceZammadView(frame: self.frame) + zammadView.update(for: reference, and: url) + zammadView.frame = self.bounds + zammadView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + + referenceView.addSubview(zammadView) + foundReferenceView = true } else if richObjectType == "deck-card", let reference = firstReference["richObject"] as? [String: AnyObject] { diff --git a/NextcloudTalk/ReferenceZammadView.swift b/NextcloudTalk/ReferenceZammadView.swift new file mode 100644 index 000000000..aafa11f86 --- /dev/null +++ b/NextcloudTalk/ReferenceZammadView.swift @@ -0,0 +1,109 @@ +// +// SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors +// SPDX-License-Identifier: GPL-3.0-or-later +// + +import Foundation + +@objcMembers class ReferenceZammadView: UIView { + + @IBOutlet var contentView: UIView! + @IBOutlet weak var referenceTypeIcon: UIImageView! + @IBOutlet weak var referenceTitle: UILabel! + @IBOutlet weak var referenceBody: UITextView! + @IBOutlet weak var referenceCommentCount: UILabel! + @IBOutlet weak var referenceCommentIcon: UIImageView! + + var url: String? + + override init(frame: CGRect) { + super.init(frame: frame) + commonInit() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + commonInit() + } + + func commonInit() { + Bundle.main.loadNibNamed("ReferenceZammadView", owner: self, options: nil) + contentView.frame = self.bounds + contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + + referenceTitle.text = "" + referenceBody.text = "" + + // Remove padding from textView and adjust lineBreakMode + referenceBody.textContainerInset = .zero + referenceBody.textContainer.lineFragmentPadding = .zero + referenceBody.textContainer.lineBreakMode = .byTruncatingTail + referenceBody.textContainer.maximumNumberOfLines = 3 + + let tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTap)) + contentView.addGestureRecognizer(tap) + + self.addSubview(contentView) + } + + func handleTap() { + if let url = url { + NCUtils.openLinkInBrowser(link: url) + } + } + + func update(for reference: [String: AnyObject], and url: String) { + self.url = url + + if reference["error"] != nil { + referenceTitle.text = NSLocalizedString("Zammad API error", comment: "'Zammad' is a product name") + + if let error = reference["error"] as? String, !error.isEmpty { + referenceBody.text = error + } else { + referenceBody.text = NSLocalizedString("Unknown error", comment: "") + } + + referenceCommentCount.isHidden = true + referenceCommentIcon.isHidden = true + + return + } + + referenceCommentCount.isHidden = false + referenceCommentIcon.isHidden = false + + if let comments = reference["article_count"] as? Int { + referenceCommentCount.text = String(comments) + } else { + referenceCommentCount.text = "0" + } + + if let title = reference["title"] as? String { + referenceTitle.text = title + } + + if let ticketNumber = reference["number"] as? String, let severity = reference["severity"] as? String, + let authorOrg = reference["zammad_ticket_author_organization"] as? [AnyHashable: Any], let authorName = authorOrg["name"] as? String { + + // Make sure we truncate each body line indiviually + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.lineBreakMode = .byTruncatingTail + + var bodyText = "#\(ticketNumber) [\(severity)]: \(authorName)".withParagraphStyle(paragraphStyle) + + // When we are able to determine the ticket state -> add that to the body + if let zammadStates = reference["zammad_ticket_states"] as? [[AnyHashable: Any]], let ticketState = reference["state_id"] as? Int, + let zammadState = zammadStates.first(where: { $0["id"] as? Int == ticketState }), let stateName = zammadState["name"] as? String { + + bodyText.append("\n\(stateName)".withParagraphStyle(paragraphStyle)) + } + + bodyText = bodyText.withFont(referenceBody.font ?? .preferredFont(forTextStyle: .callout)).withTextColor(referenceBody.textColor ?? .secondaryLabel) + + referenceBody.attributedText = bodyText + } else { + referenceBody.text = NSLocalizedString("No description provided", comment: "") + } + } +} diff --git a/NextcloudTalk/ReferenceZammadView.xib b/NextcloudTalk/ReferenceZammadView.xib new file mode 100644 index 000000000..25f76a2a7 --- /dev/null +++ b/NextcloudTalk/ReferenceZammadView.xib @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NextcloudTalk/en.lproj/Localizable.strings b/NextcloudTalk/en.lproj/Localizable.strings index 6f2107a29..b7102546b 100644 --- a/NextcloudTalk/en.lproj/Localizable.strings +++ b/NextcloudTalk/en.lproj/Localizable.strings @@ -1915,6 +1915,9 @@ /* No comment provided by engineer. */ "Your postal address" = "Your postal address"; +/* 'Zammad' is a product name */ +"Zammad API error" = "Zammad API error"; + /* Please put {actor0} and %ld placeholders in the correct position on the translated text but do not translate them */ "{actor0} added you and %ld more participants" = "{actor0} added you and %ld more participants";