diff --git a/Linphone.xcodeproj/project.pbxproj b/Linphone.xcodeproj/project.pbxproj index eaf0ae353..603952be3 100644 --- a/Linphone.xcodeproj/project.pbxproj +++ b/Linphone.xcodeproj/project.pbxproj @@ -7,7 +7,7 @@ objects = { /* Begin PBXBuildFile section */ - 4ED1F0A881A9ACB5977A8987 /* (null) in Frameworks */ = {isa = PBXBuildFile; }; + 4ED1F0A881A9ACB5977A8987 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; }; 660AAF7F2B839272004C0FA6 /* msgNotificationService.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 660AAF7B2B839271004C0FA6 /* msgNotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 660D8A712B517D260092694D /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 660D8A702B517D260092694D /* GoogleService-Info.plist */; }; 6613A0AE2BAEB7DF008923A4 /* MeetingFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6613A0AD2BAEB7DF008923A4 /* MeetingFragment.swift */; }; @@ -118,6 +118,7 @@ D76005F62B0798B00054B79A /* IntExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D76005F52B0798B00054B79A /* IntExtension.swift */; }; D7702EF22AC7205000557C00 /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7702EF12AC7205000557C00 /* WelcomeView.swift */; }; D777DBB32AE12C5900565A99 /* ContactsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D777DBB22AE12C5900565A99 /* ContactsManager.swift */; }; + D77A080E2CB6BCAF0095D589 /* MessageConferenceInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77A080D2CB6BCA10095D589 /* MessageConferenceInfo.swift */; }; D78290B82ADD3910004AA85C /* ContactsFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78290B72ADD3910004AA85C /* ContactsFragment.swift */; }; D78290BB2ADD40B2004AA85C /* ContactViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D78290BA2ADD40B2004AA85C /* ContactViewModel.swift */; }; D783C77C2B1089B200622CC2 /* assistant_linphone_default_values in Resources */ = {isa = PBXBuildFile; fileRef = D783C77A2B1089B200622CC2 /* assistant_linphone_default_values */; }; @@ -305,6 +306,7 @@ D76005F52B0798B00054B79A /* IntExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntExtension.swift; sourceTree = ""; }; D7702EF12AC7205000557C00 /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = ""; }; D777DBB22AE12C5900565A99 /* ContactsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsManager.swift; sourceTree = ""; }; + D77A080D2CB6BCA10095D589 /* MessageConferenceInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageConferenceInfo.swift; sourceTree = ""; }; D78290B72ADD3910004AA85C /* ContactsFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactsFragment.swift; sourceTree = ""; }; D78290BA2ADD40B2004AA85C /* ContactViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactViewModel.swift; sourceTree = ""; }; D783C77A2B1089B200622CC2 /* assistant_linphone_default_values */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = assistant_linphone_default_values; sourceTree = ""; }; @@ -373,7 +375,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 4ED1F0A881A9ACB5977A8987 /* (null) in Frameworks */, + 4ED1F0A881A9ACB5977A8987 /* BuildFile in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -482,6 +484,7 @@ D70959EF2B8DF33B0014AC0B /* Model */ = { isa = PBXGroup; children = ( + D77A080D2CB6BCA10095D589 /* MessageConferenceInfo.swift */, D70959F02B8DF3EC0014AC0B /* ConversationModel.swift */, D7E6ADF22B9875C20009A2BC /* Message.swift */, D7C2DA1C2CA44DE400A2441B /* EventModel.swift */, @@ -1215,6 +1218,7 @@ D7DA67642ACCB31700E95002 /* ProfileModeFragment.swift in Sources */, D7CEE03D2B7A23B200FD79B7 /* ConversationsListFragment.swift in Sources */, D74C9CFC2ACACF370021626A /* WelcomePage3Fragment.swift in Sources */, + D77A080E2CB6BCAF0095D589 /* MessageConferenceInfo.swift in Sources */, C6A5A9412C10B5D50070FEA4 /* EncodableExtension.swift in Sources */, D719ABCC2ABC769C00B41C10 /* AssistantView.swift in Sources */, C62817342C1C7C7400DBA646 /* HelpView.swift in Sources */, diff --git a/Linphone/Localizable.xcstrings b/Linphone/Localizable.xcstrings index f225dd1d9..622a43465 100644 --- a/Linphone/Localizable.xcstrings +++ b/Linphone/Localizable.xcstrings @@ -1817,6 +1817,9 @@ }, "Meeting added to iPhone calendar" : { + }, + "Meeting invite !!" : { + }, "Meetings" : { diff --git a/Linphone/UI/Main/Conversations/Fragments/ChatBubbleView.swift b/Linphone/UI/Main/Conversations/Fragments/ChatBubbleView.swift index 99e159d18..b69e85d1e 100644 --- a/Linphone/UI/Main/Conversations/Fragments/ChatBubbleView.swift +++ b/Linphone/UI/Main/Conversations/Fragments/ChatBubbleView.swift @@ -302,6 +302,10 @@ struct ChatBubbleView: View { }) .padding(.leading, eventLogMessage.message.isOutgoing ? 40 : 0) .padding(.trailing, !eventLogMessage.message.isOutgoing ? 40 : 0) + } else if eventLogMessage.message.isIcalendar { + Text("Meeting invite !!") + .foregroundStyle(Color.grayMain2c500) + .default_text_style(styleSize: 12) } } .onTapGesture {} diff --git a/Linphone/UI/Main/Conversations/Model/Message.swift b/Linphone/UI/Main/Conversations/Model/Message.swift index 271b23298..282be1e5a 100644 --- a/Linphone/UI/Main/Conversations/Model/Message.swift +++ b/Linphone/UI/Main/Conversations/Model/Message.swift @@ -84,6 +84,8 @@ public struct Message: Identifiable, Hashable { public var isEphemeral: Bool public var ephemeralExpireTime: Int public var ephemeralLifetime: Int + + public var isIcalendar: Bool public init( id: String, @@ -104,7 +106,8 @@ public struct Message: Identifiable, Hashable { reactions: [String] = [], isEphemeral: Bool = false, ephemeralExpireTime: Int = 0, - ephemeralLifetime: Int = 0 + ephemeralLifetime: Int = 0, + isIcalendar: Bool = false ) { self.id = id self.appData = appData @@ -125,6 +128,7 @@ public struct Message: Identifiable, Hashable { self.isEphemeral = isEphemeral self.ephemeralExpireTime = ephemeralExpireTime self.ephemeralLifetime = ephemeralLifetime + self.isIcalendar = isIcalendar } public static func makeMessage( diff --git a/Linphone/UI/Main/Conversations/Model/MessageConferenceInfo.swift b/Linphone/UI/Main/Conversations/Model/MessageConferenceInfo.swift new file mode 100644 index 000000000..7ec3238e0 --- /dev/null +++ b/Linphone/UI/Main/Conversations/Model/MessageConferenceInfo.swift @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2010-2023 Belledonne Communications SARL. + * + * This file is part of linphone-iphone + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import Foundation + +public enum MessageConferenceState { + case updated + case cancelled +} + +public struct MessageConferenceInfo { + public let id: String + var uri: URL + var subject: String + var description: String + var state: MessageConferenceState + var dateTime: String + //var duration: time_t + //var participantInfos: [ParticipantInfo] + + public init(id: String, uri: URL, subject: String, description: String, state: MessageConferenceState, dateTime: String) { + self.id = id + self.uri = uri + self.subject = subject + self.description = description + self.state = state + self.dateTime = dateTime + } +} diff --git a/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift b/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift index 45e344877..75c90451b 100644 --- a/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift +++ b/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift @@ -506,7 +506,8 @@ class ConversationViewModel: ObservableObject { reactions: reactionsTmp, isEphemeral: eventLog.chatMessage?.isEphemeral ?? false, ephemeralExpireTime: eventLog.chatMessage?.ephemeralExpireTime ?? 0, - ephemeralLifetime: eventLog.chatMessage?.ephemeralLifetime ?? 0 + ephemeralLifetime: eventLog.chatMessage?.ephemeralLifetime ?? 0, + isIcalendar: eventLog.chatMessage?.contents.first?.isIcalendar ?? false ) ) ) @@ -729,7 +730,8 @@ class ConversationViewModel: ObservableObject { reactions: reactionsTmp, isEphemeral: eventLog.chatMessage?.isEphemeral ?? false, ephemeralExpireTime: eventLog.chatMessage?.ephemeralExpireTime ?? 0, - ephemeralLifetime: eventLog.chatMessage?.ephemeralLifetime ?? 0 + ephemeralLifetime: eventLog.chatMessage?.ephemeralLifetime ?? 0, + isIcalendar: eventLog.chatMessage?.contents.first?.isIcalendar ?? false ) ), at: 0 ) @@ -964,7 +966,8 @@ class ConversationViewModel: ObservableObject { reactions: reactionsTmp, isEphemeral: eventLog.chatMessage?.isEphemeral ?? false, ephemeralExpireTime: eventLog.chatMessage?.ephemeralExpireTime ?? 0, - ephemeralLifetime: eventLog.chatMessage?.ephemeralLifetime ?? 0 + ephemeralLifetime: eventLog.chatMessage?.ephemeralLifetime ?? 0, + isIcalendar: eventLog.chatMessage?.contents.first?.isIcalendar ?? false ) ) @@ -1245,7 +1248,8 @@ class ConversationViewModel: ObservableObject { reactions: reactionsTmp, isEphemeral: eventLog.chatMessage?.isEphemeral ?? false, ephemeralExpireTime: eventLog.chatMessage?.ephemeralExpireTime ?? 0, - ephemeralLifetime: eventLog.chatMessage?.ephemeralLifetime ?? 0 + ephemeralLifetime: eventLog.chatMessage?.ephemeralLifetime ?? 0, + isIcalendar: eventLog.chatMessage?.contents.first?.isIcalendar ?? false ) ), at: 0 ) @@ -1888,6 +1892,75 @@ class ConversationViewModel: ObservableObject { } } } + + func parseConferenceInvite(content: Content) -> MessageConferenceInfo? { + + var meetingConferenceUri: URL? + var meetingSubject: String = "" + var meetingDescription: String = "" + var meetingUpdated: Bool = false + var meetingCancelled: Bool = false + var meetingDate: String = "" + var meetingTime: String = "" + var meetingDay: String = "" + var meetingDayNumber: String = "" + var meetingParticipants: String = "" + var meetingFound: Bool = false + + if let conferenceInfo = try? Factory.Instance.createConferenceInfoFromIcalendarContent(content: content) { + + if let conferenceAddress = conferenceInfo.uri { + let conferenceUri = conferenceAddress.asStringUriOnly() + Log.info("Found conference info with URI [\(conferenceUri)] and subject [\(conferenceInfo.subject)]") + meetingConferenceUri = URL(string: conferenceAddress.asStringUriOnly()) + meetingSubject = conferenceInfo.subject ?? "" + meetingDescription = conferenceInfo.description ?? "" + + meetingUpdated = (conferenceInfo.state == .Updated) + meetingCancelled = (conferenceInfo.state == .Cancelled) + + let timestamp = conferenceInfo.dateTime + let duration = conferenceInfo.duration + + let timeInterval = TimeInterval(timestamp) + let dateTmp = Date(timeIntervalSince1970: timeInterval) + let dateFormatter = DateFormatter() + dateFormatter.dateStyle = .full + dateFormatter.timeStyle = .none + + let date = dateFormatter.string(from: dateTmp) + + let timeFormatter = DateFormatter() + timeFormatter.dateFormat = Locale.current.identifier == "fr_FR" ? "HH:mm" : "h:mm a" + let timeTmp = timeFormatter.string(from: dateTmp) + + /* + let timeBisInterval = TimeInterval(timestamp + (Int(duration) * 60)) + let timeBis = Date(timeIntervalSince1970: timeBisInterval) + let endTime = timeFormatter.string(from: timeBis) + + let startTime = TimestampUtils.timeToString(timestamp) + let end = timestamp + (duration * 60) + let endTime = TimestampUtils.timeToString(end) + + meetingDate = date + meetingTime = "\(startTime) - \(endTime)" + meetingDay = TimestampUtils.dayOfWeek(timestamp) + meetingDayNumber = TimestampUtils.dayOfMonth(timestamp) + + let count = conferenceInfo.participantInfos.count + meetingParticipants = AppUtils.getStringWithPlural(R.plurals.conference_participants_list_title, count: count, countString: "\(count)") + + meetingFound = true + */ + if meetingConferenceUri != nil { + return MessageConferenceInfo(id: UUID().uuidString, uri: meetingConferenceUri!, subject: meetingSubject, description: meetingDescription, state: .updated, dateTime: timeTmp) + } + } + } + + return nil + } } // swiftlint:enable line_length // swiftlint:enable type_body_length