forked from mirrors/linphone-iphone
Add eventLog to ui message object for message list
This commit is contained in:
parent
e792810c3c
commit
ace392528b
6 changed files with 311 additions and 254 deletions
|
|
@ -7,7 +7,7 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
4ED1F0A881A9ACB5977A8987 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; };
|
||||
4ED1F0A881A9ACB5977A8987 /* (null) 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 */; };
|
||||
|
|
@ -364,7 +364,7 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
4ED1F0A881A9ACB5977A8987 /* BuildFile in Frameworks */,
|
||||
4ED1F0A881A9ACB5977A8987 /* (null) in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -1238,7 +1238,7 @@
|
|||
INFOPLIST_FILE = msgNotificationService/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = msgNotificationService;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
|
@ -1277,7 +1277,7 @@
|
|||
INFOPLIST_FILE = msgNotificationService/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = msgNotificationService;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
|
@ -1422,7 +1422,7 @@
|
|||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
||||
CODE_SIGN_ENTITLEMENTS = Linphone/Linphone.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 32;
|
||||
CURRENT_PROJECT_VERSION = 36;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Linphone/Preview Content\"";
|
||||
|
|
@ -1478,7 +1478,7 @@
|
|||
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
|
||||
CODE_SIGN_ENTITLEMENTS = Linphone/Linphone.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 32;
|
||||
CURRENT_PROJECT_VERSION = 36;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"Linphone/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = Z2V957B3D6;
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ struct ChatBubbleView: View {
|
|||
|
||||
@ObservedObject var conversationViewModel: ConversationViewModel
|
||||
|
||||
let message: Message
|
||||
let eventLogMessage: EventLogMessage
|
||||
|
||||
let geometryProxy: GeometryProxy
|
||||
|
||||
|
|
@ -35,50 +35,50 @@ struct ChatBubbleView: View {
|
|||
var body: some View {
|
||||
HStack {
|
||||
VStack {
|
||||
if !message.text.isEmpty || !message.attachments.isEmpty {
|
||||
if !eventLogMessage.message.text.isEmpty || !eventLogMessage.message.attachments.isEmpty {
|
||||
HStack(alignment: .top, content: {
|
||||
if message.isOutgoing {
|
||||
if eventLogMessage.message.isOutgoing {
|
||||
Spacer()
|
||||
}
|
||||
|
||||
if conversationViewModel.displayedConversation != nil && conversationViewModel.displayedConversation!.isGroup && !message.isOutgoing && message.isFirstMessage {
|
||||
if conversationViewModel.displayedConversation != nil && conversationViewModel.displayedConversation!.isGroup && !eventLogMessage.message.isOutgoing && eventLogMessage.message.isFirstMessage {
|
||||
VStack {
|
||||
Avatar(
|
||||
contactAvatarModel: conversationViewModel.participantConversationModel.first(where: {$0.address == message.address}) ??
|
||||
contactAvatarModel: conversationViewModel.participantConversationModel.first(where: {$0.address == eventLogMessage.message.address}) ??
|
||||
ContactAvatarModel(friend: nil, name: "??", address: "", withPresence: false),
|
||||
avatarSize: 35
|
||||
)
|
||||
.padding(.top, 30)
|
||||
}
|
||||
} else if conversationViewModel.displayedConversation != nil && conversationViewModel.displayedConversation!.isGroup && !message.isOutgoing {
|
||||
} else if conversationViewModel.displayedConversation != nil && conversationViewModel.displayedConversation!.isGroup && !eventLogMessage.message.isOutgoing {
|
||||
VStack {
|
||||
}
|
||||
.padding(.leading, 43)
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
if conversationViewModel.displayedConversation != nil && conversationViewModel.displayedConversation!.isGroup && !message.isOutgoing && message.isFirstMessage {
|
||||
Text(conversationViewModel.participantConversationModel.first(where: {$0.address == message.address})?.name ?? "")
|
||||
if conversationViewModel.displayedConversation != nil && conversationViewModel.displayedConversation!.isGroup && !eventLogMessage.message.isOutgoing && eventLogMessage.message.isFirstMessage {
|
||||
Text(conversationViewModel.participantConversationModel.first(where: {$0.address == eventLogMessage.message.address})?.name ?? "")
|
||||
.default_text_style(styleSize: 12)
|
||||
.padding(.top, 10)
|
||||
.padding(.bottom, 2)
|
||||
}
|
||||
|
||||
if message.replyMessage != nil {
|
||||
if eventLogMessage.message.replyMessage != nil {
|
||||
HStack {
|
||||
if message.isOutgoing {
|
||||
if eventLogMessage.message.isOutgoing {
|
||||
Spacer()
|
||||
}
|
||||
|
||||
VStack(alignment: message.isOutgoing ? .trailing : .leading) {
|
||||
VStack(alignment: message.isOutgoing ? .trailing : .leading) {
|
||||
if !message.replyMessage!.text.isEmpty {
|
||||
Text(message.replyMessage!.text)
|
||||
VStack(alignment: eventLogMessage.message.isOutgoing ? .trailing : .leading) {
|
||||
VStack(alignment: eventLogMessage.message.isOutgoing ? .trailing : .leading) {
|
||||
if !eventLogMessage.message.replyMessage!.text.isEmpty {
|
||||
Text(eventLogMessage.message.replyMessage!.text)
|
||||
.foregroundStyle(Color.grayMain2c700)
|
||||
.default_text_style(styleSize: 16)
|
||||
.lineLimit(/*@START_MENU_TOKEN@*/2/*@END_MENU_TOKEN@*/)
|
||||
} else if !message.replyMessage!.attachmentsNames.isEmpty {
|
||||
Text(message.replyMessage!.attachmentsNames)
|
||||
} else if !eventLogMessage.message.replyMessage!.attachmentsNames.isEmpty {
|
||||
Text(eventLogMessage.message.replyMessage!.attachmentsNames)
|
||||
.foregroundStyle(Color.grayMain2c700)
|
||||
.default_text_style(styleSize: 16)
|
||||
.lineLimit(/*@START_MENU_TOKEN@*/2/*@END_MENU_TOKEN@*/)
|
||||
|
|
@ -90,14 +90,14 @@ struct ChatBubbleView: View {
|
|||
.clipShape(RoundedRectangle(cornerRadius: 1))
|
||||
.roundedCorner(
|
||||
16,
|
||||
corners: message.isOutgoing ? [.topLeft, .topRight, .bottomLeft] : [.topLeft, .topRight, .bottomRight]
|
||||
corners: eventLogMessage.message.isOutgoing ? [.topLeft, .topRight, .bottomLeft] : [.topLeft, .topRight, .bottomRight]
|
||||
)
|
||||
}
|
||||
.onTapGesture {
|
||||
conversationViewModel.scrollToMessage(message: message)
|
||||
conversationViewModel.scrollToMessage(message: eventLogMessage.message)
|
||||
}
|
||||
|
||||
if !message.isOutgoing {
|
||||
if !eventLogMessage.message.isOutgoing {
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
|
@ -107,37 +107,37 @@ struct ChatBubbleView: View {
|
|||
|
||||
ZStack {
|
||||
HStack {
|
||||
if message.isOutgoing {
|
||||
if eventLogMessage.message.isOutgoing {
|
||||
Spacer()
|
||||
}
|
||||
|
||||
VStack(alignment: message.isOutgoing ? .trailing : .leading) {
|
||||
VStack(alignment: message.isOutgoing ? .trailing : .leading) {
|
||||
if !message.attachments.isEmpty {
|
||||
VStack(alignment: eventLogMessage.message.isOutgoing ? .trailing : .leading) {
|
||||
VStack(alignment: eventLogMessage.message.isOutgoing ? .trailing : .leading) {
|
||||
if !eventLogMessage.message.attachments.isEmpty {
|
||||
messageAttachments()
|
||||
}
|
||||
|
||||
if !message.text.isEmpty {
|
||||
Text(message.text)
|
||||
if !eventLogMessage.message.text.isEmpty {
|
||||
Text(eventLogMessage.message.text)
|
||||
.foregroundStyle(Color.grayMain2c700)
|
||||
.default_text_style(styleSize: 16)
|
||||
}
|
||||
|
||||
HStack(alignment: .center) {
|
||||
Text(conversationViewModel.getMessageTime(startDate: message.dateReceived))
|
||||
Text(conversationViewModel.getMessageTime(startDate: eventLogMessage.message.dateReceived))
|
||||
.foregroundStyle(Color.grayMain2c500)
|
||||
.default_text_style_300(styleSize: 14)
|
||||
.padding(.top, 1)
|
||||
|
||||
if (conversationViewModel.displayedConversation != nil && conversationViewModel.displayedConversation!.isGroup) || message.isOutgoing {
|
||||
if message.status == .sending {
|
||||
if (conversationViewModel.displayedConversation != nil && conversationViewModel.displayedConversation!.isGroup) || eventLogMessage.message.isOutgoing {
|
||||
if eventLogMessage.message.status == .sending {
|
||||
ProgressView()
|
||||
.controlSize(.mini)
|
||||
.progressViewStyle(CircularProgressViewStyle(tint: .orangeMain500))
|
||||
.frame(width: 10, height: 10)
|
||||
.padding(.top, 1)
|
||||
} else if message.status != nil {
|
||||
Image(conversationViewModel.getImageIMDN(status: message.status!))
|
||||
} else if eventLogMessage.message.status != nil {
|
||||
Image(conversationViewModel.getImageIMDN(status: eventLogMessage.message.status!))
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(Color.orangeMain500)
|
||||
|
|
@ -149,49 +149,49 @@ struct ChatBubbleView: View {
|
|||
.padding(.top, -4)
|
||||
}
|
||||
.padding(.all, 15)
|
||||
.background(message.isOutgoing ? Color.orangeMain100 : Color.grayMain2c100)
|
||||
.background(eventLogMessage.message.isOutgoing ? Color.orangeMain100 : Color.grayMain2c100)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 3))
|
||||
.roundedCorner(
|
||||
16,
|
||||
corners: message.isOutgoing && message.isFirstMessage ? [.topLeft, .topRight, .bottomLeft] :
|
||||
(!message.isOutgoing && message.isFirstMessage ? [.topRight, .bottomRight, .bottomLeft] : [.allCorners]))
|
||||
corners: eventLogMessage.message.isOutgoing && eventLogMessage.message.isFirstMessage ? [.topLeft, .topRight, .bottomLeft] :
|
||||
(!eventLogMessage.message.isOutgoing && eventLogMessage.message.isFirstMessage ? [.topRight, .bottomRight, .bottomLeft] : [.allCorners]))
|
||||
|
||||
if !message.reactions.isEmpty {
|
||||
if !eventLogMessage.message.reactions.isEmpty {
|
||||
HStack {
|
||||
ForEach(0..<message.reactions.count, id: \.self) { index in
|
||||
if message.reactions.firstIndex(of: message.reactions[index]) == index {
|
||||
Text(message.reactions[index])
|
||||
ForEach(0..<eventLogMessage.message.reactions.count, id: \.self) { index in
|
||||
if eventLogMessage.message.reactions.firstIndex(of: eventLogMessage.message.reactions[index]) == index {
|
||||
Text(eventLogMessage.message.reactions[index])
|
||||
.default_text_style(styleSize: 14)
|
||||
.padding(.horizontal, -2)
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
(message.reactions.contains("👍") ? 1 : 0) +
|
||||
(message.reactions.contains("❤️") ? 1 : 0) +
|
||||
(message.reactions.contains("😂") ? 1 : 0) +
|
||||
(message.reactions.contains("😮") ? 1 : 0) +
|
||||
(message.reactions.contains("😢") ? 1 : 0)
|
||||
) != message.reactions.count {
|
||||
Text("\(message.reactions.count)")
|
||||
(eventLogMessage.message.reactions.contains("👍") ? 1 : 0) +
|
||||
(eventLogMessage.message.reactions.contains("❤️") ? 1 : 0) +
|
||||
(eventLogMessage.message.reactions.contains("😂") ? 1 : 0) +
|
||||
(eventLogMessage.message.reactions.contains("😮") ? 1 : 0) +
|
||||
(eventLogMessage.message.reactions.contains("😢") ? 1 : 0)
|
||||
) != eventLogMessage.message.reactions.count {
|
||||
Text("\(eventLogMessage.message.reactions.count)")
|
||||
.default_text_style(styleSize: 14)
|
||||
.padding(.horizontal, -2)
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 6)
|
||||
.padding(.horizontal, 10)
|
||||
.background(message.isOutgoing ? Color.orangeMain100 : Color.grayMain2c100)
|
||||
.background(eventLogMessage.message.isOutgoing ? Color.orangeMain100 : Color.grayMain2c100)
|
||||
.cornerRadius(20)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 20)
|
||||
.stroke(.white, lineWidth: 3)
|
||||
)
|
||||
.padding(.top, -20)
|
||||
.padding(message.isOutgoing ? .trailing : .leading, 5)
|
||||
.padding(eventLogMessage.message.isOutgoing ? .trailing : .leading, 5)
|
||||
}
|
||||
}
|
||||
|
||||
if !message.isOutgoing {
|
||||
if !eventLogMessage.message.isOutgoing {
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
|
@ -200,12 +200,12 @@ struct ChatBubbleView: View {
|
|||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
|
||||
if !message.isOutgoing {
|
||||
if !eventLogMessage.message.isOutgoing {
|
||||
Spacer()
|
||||
}
|
||||
})
|
||||
.padding(.leading, message.isOutgoing ? 40 : 0)
|
||||
.padding(.trailing, !message.isOutgoing ? 40 : 0)
|
||||
.padding(.leading, eventLogMessage.message.isOutgoing ? 40 : 0)
|
||||
.padding(.trailing, !eventLogMessage.message.isOutgoing ? 40 : 0)
|
||||
}
|
||||
}
|
||||
.onTapGesture {}
|
||||
|
|
@ -226,7 +226,7 @@ struct ChatBubbleView: View {
|
|||
|
||||
self.timePassed = self.ticker.timeIntervalSinceStarted
|
||||
withAnimation {
|
||||
conversationViewModel.selectedMessage = message
|
||||
conversationViewModel.selectedMessage = eventLogMessage
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -242,9 +242,9 @@ struct ChatBubbleView: View {
|
|||
|
||||
@ViewBuilder
|
||||
func messageAttachments() -> some View {
|
||||
if message.attachments.count == 1 {
|
||||
if message.attachments.first!.type == .image || message.attachments.first!.type == .gif || message.attachments.first!.type == .video {
|
||||
let result = imageDimensions(url: message.attachments.first!.thumbnail.absoluteString)
|
||||
if eventLogMessage.message.attachments.count == 1 {
|
||||
if eventLogMessage.message.attachments.first!.type == .image || eventLogMessage.message.attachments.first!.type == .gif || eventLogMessage.message.attachments.first!.type == .video {
|
||||
let result = imageDimensions(url: eventLogMessage.message.attachments.first!.thumbnail.absoluteString)
|
||||
ZStack {
|
||||
Rectangle()
|
||||
.fill(Color(.white))
|
||||
|
|
@ -268,9 +268,9 @@ struct ChatBubbleView: View {
|
|||
)
|
||||
}
|
||||
|
||||
if message.attachments.first!.type == .image || message.attachments.first!.type == .video {
|
||||
if eventLogMessage.message.attachments.first!.type == .image || eventLogMessage.message.attachments.first!.type == .video {
|
||||
if #available(iOS 16.0, *) {
|
||||
AsyncImage(url: message.attachments.first!.thumbnail) { phase in
|
||||
AsyncImage(url: eventLogMessage.message.attachments.first!.thumbnail) { phase in
|
||||
switch phase {
|
||||
case .empty:
|
||||
ProgressView()
|
||||
|
|
@ -281,7 +281,7 @@ struct ChatBubbleView: View {
|
|||
.interpolation(.medium)
|
||||
.aspectRatio(contentMode: .fill)
|
||||
|
||||
if message.attachments.first!.type == .video {
|
||||
if eventLogMessage.message.attachments.first!.type == .video {
|
||||
Image("play-fill")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
|
|
@ -298,7 +298,7 @@ struct ChatBubbleView: View {
|
|||
.layoutPriority(-1)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 4))
|
||||
} else {
|
||||
AsyncImage(url: message.attachments.first!.thumbnail) { phase in
|
||||
AsyncImage(url: eventLogMessage.message.attachments.first!.thumbnail) { phase in
|
||||
switch phase {
|
||||
case .empty:
|
||||
ProgressView()
|
||||
|
|
@ -309,7 +309,7 @@ struct ChatBubbleView: View {
|
|||
.interpolation(.medium)
|
||||
.aspectRatio(contentMode: .fill)
|
||||
|
||||
if message.attachments.first!.type == .video {
|
||||
if eventLogMessage.message.attachments.first!.type == .video {
|
||||
Image("play-fill")
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
|
|
@ -327,13 +327,13 @@ struct ChatBubbleView: View {
|
|||
.clipShape(RoundedRectangle(cornerRadius: 4))
|
||||
.id(UUID())
|
||||
}
|
||||
} else if message.attachments.first!.type == .gif {
|
||||
} else if eventLogMessage.message.attachments.first!.type == .gif {
|
||||
if #available(iOS 16.0, *) {
|
||||
GifImageView(message.attachments.first!.thumbnail)
|
||||
GifImageView(eventLogMessage.message.attachments.first!.thumbnail)
|
||||
.layoutPriority(-1)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 4))
|
||||
} else {
|
||||
GifImageView(message.attachments.first!.thumbnail)
|
||||
GifImageView(eventLogMessage.message.attachments.first!.thumbnail)
|
||||
.id(UUID())
|
||||
.layoutPriority(-1)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 4))
|
||||
|
|
@ -343,12 +343,12 @@ struct ChatBubbleView: View {
|
|||
.clipShape(RoundedRectangle(cornerRadius: 4))
|
||||
.clipped()
|
||||
}
|
||||
} else if message.attachments.count > 1 {
|
||||
} else if eventLogMessage.message.attachments.count > 1 {
|
||||
let isGroup = conversationViewModel.displayedConversation != nil && conversationViewModel.displayedConversation!.isGroup
|
||||
LazyVGrid(columns: [
|
||||
GridItem(.adaptive(minimum: 120), spacing: 1)
|
||||
], spacing: 3) {
|
||||
ForEach(message.attachments) { attachment in
|
||||
ForEach(eventLogMessage.message.attachments) { attachment in
|
||||
ZStack {
|
||||
Rectangle()
|
||||
.fill(Color(.white))
|
||||
|
|
@ -402,9 +402,9 @@ struct ChatBubbleView: View {
|
|||
}
|
||||
}
|
||||
.frame(
|
||||
width: geometryProxy.size.width > 0 && CGFloat(122 * message.attachments.count) > geometryProxy.size.width - 110 - (isGroup ? 40 : 0)
|
||||
width: geometryProxy.size.width > 0 && CGFloat(122 * eventLogMessage.message.attachments.count) > geometryProxy.size.width - 110 - (isGroup ? 40 : 0)
|
||||
? 122 * floor(CGFloat(geometryProxy.size.width - 110 - (isGroup ? 40 : 0)) / 122)
|
||||
: CGFloat(122 * message.attachments.count)
|
||||
: CGFloat(122 * eventLogMessage.message.attachments.count)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -235,8 +235,8 @@ struct ConversationFragment: View {
|
|||
if conversationViewModel.conversationMessagesSection.first != nil {
|
||||
let counter = conversationViewModel.conversationMessagesSection.first!.rows.count
|
||||
ForEach(0..<counter, id: \.self) { index in
|
||||
ChatBubbleView(conversationViewModel: conversationViewModel, message: conversationViewModel.conversationMessagesSection.first!.rows[index], geometryProxy: geometry)
|
||||
.id(conversationViewModel.conversationMessagesSection.first!.rows[index].id)
|
||||
ChatBubbleView(conversationViewModel: conversationViewModel, eventLogMessage: conversationViewModel.conversationMessagesSection.first!.rows[index], geometryProxy: geometry)
|
||||
.id(conversationViewModel.conversationMessagesSection.first!.rows[index].message.id)
|
||||
.listRowInsets(EdgeInsets(top: 2, leading: 10, bottom: 2, trailing: 10))
|
||||
.listRowSeparator(.hidden)
|
||||
.scaleEffect(x: 1, y: -1, anchor: .center)
|
||||
|
|
@ -250,6 +250,8 @@ struct ConversationFragment: View {
|
|||
|
||||
if index == 0 {
|
||||
displayFloatingButton = false
|
||||
conversationViewModel.markAsRead()
|
||||
conversationsListViewModel.computeChatRoomsList(filter: "")
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
|
|
@ -262,12 +264,16 @@ struct ConversationFragment: View {
|
|||
}
|
||||
.scaleEffect(x: 1, y: -1, anchor: .center)
|
||||
.listStyle(.plain)
|
||||
.onAppear {
|
||||
conversationViewModel.markAsRead()
|
||||
conversationsListViewModel.computeChatRoomsList(filter: "")
|
||||
}
|
||||
|
||||
if displayFloatingButton {
|
||||
Button {
|
||||
if conversationViewModel.conversationMessagesSection.first != nil && conversationViewModel.conversationMessagesSection.first!.rows.first != nil {
|
||||
withAnimation {
|
||||
proxy.scrollTo(conversationViewModel.conversationMessagesSection.first!.rows.first!.id)
|
||||
proxy.scrollTo(conversationViewModel.conversationMessagesSection.first!.rows.first!.message.id)
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
|
|
@ -327,19 +333,19 @@ struct ConversationFragment: View {
|
|||
VStack {
|
||||
(
|
||||
Text("conversation_reply_to_message_title")
|
||||
+ Text("**\(conversationViewModel.participantConversationModel.first(where: {$0.address == conversationViewModel.messageToReply!.address})?.name ?? "")**"))
|
||||
+ Text("**\(conversationViewModel.participantConversationModel.first(where: {$0.address == conversationViewModel.messageToReply!.message.address})?.name ?? "")**"))
|
||||
.default_text_style_300(styleSize: 15)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(.bottom, 1)
|
||||
.lineLimit(1)
|
||||
|
||||
if conversationViewModel.messageToReply!.text.isEmpty {
|
||||
Text(conversationViewModel.messageToReply!.attachmentsNames)
|
||||
if conversationViewModel.messageToReply!.message.text.isEmpty {
|
||||
Text(conversationViewModel.messageToReply!.message.attachmentsNames)
|
||||
.default_text_style_300(styleSize: 15)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.lineLimit(1)
|
||||
} else {
|
||||
Text("\(conversationViewModel.messageToReply!.text)")
|
||||
Text("\(conversationViewModel.messageToReply!.message.text)")
|
||||
.default_text_style_300(styleSize: 15)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.lineLimit(1)
|
||||
|
|
@ -586,7 +592,7 @@ struct ConversationFragment: View {
|
|||
|
||||
VStack {
|
||||
HStack {
|
||||
if conversationViewModel.selectedMessage!.isOutgoing {
|
||||
if conversationViewModel.selectedMessage!.message.isOutgoing {
|
||||
Spacer()
|
||||
}
|
||||
|
||||
|
|
@ -598,7 +604,7 @@ struct ConversationFragment: View {
|
|||
.default_text_style(styleSize: iconSize > 50 ? 50 : iconSize)
|
||||
}
|
||||
.padding(.horizontal, 8)
|
||||
.background(conversationViewModel.selectedMessage?.ownReaction == "👍" ? Color.gray200 : .white)
|
||||
.background(conversationViewModel.selectedMessage?.message.ownReaction == "👍" ? Color.gray200 : .white)
|
||||
.cornerRadius(10)
|
||||
|
||||
Button {
|
||||
|
|
@ -608,7 +614,7 @@ struct ConversationFragment: View {
|
|||
.default_text_style(styleSize: iconSize > 50 ? 50 : iconSize)
|
||||
}
|
||||
.padding(.horizontal, 8)
|
||||
.background(conversationViewModel.selectedMessage?.ownReaction == "❤️" ? Color.gray200 : .white)
|
||||
.background(conversationViewModel.selectedMessage?.message.ownReaction == "❤️" ? Color.gray200 : .white)
|
||||
.cornerRadius(10)
|
||||
|
||||
Button {
|
||||
|
|
@ -618,7 +624,7 @@ struct ConversationFragment: View {
|
|||
.default_text_style(styleSize: iconSize > 50 ? 50 : iconSize)
|
||||
}
|
||||
.padding(.horizontal, 8)
|
||||
.background(conversationViewModel.selectedMessage?.ownReaction == "😂" ? Color.gray200 : .white)
|
||||
.background(conversationViewModel.selectedMessage?.message.ownReaction == "😂" ? Color.gray200 : .white)
|
||||
.cornerRadius(10)
|
||||
|
||||
Button {
|
||||
|
|
@ -628,7 +634,7 @@ struct ConversationFragment: View {
|
|||
.default_text_style(styleSize: iconSize > 50 ? 50 : iconSize)
|
||||
}
|
||||
.padding(.horizontal, 8)
|
||||
.background(conversationViewModel.selectedMessage?.ownReaction == "😮" ? Color.gray200 : .white)
|
||||
.background(conversationViewModel.selectedMessage?.message.ownReaction == "😮" ? Color.gray200 : .white)
|
||||
.cornerRadius(10)
|
||||
|
||||
Button {
|
||||
|
|
@ -638,7 +644,7 @@ struct ConversationFragment: View {
|
|||
.default_text_style(styleSize: iconSize > 50 ? 50 : iconSize)
|
||||
}
|
||||
.padding(.horizontal, 8)
|
||||
.background(conversationViewModel.selectedMessage?.ownReaction == "😢" ? Color.gray200 : .white)
|
||||
.background(conversationViewModel.selectedMessage?.message.ownReaction == "😢" ? Color.gray200 : .white)
|
||||
.cornerRadius(10)
|
||||
|
||||
Button {
|
||||
|
|
@ -656,7 +662,7 @@ struct ConversationFragment: View {
|
|||
.background(.white)
|
||||
.cornerRadius(20)
|
||||
|
||||
if !conversationViewModel.selectedMessage!.isOutgoing {
|
||||
if !conversationViewModel.selectedMessage!.message.isOutgoing {
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
|
@ -665,19 +671,19 @@ struct ConversationFragment: View {
|
|||
.padding(.leading, conversationViewModel.displayedConversation!.isGroup ? 43 : 0)
|
||||
.shadow(color: .black.opacity(0.1), radius: 10)
|
||||
|
||||
ChatBubbleView(conversationViewModel: conversationViewModel, message: conversationViewModel.selectedMessage!, geometryProxy: geometry)
|
||||
ChatBubbleView(conversationViewModel: conversationViewModel, eventLogMessage: conversationViewModel.selectedMessage!, geometryProxy: geometry)
|
||||
.padding(.horizontal, 10)
|
||||
.padding(.vertical, 1)
|
||||
.shadow(color: .black.opacity(0.1), radius: 10)
|
||||
|
||||
HStack {
|
||||
if conversationViewModel.selectedMessage!.isOutgoing {
|
||||
if conversationViewModel.selectedMessage!.message.isOutgoing {
|
||||
Spacer()
|
||||
}
|
||||
|
||||
VStack {
|
||||
Button {
|
||||
let indexMessage = conversationViewModel.conversationMessagesSection[0].rows.firstIndex(where: {$0.id == conversationViewModel.selectedMessage!.id})
|
||||
let indexMessage = conversationViewModel.conversationMessagesSection[0].rows.firstIndex(where: {$0.message.id == conversationViewModel.selectedMessage!.message.id})
|
||||
conversationViewModel.selectedMessage = nil
|
||||
conversationViewModel.replyToMessage(index: indexMessage ?? 0)
|
||||
} label: {
|
||||
|
|
@ -695,10 +701,10 @@ struct ConversationFragment: View {
|
|||
|
||||
Divider()
|
||||
|
||||
if !conversationViewModel.selectedMessage!.text.isEmpty {
|
||||
if !conversationViewModel.selectedMessage!.message.text.isEmpty {
|
||||
Button {
|
||||
UIPasteboard.general.setValue(
|
||||
conversationViewModel.selectedMessage!.text,
|
||||
conversationViewModel.selectedMessage!.message.text,
|
||||
forPasteboardType: UTType.plainText.identifier
|
||||
)
|
||||
|
||||
|
|
@ -760,7 +766,7 @@ struct ConversationFragment: View {
|
|||
.background(.white)
|
||||
.cornerRadius(20)
|
||||
|
||||
if !conversationViewModel.selectedMessage!.isOutgoing {
|
||||
if !conversationViewModel.selectedMessage!.message.isOutgoing {
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
// swiftlint:disable large_tuple
|
||||
import SwiftUI
|
||||
import linphonesw
|
||||
|
||||
public extension Notification.Name {
|
||||
static let onScrollToBottom = Notification.Name("onScrollToBottom")
|
||||
|
|
@ -64,7 +65,7 @@ struct UIList: UIViewRepresentable {
|
|||
&& conversationViewModel.displayedConversation != nil
|
||||
&& context.coordinator.sections.first!.chatRoomID == conversationViewModel.displayedConversation!.id
|
||||
&& context.coordinator.sections.first!.rows.count == conversationViewModel.conversationMessagesSection.first!.rows.count {
|
||||
tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .bottom, animated: true)
|
||||
tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true)
|
||||
} else {
|
||||
NotificationCenter.default.removeObserver(self, name: .onScrollToBottom, object: nil)
|
||||
}
|
||||
|
|
@ -209,24 +210,26 @@ struct UIList: UIViewRepresentable {
|
|||
|
||||
var oldRows = appliedDeletes[oldIndex].rows
|
||||
var newRows = appliedDeletesSwapsAndEdits[newIndex].rows
|
||||
let oldRowIDs = Set(oldRows.map { $0.id })
|
||||
let newRowIDs = Set(newRows.map { $0.id })
|
||||
let oldRowIDs = Set(oldRows.map { $0.message.id })
|
||||
let newRowIDs = Set(newRows.map { $0.message.id })
|
||||
let rowIDsToDelete = oldRowIDs.subtracting(newRowIDs)
|
||||
let rowIDsToInsert = newRowIDs.subtracting(oldRowIDs)
|
||||
|
||||
for rowId in rowIDsToDelete {
|
||||
if let index = oldRows.firstIndex(where: { $0.id == rowId }) {
|
||||
if let index = oldRows.firstIndex(where: { $0.message.id == rowId }) {
|
||||
oldRows.remove(at: index)
|
||||
deleteOperations.append(.delete(oldIndex, index))
|
||||
}
|
||||
}
|
||||
|
||||
for rowId in rowIDsToInsert {
|
||||
if let index = newRows.firstIndex(where: { $0.id == rowId }) {
|
||||
if let index = newRows.firstIndex(where: { $0.message.id == rowId }) {
|
||||
insertOperations.append(.insert(newIndex, index))
|
||||
}
|
||||
}
|
||||
|
||||
for rowId in rowIDsToInsert {
|
||||
if let index = newRows.firstIndex(where: { $0.id == rowId }) {
|
||||
if let index = newRows.firstIndex(where: { $0.message.id == rowId }) {
|
||||
newRows.remove(at: index)
|
||||
}
|
||||
}
|
||||
|
|
@ -234,8 +237,8 @@ struct UIList: UIViewRepresentable {
|
|||
for row in 0..<oldRows.count {
|
||||
let oldRow = oldRows[row]
|
||||
let newRow = newRows[row]
|
||||
if oldRow.id != newRow.id {
|
||||
if let index = newRows.firstIndex(where: { $0.id == oldRow.id }) {
|
||||
if oldRow.message.id != newRow.message.id {
|
||||
if let index = newRows.firstIndex(where: { $0.message.id == oldRow.message.id }) {
|
||||
if !swapsContain(swaps: swapOperations, section: oldIndex, index: row) ||
|
||||
!swapsContain(swaps: swapOperations, section: oldIndex, index: index) {
|
||||
swapOperations.append(.swap(oldIndex, row, index))
|
||||
|
|
@ -338,7 +341,7 @@ struct UIList: UIViewRepresentable {
|
|||
let row = sections[indexPath.section].rows[indexPath.row]
|
||||
if #available(iOS 16.0, *) {
|
||||
tableViewCell.contentConfiguration = UIHostingConfiguration {
|
||||
ChatBubbleView(conversationViewModel: conversationViewModel, message: row, geometryProxy: geometryProxy)
|
||||
ChatBubbleView(conversationViewModel: conversationViewModel, eventLogMessage: row, geometryProxy: geometryProxy)
|
||||
.padding(.vertical, 2)
|
||||
.padding(.horizontal, 10)
|
||||
.onTapGesture { }
|
||||
|
|
@ -354,7 +357,7 @@ struct UIList: UIViewRepresentable {
|
|||
|
||||
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||
let row = sections[indexPath.section].rows[indexPath.row]
|
||||
paginationState.handle(row)
|
||||
paginationState.handle(row.message)
|
||||
}
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
|
|
@ -367,7 +370,10 @@ struct UIList: UIViewRepresentable {
|
|||
if !isScrolledToTop && scrollView.contentOffset.y >= scrollView.contentSize.height - scrollView.frame.height - 200 {
|
||||
self.conversationViewModel.getOldMessages()
|
||||
}
|
||||
isScrolledToTop = scrollView.contentOffset.y >= scrollView.contentSize.height - scrollView.frame.height - 200
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.isScrolledToTop = scrollView.contentOffset.y >= scrollView.contentSize.height - scrollView.frame.height - 200
|
||||
}
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
|
||||
|
|
@ -390,7 +396,7 @@ struct MessagesSection: Equatable {
|
|||
|
||||
let date: Date
|
||||
let chatRoomID: String
|
||||
var rows: [Message]
|
||||
var rows: [EventLogMessage]
|
||||
|
||||
static var formatter = {
|
||||
let formatter = DateFormatter()
|
||||
|
|
@ -398,7 +404,7 @@ struct MessagesSection: Equatable {
|
|||
return formatter
|
||||
}()
|
||||
|
||||
init(date: Date, chatRoomID: String, rows: [Message]) {
|
||||
init(date: Date, chatRoomID: String, rows: [EventLogMessage]) {
|
||||
self.date = date
|
||||
self.chatRoomID = chatRoomID
|
||||
self.rows = rows
|
||||
|
|
@ -414,6 +420,28 @@ struct MessagesSection: Equatable {
|
|||
|
||||
}
|
||||
|
||||
struct EventLogMessage: Equatable {
|
||||
|
||||
let eventLog: EventLog
|
||||
var message: Message
|
||||
|
||||
static var formatter = {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = "EEEE, MMMM d"
|
||||
return formatter
|
||||
}()
|
||||
|
||||
init(eventLog: EventLog, message: Message) {
|
||||
self.eventLog = eventLog
|
||||
self.message = message
|
||||
}
|
||||
|
||||
static func == (lhs: EventLogMessage, rhs: EventLogMessage) -> Bool {
|
||||
lhs.message == rhs.message
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final class PaginationState: ObservableObject {
|
||||
var onEvent: ChatPaginationClosure?
|
||||
var offset: Int
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ public struct Message: Identifiable, Hashable {
|
|||
}
|
||||
|
||||
public var id: String
|
||||
public var appData: String
|
||||
public var status: Status?
|
||||
public var createdAt: Date
|
||||
public var isOutgoing: Bool
|
||||
|
|
@ -79,6 +80,7 @@ public struct Message: Identifiable, Hashable {
|
|||
|
||||
public init(
|
||||
id: String,
|
||||
appData: String = "",
|
||||
status: Status? = nil,
|
||||
createdAt: Date = Date(),
|
||||
isOutgoing: Bool,
|
||||
|
|
@ -94,6 +96,7 @@ public struct Message: Identifiable, Hashable {
|
|||
reactions: [String] = []
|
||||
) {
|
||||
self.id = id
|
||||
self.appData = appData
|
||||
self.status = status
|
||||
self.createdAt = createdAt
|
||||
self.isOutgoing = isOutgoing
|
||||
|
|
|
|||
|
|
@ -45,8 +45,8 @@ class ConversationViewModel: ObservableObject {
|
|||
|
||||
var oldMessageReceived = false
|
||||
|
||||
@Published var selectedMessage: Message?
|
||||
@Published var messageToReply: Message?
|
||||
@Published var selectedMessage: EventLogMessage?
|
||||
@Published var messageToReply: EventLogMessage?
|
||||
|
||||
init() {}
|
||||
|
||||
|
|
@ -79,22 +79,24 @@ class ConversationViewModel: ObservableObject {
|
|||
statusTmp = .received
|
||||
case .Displayed:
|
||||
statusTmp = .read
|
||||
case .NotDelivered:
|
||||
statusTmp = .error
|
||||
default:
|
||||
statusTmp = .sending
|
||||
}
|
||||
|
||||
let indexMessage = self.conversationMessagesSection[0].rows.firstIndex(where: {$0.id == message.messageId})
|
||||
var indexMessage = self.conversationMessagesSection[0].rows.firstIndex(where: {$0.eventLog.chatMessage?.messageId == message.messageId})
|
||||
|
||||
DispatchQueue.main.async {
|
||||
if indexMessage != nil {
|
||||
self.objectWillChange.send()
|
||||
self.conversationMessagesSection[0].rows[indexMessage!].status = statusTmp
|
||||
self.conversationMessagesSection[0].rows[indexMessage!].message.status = statusTmp
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.chatMessageSuscriptions.insert(message.publisher?.onNewMessageReaction?.postOnCoreQueue {(cbValue: (message: ChatMessage, reaction: ChatMessageReaction)) in
|
||||
let indexMessage = self.conversationMessagesSection[0].rows.firstIndex(where: {$0.id == message.messageId})
|
||||
let indexMessage = self.conversationMessagesSection[0].rows.firstIndex(where: {$0.eventLog.chatMessage?.messageId == message.messageId})
|
||||
var reactionsTmp: [String] = []
|
||||
cbValue.message.reactions.forEach({ chatMessageReaction in
|
||||
reactionsTmp.append(chatMessageReaction.body)
|
||||
|
|
@ -103,13 +105,13 @@ class ConversationViewModel: ObservableObject {
|
|||
DispatchQueue.main.async {
|
||||
if indexMessage != nil {
|
||||
self.objectWillChange.send()
|
||||
self.conversationMessagesSection[0].rows[indexMessage!].reactions = reactionsTmp
|
||||
self.conversationMessagesSection[0].rows[indexMessage!].message.reactions = reactionsTmp
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
self.chatMessageSuscriptions.insert(message.publisher?.onReactionRemoved?.postOnCoreQueue {(cbValue: (message: ChatMessage, address: Address)) in
|
||||
let indexMessage = self.conversationMessagesSection[0].rows.firstIndex(where: {$0.id == message.messageId})
|
||||
let indexMessage = self.conversationMessagesSection[0].rows.firstIndex(where: {$0.eventLog.chatMessage?.messageId == message.messageId})
|
||||
var reactionsTmp: [String] = []
|
||||
cbValue.message.reactions.forEach({ chatMessageReaction in
|
||||
reactionsTmp.append(chatMessageReaction.body)
|
||||
|
|
@ -118,7 +120,7 @@ class ConversationViewModel: ObservableObject {
|
|||
DispatchQueue.main.async {
|
||||
if indexMessage != nil {
|
||||
self.objectWillChange.send()
|
||||
self.conversationMessagesSection[0].rows[indexMessage!].reactions = reactionsTmp
|
||||
self.conversationMessagesSection[0].rows[indexMessage!].message.reactions = reactionsTmp
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -156,13 +158,15 @@ class ConversationViewModel: ObservableObject {
|
|||
|
||||
func markAsRead() {
|
||||
coreContext.doOnCoreQueue { _ in
|
||||
let unreadMessagesCount = self.displayedConversation!.chatRoom.unreadMessagesCount
|
||||
|
||||
if unreadMessagesCount > 0 {
|
||||
self.displayedConversation!.chatRoom.markAsRead()
|
||||
if self.displayedConversation != nil {
|
||||
let unreadMessagesCount = self.displayedConversation!.chatRoom.unreadMessagesCount
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.displayedConversationUnreadMessagesCount = 0
|
||||
if unreadMessagesCount > 0 {
|
||||
self.displayedConversation!.chatRoom.markAsRead()
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.displayedConversationUnreadMessagesCount = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -206,7 +210,7 @@ class ConversationViewModel: ObservableObject {
|
|||
if self.displayedConversation != nil {
|
||||
let historyEvents = self.displayedConversation!.chatRoom.getHistoryRangeEvents(begin: 0, end: 30)
|
||||
|
||||
var conversationMessage: [Message] = []
|
||||
var conversationMessage: [EventLogMessage] = []
|
||||
historyEvents.enumerated().forEach { index, eventLog in
|
||||
|
||||
var attachmentNameList: String = ""
|
||||
|
|
@ -342,36 +346,42 @@ class ConversationViewModel: ObservableObject {
|
|||
|
||||
if eventLog.chatMessage != nil {
|
||||
conversationMessage.append(
|
||||
Message(
|
||||
id: eventLog.chatMessage?.messageId ?? UUID().uuidString,
|
||||
status: statusTmp,
|
||||
isOutgoing: eventLog.chatMessage?.isOutgoing ?? false,
|
||||
dateReceived: eventLog.chatMessage?.time ?? 0,
|
||||
address: addressCleaned?.asStringUriOnly() ?? "",
|
||||
isFirstMessage: isFirstMessageTmp,
|
||||
text: contentText,
|
||||
attachmentsNames: attachmentNameList,
|
||||
attachments: attachmentList,
|
||||
replyMessage: replyMessageTmp,
|
||||
ownReaction: eventLog.chatMessage?.ownReaction?.body ?? "",
|
||||
reactions: reactionsTmp
|
||||
EventLogMessage(
|
||||
eventLog: eventLog,
|
||||
message: Message(
|
||||
id: !eventLog.chatMessage!.messageId.isEmpty ? eventLog.chatMessage!.messageId : UUID().uuidString,
|
||||
status: statusTmp,
|
||||
isOutgoing: eventLog.chatMessage?.isOutgoing ?? false,
|
||||
dateReceived: eventLog.chatMessage?.time ?? 0,
|
||||
address: addressCleaned?.asStringUriOnly() ?? "",
|
||||
isFirstMessage: isFirstMessageTmp,
|
||||
text: contentText,
|
||||
attachmentsNames: attachmentNameList,
|
||||
attachments: attachmentList,
|
||||
replyMessage: replyMessageTmp,
|
||||
ownReaction: eventLog.chatMessage?.ownReaction?.body ?? "",
|
||||
reactions: reactionsTmp
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
self.addChatMessageDelegate(message: eventLog.chatMessage!)
|
||||
} else {
|
||||
conversationMessage.insert(
|
||||
Message(
|
||||
id: UUID().uuidString,
|
||||
status: nil,
|
||||
isOutgoing: false,
|
||||
dateReceived: 0,
|
||||
address: "",
|
||||
isFirstMessage: false,
|
||||
text: "",
|
||||
attachments: [],
|
||||
ownReaction: "",
|
||||
reactions: []
|
||||
EventLogMessage(
|
||||
eventLog: eventLog,
|
||||
message: Message(
|
||||
id: UUID().uuidString,
|
||||
status: nil,
|
||||
isOutgoing: false,
|
||||
dateReceived: 0,
|
||||
address: "",
|
||||
isFirstMessage: false,
|
||||
text: "",
|
||||
attachments: [],
|
||||
ownReaction: "",
|
||||
reactions: []
|
||||
)
|
||||
), at: 0
|
||||
)
|
||||
}
|
||||
|
|
@ -391,7 +401,7 @@ class ConversationViewModel: ObservableObject {
|
|||
if self.displayedConversation != nil && self.displayedConversationHistorySize > self.conversationMessagesSection[0].rows.count && !self.oldMessageReceived {
|
||||
self.oldMessageReceived = true
|
||||
let historyEvents = self.displayedConversation!.chatRoom.getHistoryRangeEvents(begin: self.conversationMessagesSection[0].rows.count, end: self.conversationMessagesSection[0].rows.count + 30)
|
||||
var conversationMessagesTmp: [Message] = []
|
||||
var conversationMessagesTmp: [EventLogMessage] = []
|
||||
|
||||
historyEvents.enumerated().reversed().forEach { index, eventLog in
|
||||
var attachmentNameList: String = ""
|
||||
|
|
@ -479,6 +489,8 @@ class ConversationViewModel: ObservableObject {
|
|||
statusTmp = .received
|
||||
case .Displayed:
|
||||
statusTmp = .read
|
||||
case .NotDelivered:
|
||||
statusTmp = .error
|
||||
default:
|
||||
statusTmp = .sending
|
||||
}
|
||||
|
|
@ -525,36 +537,42 @@ class ConversationViewModel: ObservableObject {
|
|||
|
||||
if eventLog.chatMessage != nil {
|
||||
conversationMessagesTmp.insert(
|
||||
Message(
|
||||
id: eventLog.chatMessage?.messageId ?? UUID().uuidString,
|
||||
status: statusTmp,
|
||||
isOutgoing: eventLog.chatMessage?.isOutgoing ?? false,
|
||||
dateReceived: eventLog.chatMessage?.time ?? 0,
|
||||
address: addressCleaned?.asStringUriOnly() ?? "",
|
||||
isFirstMessage: isFirstMessageTmp,
|
||||
text: contentText,
|
||||
attachmentsNames: attachmentNameList,
|
||||
attachments: attachmentList,
|
||||
replyMessage: replyMessageTmp,
|
||||
ownReaction: eventLog.chatMessage?.ownReaction?.body ?? "",
|
||||
reactions: reactionsTmp
|
||||
EventLogMessage(
|
||||
eventLog: eventLog,
|
||||
message: Message(
|
||||
id: !eventLog.chatMessage!.messageId.isEmpty ? eventLog.chatMessage!.messageId : UUID().uuidString,
|
||||
status: statusTmp,
|
||||
isOutgoing: eventLog.chatMessage?.isOutgoing ?? false,
|
||||
dateReceived: eventLog.chatMessage?.time ?? 0,
|
||||
address: addressCleaned?.asStringUriOnly() ?? "",
|
||||
isFirstMessage: isFirstMessageTmp,
|
||||
text: contentText,
|
||||
attachmentsNames: attachmentNameList,
|
||||
attachments: attachmentList,
|
||||
replyMessage: replyMessageTmp,
|
||||
ownReaction: eventLog.chatMessage?.ownReaction?.body ?? "",
|
||||
reactions: reactionsTmp
|
||||
)
|
||||
), at: 0
|
||||
)
|
||||
|
||||
self.addChatMessageDelegate(message: eventLog.chatMessage!)
|
||||
} else {
|
||||
conversationMessagesTmp.insert(
|
||||
Message(
|
||||
id: UUID().uuidString,
|
||||
status: nil,
|
||||
isOutgoing: false,
|
||||
dateReceived: 0,
|
||||
address: "",
|
||||
isFirstMessage: false,
|
||||
text: "",
|
||||
attachments: [],
|
||||
ownReaction: "",
|
||||
reactions: []
|
||||
EventLogMessage(
|
||||
eventLog: eventLog,
|
||||
message: Message(
|
||||
id: UUID().uuidString,
|
||||
status: nil,
|
||||
isOutgoing: false,
|
||||
dateReceived: 0,
|
||||
address: "",
|
||||
isFirstMessage: false,
|
||||
text: "",
|
||||
attachments: [],
|
||||
ownReaction: "",
|
||||
reactions: []
|
||||
)
|
||||
), at: 0
|
||||
)
|
||||
}
|
||||
|
|
@ -562,8 +580,8 @@ class ConversationViewModel: ObservableObject {
|
|||
|
||||
if !conversationMessagesTmp.isEmpty {
|
||||
DispatchQueue.main.async {
|
||||
if self.conversationMessagesSection[0].rows.last?.address == conversationMessagesTmp.last?.address {
|
||||
self.conversationMessagesSection[0].rows[self.conversationMessagesSection[0].rows.count - 1].isFirstMessage = false
|
||||
if self.conversationMessagesSection[0].rows.last?.message.address == conversationMessagesTmp.last?.message.address {
|
||||
self.conversationMessagesSection[0].rows[self.conversationMessagesSection[0].rows.count - 1].message.isFirstMessage = false
|
||||
}
|
||||
self.conversationMessagesSection[0].rows.append(contentsOf: conversationMessagesTmp.reversed())
|
||||
self.oldMessageReceived = false
|
||||
|
|
@ -650,7 +668,7 @@ class ConversationViewModel: ObservableObject {
|
|||
: (
|
||||
self.conversationMessagesSection.isEmpty || self.conversationMessagesSection[0].rows.isEmpty
|
||||
? true
|
||||
: self.conversationMessagesSection[0].rows[0].address != addressCleaned?.asStringUriOnly()
|
||||
: self.conversationMessagesSection[0].rows[0].message.address != addressCleaned?.asStringUriOnly()
|
||||
)
|
||||
|
||||
let isFirstMessageOutgoingTmp = index <= eventLogs.count - 2
|
||||
|
|
@ -658,7 +676,7 @@ class ConversationViewModel: ObservableObject {
|
|||
: (
|
||||
self.conversationMessagesSection.isEmpty || self.conversationMessagesSection[0].rows.isEmpty
|
||||
? true
|
||||
: !self.conversationMessagesSection[0].rows[0].isOutgoing || self.conversationMessagesSection[0].rows[0].address == addressCleaned?.asStringUriOnly()
|
||||
: !self.conversationMessagesSection[0].rows[0].message.isOutgoing || self.conversationMessagesSection[0].rows[0].message.address == addressCleaned?.asStringUriOnly()
|
||||
)
|
||||
|
||||
let isFirstMessageTmp = (eventLog.chatMessage?.isOutgoing ?? false) ? isFirstMessageOutgoingTmp : isFirstMessageIncomingTmp
|
||||
|
|
@ -675,6 +693,8 @@ class ConversationViewModel: ObservableObject {
|
|||
statusTmp = .received
|
||||
case .Displayed:
|
||||
statusTmp = .read
|
||||
case .NotDelivered:
|
||||
statusTmp = .error
|
||||
default:
|
||||
statusTmp = .sending
|
||||
}
|
||||
|
|
@ -720,19 +740,23 @@ class ConversationViewModel: ObservableObject {
|
|||
}
|
||||
|
||||
if eventLog.chatMessage != nil {
|
||||
let message = Message(
|
||||
id: eventLog.chatMessage?.messageId ?? UUID().uuidString,
|
||||
status: statusTmp,
|
||||
isOutgoing: eventLog.chatMessage?.isOutgoing ?? false,
|
||||
dateReceived: eventLog.chatMessage?.time ?? 0,
|
||||
address: addressCleaned?.asStringUriOnly() ?? "",
|
||||
isFirstMessage: isFirstMessageTmp,
|
||||
text: contentText,
|
||||
attachmentsNames: attachmentNameList,
|
||||
attachments: attachmentList,
|
||||
replyMessage: replyMessageTmp,
|
||||
ownReaction: eventLog.chatMessage?.ownReaction?.body ?? "",
|
||||
reactions: reactionsTmp
|
||||
let message = EventLogMessage(
|
||||
eventLog: eventLog,
|
||||
message: Message(
|
||||
id: !eventLog.chatMessage!.messageId.isEmpty ? eventLog.chatMessage!.messageId : UUID().uuidString,
|
||||
appData: eventLog.chatMessage!.appdata ?? "",
|
||||
status: statusTmp,
|
||||
isOutgoing: eventLog.chatMessage?.isOutgoing ?? false,
|
||||
dateReceived: eventLog.chatMessage?.time ?? 0,
|
||||
address: addressCleaned?.asStringUriOnly() ?? "",
|
||||
isFirstMessage: isFirstMessageTmp,
|
||||
text: contentText,
|
||||
attachmentsNames: attachmentNameList,
|
||||
attachments: attachmentList,
|
||||
replyMessage: replyMessageTmp,
|
||||
ownReaction: eventLog.chatMessage?.ownReaction?.body ?? "",
|
||||
reactions: reactionsTmp
|
||||
)
|
||||
)
|
||||
|
||||
self.addChatMessageDelegate(message: eventLog.chatMessage!)
|
||||
|
|
@ -740,9 +764,9 @@ class ConversationViewModel: ObservableObject {
|
|||
DispatchQueue.main.async {
|
||||
if !self.conversationMessagesSection.isEmpty
|
||||
&& !self.conversationMessagesSection[0].rows.isEmpty
|
||||
&& self.conversationMessagesSection[0].rows[0].isOutgoing
|
||||
&& (self.conversationMessagesSection[0].rows[0].address == message.address) {
|
||||
self.conversationMessagesSection[0].rows[0].isFirstMessage = false
|
||||
&& self.conversationMessagesSection[0].rows[0].message.isOutgoing
|
||||
&& (self.conversationMessagesSection[0].rows[0].message.address == message.message.address) {
|
||||
self.conversationMessagesSection[0].rows[0].message.isFirstMessage = false
|
||||
}
|
||||
|
||||
if self.conversationMessagesSection.isEmpty && self.displayedConversation != nil {
|
||||
|
|
@ -751,22 +775,25 @@ class ConversationViewModel: ObservableObject {
|
|||
self.conversationMessagesSection[0].rows.insert(message, at: 0)
|
||||
}
|
||||
|
||||
if !message.isOutgoing {
|
||||
if !message.message.isOutgoing {
|
||||
self.displayedConversationUnreadMessagesCount = unreadMessagesCount
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let message = Message(
|
||||
id: UUID().uuidString,
|
||||
status: nil,
|
||||
isOutgoing: false,
|
||||
dateReceived: 0,
|
||||
address: "",
|
||||
isFirstMessage: false,
|
||||
text: "",
|
||||
attachments: [],
|
||||
ownReaction: "",
|
||||
reactions: []
|
||||
let message = EventLogMessage(
|
||||
eventLog: eventLog,
|
||||
message: Message(
|
||||
id: UUID().uuidString,
|
||||
status: nil,
|
||||
isOutgoing: false,
|
||||
dateReceived: 0,
|
||||
address: "",
|
||||
isFirstMessage: false,
|
||||
text: "",
|
||||
attachments: [],
|
||||
ownReaction: "",
|
||||
reactions: []
|
||||
)
|
||||
)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
|
|
@ -795,10 +822,11 @@ class ConversationViewModel: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
// swiftlint:disable cyclomatic_complexity
|
||||
func scrollToMessage(message: Message) {
|
||||
coreContext.doOnCoreQueue { _ in
|
||||
if message.replyMessage != nil {
|
||||
if let indexMessage = self.conversationMessagesSection[0].rows.firstIndex(where: {$0.id == message.replyMessage!.id}) {
|
||||
if let indexMessage = self.conversationMessagesSection[0].rows.firstIndex(where: {$0.eventLog.chatMessage?.messageId == message.replyMessage!.id}) {
|
||||
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "onScrollToIndex"), object: nil, userInfo: ["index": indexMessage, "animated": true])
|
||||
} else {
|
||||
if self.conversationMessagesSection[0].rows.last != nil {
|
||||
|
|
@ -825,7 +853,7 @@ class ConversationViewModel: ObservableObject {
|
|||
|
||||
historyEvents.insert(contentsOf: historyEventsAfter, at: 0)
|
||||
|
||||
var conversationMessagesTmp: [Message] = []
|
||||
var conversationMessagesTmp: [EventLogMessage] = []
|
||||
|
||||
historyEvents.enumerated().reversed().forEach { index, eventLog in
|
||||
var attachmentNameList: String = ""
|
||||
|
|
@ -913,6 +941,8 @@ class ConversationViewModel: ObservableObject {
|
|||
statusTmp = .received
|
||||
case .Displayed:
|
||||
statusTmp = .read
|
||||
case .NotDelivered:
|
||||
statusTmp = .error
|
||||
default:
|
||||
statusTmp = .sending
|
||||
}
|
||||
|
|
@ -959,36 +989,42 @@ class ConversationViewModel: ObservableObject {
|
|||
|
||||
if eventLog.chatMessage != nil {
|
||||
conversationMessagesTmp.insert(
|
||||
Message(
|
||||
id: eventLog.chatMessage?.messageId ?? UUID().uuidString,
|
||||
status: statusTmp,
|
||||
isOutgoing: eventLog.chatMessage?.isOutgoing ?? false,
|
||||
dateReceived: eventLog.chatMessage?.time ?? 0,
|
||||
address: addressCleaned?.asStringUriOnly() ?? "",
|
||||
isFirstMessage: isFirstMessageTmp,
|
||||
text: contentText,
|
||||
attachmentsNames: attachmentNameList,
|
||||
attachments: attachmentList,
|
||||
replyMessage: replyMessageTmp,
|
||||
ownReaction: eventLog.chatMessage?.ownReaction?.body ?? "",
|
||||
reactions: reactionsTmp
|
||||
EventLogMessage(
|
||||
eventLog: eventLog,
|
||||
message: Message(
|
||||
id: !eventLog.chatMessage!.messageId.isEmpty ? eventLog.chatMessage!.messageId : UUID().uuidString,
|
||||
status: statusTmp,
|
||||
isOutgoing: eventLog.chatMessage?.isOutgoing ?? false,
|
||||
dateReceived: eventLog.chatMessage?.time ?? 0,
|
||||
address: addressCleaned?.asStringUriOnly() ?? "",
|
||||
isFirstMessage: isFirstMessageTmp,
|
||||
text: contentText,
|
||||
attachmentsNames: attachmentNameList,
|
||||
attachments: attachmentList,
|
||||
replyMessage: replyMessageTmp,
|
||||
ownReaction: eventLog.chatMessage?.ownReaction?.body ?? "",
|
||||
reactions: reactionsTmp
|
||||
)
|
||||
), at: 0
|
||||
)
|
||||
|
||||
self.addChatMessageDelegate(message: eventLog.chatMessage!)
|
||||
} else {
|
||||
conversationMessagesTmp.insert(
|
||||
Message(
|
||||
id: UUID().uuidString,
|
||||
status: nil,
|
||||
isOutgoing: false,
|
||||
dateReceived: 0,
|
||||
address: "",
|
||||
isFirstMessage: false,
|
||||
text: "",
|
||||
attachments: [],
|
||||
ownReaction: "",
|
||||
reactions: []
|
||||
EventLogMessage(
|
||||
eventLog: eventLog,
|
||||
message: Message(
|
||||
id: UUID().uuidString,
|
||||
status: nil,
|
||||
isOutgoing: false,
|
||||
dateReceived: 0,
|
||||
address: "",
|
||||
isFirstMessage: false,
|
||||
text: "",
|
||||
attachments: [],
|
||||
ownReaction: "",
|
||||
reactions: []
|
||||
)
|
||||
), at: 0
|
||||
)
|
||||
}
|
||||
|
|
@ -996,8 +1032,8 @@ class ConversationViewModel: ObservableObject {
|
|||
|
||||
if !conversationMessagesTmp.isEmpty {
|
||||
DispatchQueue.main.async {
|
||||
if self.conversationMessagesSection[0].rows.last?.address == conversationMessagesTmp.last?.address {
|
||||
self.conversationMessagesSection[0].rows[self.conversationMessagesSection[0].rows.count - 1].isFirstMessage = false
|
||||
if self.conversationMessagesSection[0].rows.last?.message.address == conversationMessagesTmp.last?.message.address {
|
||||
self.conversationMessagesSection[0].rows[self.conversationMessagesSection[0].rows.count - 1].message.isFirstMessage = false
|
||||
}
|
||||
self.conversationMessagesSection[0].rows.append(contentsOf: conversationMessagesTmp.reversed())
|
||||
|
||||
|
|
@ -1013,13 +1049,14 @@ class ConversationViewModel: ObservableObject {
|
|||
}
|
||||
}
|
||||
}
|
||||
// swiftlint:enable cyclomatic_complexity
|
||||
|
||||
func sendMessage() {
|
||||
coreContext.doOnCoreQueue { _ in
|
||||
do {
|
||||
var message: ChatMessage?
|
||||
if self.messageToReply != nil {
|
||||
let chatMessageToReply = self.displayedConversation!.chatRoom.findMessage(messageId: self.messageToReply!.id)
|
||||
let chatMessageToReply = self.messageToReply!.eventLog.chatMessage
|
||||
if chatMessageToReply != nil {
|
||||
message = try self.displayedConversation!.chatRoom.createReplyMessage(message: chatMessageToReply!)
|
||||
}
|
||||
|
|
@ -1264,8 +1301,8 @@ class ConversationViewModel: ObservableObject {
|
|||
func sendReaction(emoji: String) {
|
||||
coreContext.doOnCoreQueue { _ in
|
||||
if self.selectedMessage != nil {
|
||||
Log.info("[ConversationViewModel] Sending reaction \(emoji) to message with ID \(self.selectedMessage!.id)")
|
||||
let messageToSendReaction = self.displayedConversation!.chatRoom.findMessage(messageId: self.selectedMessage!.id)
|
||||
Log.info("[ConversationViewModel] Sending reaction \(emoji) to message with ID \(self.selectedMessage!.message.id)")
|
||||
let messageToSendReaction = self.selectedMessage!.eventLog.chatMessage
|
||||
if messageToSendReaction != nil {
|
||||
do {
|
||||
let reaction = try messageToSendReaction!.createReaction(utf8Reaction: messageToSendReaction?.ownReaction?.body == emoji ? "" : emoji)
|
||||
|
|
@ -1275,12 +1312,12 @@ class ConversationViewModel: ObservableObject {
|
|||
|
||||
DispatchQueue.main.async {
|
||||
if indexMessageSelected != nil {
|
||||
self.conversationMessagesSection[0].rows[indexMessageSelected!].ownReaction = messageToSendReaction?.ownReaction?.body == emoji ? "" : emoji
|
||||
self.conversationMessagesSection[0].rows[indexMessageSelected!].message.ownReaction = messageToSendReaction?.ownReaction?.body == emoji ? "" : emoji
|
||||
}
|
||||
self.selectedMessage = nil
|
||||
}
|
||||
} catch {
|
||||
Log.info("[ConversationViewModel] Error: Can't send reaction \(emoji) to message with ID \(self.selectedMessage!.id)")
|
||||
Log.info("[ConversationViewModel] Error: Can't send reaction \(emoji) to message with ID \(self.selectedMessage!.message.id)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1289,28 +1326,11 @@ class ConversationViewModel: ObservableObject {
|
|||
|
||||
func resend() {
|
||||
coreContext.doOnCoreQueue { _ in
|
||||
if self.selectedMessage != nil {
|
||||
Log.info("[ConversationViewModel] Re-sending message with ID \(self.selectedMessage!.id)")
|
||||
let messageToResend = self.displayedConversation!.chatRoom.findMessage(messageId: self.selectedMessage!.id)
|
||||
if messageToResend != nil {
|
||||
messageToResend!.send()
|
||||
}
|
||||
if self.selectedMessage != nil && self.selectedMessage!.eventLog.chatMessage != nil {
|
||||
Log.info("[ConversationViewModel] Re-sending message with ID \(self.selectedMessage!.eventLog.chatMessage!)")
|
||||
self.selectedMessage!.eventLog.chatMessage!.send()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
struct LinphoneCustomEventLog: Hashable {
|
||||
var id = UUID()
|
||||
var eventLog: EventLog
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(id)
|
||||
}
|
||||
}
|
||||
|
||||
extension LinphoneCustomEventLog {
|
||||
static func ==(lhs: LinphoneCustomEventLog, rhs: LinphoneCustomEventLog) -> Bool {
|
||||
return lhs.id == rhs.id
|
||||
}
|
||||
}
|
||||
// swiftlint:enable type_body_length
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue