forked from mirrors/linphone-iphone
Add swipe action in message list
This commit is contained in:
parent
8045c4af2d
commit
3ba5fd5f38
10 changed files with 325 additions and 145 deletions
21
Linphone/Assets.xcassets/reply-reversed.imageset/Contents.json
vendored
Normal file
21
Linphone/Assets.xcassets/reply-reversed.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "reply-reversed.svg",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
5
Linphone/Assets.xcassets/reply-reversed.imageset/reply-reversed.svg
vendored
Normal file
5
Linphone/Assets.xcassets/reply-reversed.imageset/reply-reversed.svg
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Répondre" transform="translate(0, 28) scale(1, -1)">
|
||||
<path id="Vector" d="M2.57775 14.19L10.9109 22.5231C11.0274 22.6398 11.176 22.7193 11.3377 22.7515C11.4994 22.7837 11.6671 22.7672 11.8194 22.7041C11.9718 22.6409 12.102 22.534 12.1935 22.3969C12.2851 22.2597 12.3339 22.0985 12.3338 21.9336V17.791C18.2815 18.1285 22.3595 21.9856 23.5043 23.2075C23.6841 23.3995 23.9198 23.5298 24.1779 23.5801C24.4361 23.6303 24.7035 23.5979 24.9421 23.4873C25.1808 23.3768 25.3784 23.1938 25.507 22.9644C25.6356 22.735 25.6886 22.4708 25.6584 22.2096C25.272 18.8493 23.4314 15.6171 20.4752 13.1088C18.0201 11.0255 15.0358 9.70366 12.3338 9.47242V5.26732C12.3339 5.10241 12.2851 4.94117 12.1935 4.80401C12.102 4.66685 11.9718 4.55994 11.8194 4.49682C11.6671 4.43369 11.4994 4.41719 11.3377 4.4494C11.176 4.48161 11.0274 4.56108 10.9109 4.67775L2.57775 13.0109C2.50027 13.0883 2.43881 13.1802 2.39688 13.2813C2.35494 13.3825 2.33335 13.4909 2.33335 13.6004C2.33335 13.71 2.35494 13.8184 2.39688 13.9195C2.43881 14.0207 2.50027 14.1126 2.57775 14.19Z" fill="#6C7A87"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
|
|
@ -27,6 +27,9 @@
|
|||
},
|
||||
"*" : {
|
||||
|
||||
},
|
||||
"**%@**" : {
|
||||
|
||||
},
|
||||
"**Camera** : Pour capturer votre vidéo lors des appels vidéo et conférence." : {
|
||||
|
||||
|
|
@ -969,6 +972,23 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"conversation_reply_to_message_title" : {
|
||||
"extractionState" : "manual",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Replying to: "
|
||||
}
|
||||
},
|
||||
"fr" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "En réponse à : "
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Conversations" : {
|
||||
|
||||
},
|
||||
|
|
|
|||
|
|
@ -33,159 +33,168 @@ struct ChatBubbleView: View {
|
|||
@State private var timePassed: TimeInterval?
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
if !message.text.isEmpty || !message.attachments.isEmpty {
|
||||
HStack(alignment: .top, content: {
|
||||
if message.isOutgoing {
|
||||
Spacer()
|
||||
}
|
||||
|
||||
if conversationViewModel.displayedConversation != nil && conversationViewModel.displayedConversation!.isGroup && !message.isOutgoing && message.isFirstMessage {
|
||||
VStack {
|
||||
Avatar(
|
||||
contactAvatarModel: conversationViewModel.participantConversationModel.first(where: {$0.address == message.address}) ??
|
||||
ContactAvatarModel(friend: nil, name: "??", address: "", withPresence: false),
|
||||
avatarSize: 35
|
||||
)
|
||||
.padding(.top, 30)
|
||||
HStack {
|
||||
VStack {
|
||||
if !message.text.isEmpty || !message.attachments.isEmpty {
|
||||
HStack(alignment: .top, content: {
|
||||
if message.isOutgoing {
|
||||
Spacer()
|
||||
}
|
||||
} else if conversationViewModel.displayedConversation != nil && conversationViewModel.displayedConversation!.isGroup && !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 ?? "")
|
||||
.default_text_style(styleSize: 12)
|
||||
.padding(.top, 10)
|
||||
.padding(.bottom, 2)
|
||||
VStack {
|
||||
Avatar(
|
||||
contactAvatarModel: conversationViewModel.participantConversationModel.first(where: {$0.address == message.address}) ??
|
||||
ContactAvatarModel(friend: nil, name: "??", address: "", withPresence: false),
|
||||
avatarSize: 35
|
||||
)
|
||||
.padding(.top, 30)
|
||||
}
|
||||
} else if conversationViewModel.displayedConversation != nil && conversationViewModel.displayedConversation!.isGroup && !message.isOutgoing {
|
||||
VStack {
|
||||
}
|
||||
.padding(.leading, 43)
|
||||
}
|
||||
ZStack {
|
||||
|
||||
HStack {
|
||||
if message.isOutgoing {
|
||||
Spacer()
|
||||
}
|
||||
|
||||
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 ?? "")
|
||||
.default_text_style(styleSize: 12)
|
||||
.padding(.top, 10)
|
||||
.padding(.bottom, 2)
|
||||
}
|
||||
ZStack {
|
||||
|
||||
VStack(alignment: message.isOutgoing ? .trailing : .leading) {
|
||||
HStack {
|
||||
if message.isOutgoing {
|
||||
Spacer()
|
||||
}
|
||||
|
||||
VStack(alignment: message.isOutgoing ? .trailing : .leading) {
|
||||
if !message.attachments.isEmpty {
|
||||
messageAttachments()
|
||||
}
|
||||
|
||||
if !message.text.isEmpty {
|
||||
Text(message.text)
|
||||
.foregroundStyle(Color.grayMain2c700)
|
||||
.default_text_style(styleSize: 16)
|
||||
}
|
||||
|
||||
HStack(alignment: .center) {
|
||||
Text(conversationViewModel.getMessageTime(startDate: message.dateReceived))
|
||||
.foregroundStyle(Color.grayMain2c500)
|
||||
.default_text_style_300(styleSize: 14)
|
||||
.padding(.top, 1)
|
||||
VStack(alignment: message.isOutgoing ? .trailing : .leading) {
|
||||
if !message.attachments.isEmpty {
|
||||
messageAttachments()
|
||||
}
|
||||
|
||||
if (conversationViewModel.displayedConversation != nil && conversationViewModel.displayedConversation!.isGroup) || message.isOutgoing {
|
||||
if message.status == .sending {
|
||||
ProgressView()
|
||||
.progressViewStyle(CircularProgressViewStyle(tint: .orangeMain500))
|
||||
.frame(width: 15, height: 15)
|
||||
.padding(.top, 1)
|
||||
} else if message.status != nil {
|
||||
Image(conversationViewModel.getImageIMDN(status: message.status!))
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(Color.orangeMain500)
|
||||
.frame(width: 15, height: 15)
|
||||
.padding(.top, 1)
|
||||
if !message.text.isEmpty {
|
||||
Text(message.text)
|
||||
.foregroundStyle(Color.grayMain2c700)
|
||||
.default_text_style(styleSize: 16)
|
||||
}
|
||||
|
||||
HStack(alignment: .center) {
|
||||
Text(conversationViewModel.getMessageTime(startDate: 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 {
|
||||
ProgressView()
|
||||
.progressViewStyle(CircularProgressViewStyle(tint: .orangeMain500))
|
||||
.frame(width: 15, height: 15)
|
||||
.padding(.top, 1)
|
||||
} else if message.status != nil {
|
||||
Image(conversationViewModel.getImageIMDN(status: message.status!))
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.foregroundStyle(Color.orangeMain500)
|
||||
.frame(width: 15, height: 15)
|
||||
.padding(.top, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.top, -4)
|
||||
}
|
||||
.padding(.top, -4)
|
||||
}
|
||||
.padding(.all, 15)
|
||||
.background(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]))
|
||||
|
||||
if !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])
|
||||
.padding(.all, 15)
|
||||
.background(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]))
|
||||
|
||||
if !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])
|
||||
.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)")
|
||||
.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)")
|
||||
.default_text_style(styleSize: 14)
|
||||
.padding(.horizontal, -2)
|
||||
}
|
||||
.padding(.vertical, 6)
|
||||
.padding(.horizontal, 10)
|
||||
.background(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(.vertical, 6)
|
||||
.padding(.horizontal, 10)
|
||||
.background(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)
|
||||
}
|
||||
|
||||
if !message.isOutgoing {
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
|
||||
if !message.isOutgoing {
|
||||
Spacer()
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
|
||||
if !message.isOutgoing {
|
||||
Spacer()
|
||||
}
|
||||
})
|
||||
.padding(.leading, message.isOutgoing ? 40 : 0)
|
||||
.padding(.trailing, !message.isOutgoing ? 40 : 0)
|
||||
|
||||
if !message.isOutgoing {
|
||||
Spacer()
|
||||
}
|
||||
})
|
||||
.padding(.leading, message.isOutgoing ? 40 : 0)
|
||||
.padding(.trailing, !message.isOutgoing ? 40 : 0)
|
||||
}
|
||||
}
|
||||
.onTapGesture {}
|
||||
.onLongPressGesture(minimumDuration: .infinity, maximumDistance: .infinity, pressing: { (value) in
|
||||
self.isPressed = value
|
||||
if value == true {
|
||||
self.timePassed = 0
|
||||
self.ticker.start(interval: 0.2)
|
||||
}
|
||||
|
||||
}, perform: {})
|
||||
.onReceive(ticker.objectWillChange) { (_) in
|
||||
// Stop timer and reset the start date if the button in not pressed
|
||||
guard self.isPressed else {
|
||||
self.ticker.stop()
|
||||
return
|
||||
}
|
||||
|
||||
self.timePassed = self.ticker.timeIntervalSinceStarted
|
||||
withAnimation {
|
||||
conversationViewModel.selectedMessage = message
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
.onTapGesture {}
|
||||
.onLongPressGesture(minimumDuration: .infinity, maximumDistance: .infinity, pressing: { (value) in
|
||||
self.isPressed = value
|
||||
if value == true {
|
||||
self.timePassed = 0
|
||||
self.ticker.start(interval: 0.2)
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
if conversationViewModel.selectedMessage != nil {
|
||||
conversationViewModel.selectedMessage = nil
|
||||
}
|
||||
|
||||
}, perform: {})
|
||||
.onReceive(ticker.objectWillChange) { (_) in
|
||||
// Stop timer and reset the start date if the button in not pressed
|
||||
guard self.isPressed else {
|
||||
self.ticker.stop()
|
||||
return
|
||||
}
|
||||
|
||||
self.timePassed = self.ticker.timeIntervalSinceStarted
|
||||
withAnimation {
|
||||
conversationViewModel.selectedMessage = message
|
||||
}
|
||||
|
||||
UIApplication.shared.endEditing()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -222,9 +222,6 @@ struct ConversationFragment: View {
|
|||
.padding()
|
||||
}
|
||||
}
|
||||
.onTapGesture {
|
||||
UIApplication.shared.endEditing()
|
||||
}
|
||||
.onAppear {
|
||||
conversationViewModel.getMessages()
|
||||
}
|
||||
|
|
@ -315,9 +312,6 @@ struct ConversationFragment: View {
|
|||
.padding()
|
||||
}
|
||||
}
|
||||
.onTapGesture {
|
||||
UIApplication.shared.endEditing()
|
||||
}
|
||||
.onAppear {
|
||||
conversationViewModel.getMessages()
|
||||
}
|
||||
|
|
@ -327,6 +321,53 @@ struct ConversationFragment: View {
|
|||
}
|
||||
}
|
||||
|
||||
if conversationViewModel.messageToReply != nil {
|
||||
ZStack(alignment: .top) {
|
||||
HStack {
|
||||
VStack {
|
||||
(
|
||||
Text("conversation_reply_to_message_title")
|
||||
+ Text("**\(conversationViewModel.participantConversationModel.first(where: {$0.address == conversationViewModel.messageToReply!.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)
|
||||
.default_text_style_300(styleSize: 15)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.lineLimit(1)
|
||||
} else {
|
||||
Text("\(conversationViewModel.messageToReply!.text)")
|
||||
.default_text_style_300(styleSize: 15)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.all, 20)
|
||||
.background(Color.gray100)
|
||||
|
||||
HStack {
|
||||
Spacer()
|
||||
|
||||
Button(action: {
|
||||
withAnimation {
|
||||
conversationViewModel.messageToReply = nil
|
||||
}
|
||||
}, label: {
|
||||
Image("x")
|
||||
.resizable()
|
||||
.frame(width: 30, height: 30, alignment: .leading)
|
||||
.padding(.all, 10)
|
||||
})
|
||||
}
|
||||
}
|
||||
.transition(.move(edge: .bottom))
|
||||
}
|
||||
|
||||
if !conversationViewModel.mediasToSend.isEmpty || mediasIsLoading {
|
||||
ZStack(alignment: .top) {
|
||||
HStack {
|
||||
|
|
@ -636,6 +677,9 @@ struct ConversationFragment: View {
|
|||
|
||||
VStack {
|
||||
Button {
|
||||
let indexMessage = conversationViewModel.conversationMessagesSection[0].rows.firstIndex(where: {$0.id == conversationViewModel.selectedMessage!.id})
|
||||
conversationViewModel.selectedMessage = nil
|
||||
conversationViewModel.replyToMessage(index: indexMessage ?? 0)
|
||||
} label: {
|
||||
HStack {
|
||||
Text("menu_reply_to_chat_message")
|
||||
|
|
@ -819,7 +863,7 @@ struct ImagePicker: UIViewControllerRepresentable {
|
|||
if data != nil {
|
||||
do {
|
||||
let decodedData: () = try data!.write(to: path)
|
||||
let attachment = Attachment(id: UUID().uuidString, url: path, type: .image)
|
||||
let attachment = Attachment(id: UUID().uuidString, name: (dateString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? "") + ".jpeg", url: path, type: .image)
|
||||
parent.selectedMedia.append(attachment)
|
||||
} catch {
|
||||
}
|
||||
|
|
@ -836,6 +880,7 @@ struct ImagePicker: UIViewControllerRepresentable {
|
|||
let attachment =
|
||||
Attachment(
|
||||
id: UUID().uuidString,
|
||||
name: name,
|
||||
thumbnail: pathThumbnail!,
|
||||
full: videoUrl!,
|
||||
type: .video
|
||||
|
|
|
|||
|
|
@ -338,6 +338,20 @@ struct UIList: UIViewRepresentable {
|
|||
}
|
||||
isScrolledToTop = scrollView.contentOffset.y >= scrollView.contentSize.height - scrollView.frame.height - 200
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
|
||||
|
||||
let archiveAction = UIContextualAction(style: .normal, title: "") { action, view, completionHandler in
|
||||
self.conversationViewModel.replyToMessage(index: indexPath.row)
|
||||
completionHandler(true)
|
||||
}
|
||||
|
||||
archiveAction.image = UIImage(named: "reply-reversed")!
|
||||
|
||||
let configuration = UISwipeActionsConfiguration(actions: [archiveAction])
|
||||
|
||||
return configuration
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,18 +45,20 @@ public enum AttachmentType: String, Codable {
|
|||
|
||||
public struct Attachment: Codable, Identifiable, Hashable {
|
||||
public let id: String
|
||||
public let name: String
|
||||
public let thumbnail: URL
|
||||
public let full: URL
|
||||
public let type: AttachmentType
|
||||
|
||||
public init(id: String, thumbnail: URL, full: URL, type: AttachmentType) {
|
||||
public init(id: String, name: String, thumbnail: URL, full: URL, type: AttachmentType) {
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.thumbnail = thumbnail
|
||||
self.full = full
|
||||
self.type = type
|
||||
}
|
||||
|
||||
public init(id: String, url: URL, type: AttachmentType) {
|
||||
self.init(id: id, thumbnail: url, full: url, type: type)
|
||||
public init(id: String, name: String, url: URL, type: AttachmentType) {
|
||||
self.init(id: id, name: name, thumbnail: url, full: url, type: type)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ public struct Message: Identifiable, Hashable {
|
|||
public var address: String
|
||||
public var isFirstMessage: Bool
|
||||
public var text: String
|
||||
public var attachmentsNames: String
|
||||
public var attachments: [Attachment]
|
||||
public var recording: Recording?
|
||||
public var replyMessage: ReplyMessage?
|
||||
|
|
@ -85,6 +86,7 @@ public struct Message: Identifiable, Hashable {
|
|||
address: String,
|
||||
isFirstMessage: Bool = false,
|
||||
text: String = "",
|
||||
attachmentsNames: String = "",
|
||||
attachments: [Attachment] = [],
|
||||
recording: Recording? = nil,
|
||||
replyMessage: ReplyMessage? = nil,
|
||||
|
|
@ -99,6 +101,7 @@ public struct Message: Identifiable, Hashable {
|
|||
self.isFirstMessage = isFirstMessage
|
||||
self.address = address
|
||||
self.text = text
|
||||
self.attachmentsNames = attachmentsNames
|
||||
self.attachments = attachments
|
||||
self.recording = recording
|
||||
self.replyMessage = replyMessage
|
||||
|
|
@ -117,12 +120,12 @@ public struct Message: Identifiable, Hashable {
|
|||
|
||||
switch media.type {
|
||||
case .image:
|
||||
return Attachment(id: UUID().uuidString, url: thumbnailURL, type: .image)
|
||||
return Attachment(id: UUID().uuidString, name: "", url: thumbnailURL, type: .image)
|
||||
case .video:
|
||||
guard let fullURL = await media.getURL() else {
|
||||
return nil
|
||||
}
|
||||
return Attachment(id: UUID().uuidString, thumbnail: thumbnailURL, full: fullURL, type: .video)
|
||||
return Attachment(id: UUID().uuidString, name: "", thumbnail: thumbnailURL, full: fullURL, type: .video)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ class ConversationViewModel: ObservableObject {
|
|||
var oldMessageReceived = false
|
||||
|
||||
@Published var selectedMessage: Message?
|
||||
@Published var messageToReply: Message?
|
||||
|
||||
init() {}
|
||||
|
||||
|
|
@ -180,6 +181,15 @@ class ConversationViewModel: ObservableObject {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.displayedConversation!.chatRoom.me != nil {
|
||||
ContactAvatarModel.getAvatarModelFromAddress(address: self.displayedConversation!.chatRoom.me!.address!) { avatarResult in
|
||||
let avatarModelTmp = avatarResult
|
||||
DispatchQueue.main.async {
|
||||
self.participantConversationModel.append(avatarModelTmp)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -188,6 +198,10 @@ class ConversationViewModel: ObservableObject {
|
|||
self.getHistorySize()
|
||||
self.getUnreadMessagesCount()
|
||||
self.getParticipantConversationModel()
|
||||
|
||||
self.mediasToSend.removeAll()
|
||||
self.messageToReply = nil
|
||||
|
||||
coreContext.doOnCoreQueue { _ in
|
||||
if self.displayedConversation != nil {
|
||||
let historyEvents = self.displayedConversation!.chatRoom.getHistoryRangeEvents(begin: 0, end: 30)
|
||||
|
|
@ -195,6 +209,7 @@ class ConversationViewModel: ObservableObject {
|
|||
var conversationMessage: [Message] = []
|
||||
historyEvents.enumerated().forEach { index, eventLog in
|
||||
|
||||
var attachmentNameList: String = ""
|
||||
var attachmentList: [Attachment] = []
|
||||
var contentText = ""
|
||||
|
||||
|
|
@ -211,9 +226,11 @@ class ConversationViewModel: ObservableObject {
|
|||
let attachment =
|
||||
Attachment(
|
||||
id: UUID().uuidString,
|
||||
name: content.name!,
|
||||
url: path!,
|
||||
type: .image
|
||||
)
|
||||
attachmentNameList += ", \(content.name!)"
|
||||
attachmentList.append(attachment)
|
||||
}
|
||||
} else {
|
||||
|
|
@ -224,9 +241,11 @@ class ConversationViewModel: ObservableObject {
|
|||
let attachment =
|
||||
Attachment(
|
||||
id: UUID().uuidString,
|
||||
name: content.name!,
|
||||
url: path!,
|
||||
type: (content.name?.lowercased().hasSuffix("gif"))! ? .gif : .image
|
||||
)
|
||||
attachmentNameList += ", \(content.name!)"
|
||||
attachmentList.append(attachment)
|
||||
}
|
||||
} else if content.type == "video" {
|
||||
|
|
@ -237,10 +256,12 @@ class ConversationViewModel: ObservableObject {
|
|||
let attachment =
|
||||
Attachment(
|
||||
id: UUID().uuidString,
|
||||
name: content.name!,
|
||||
thumbnail: pathThumbnail!,
|
||||
full: path!,
|
||||
type: .video
|
||||
)
|
||||
attachmentNameList += ", \(content.name!)"
|
||||
attachmentList.append(attachment)
|
||||
}
|
||||
}
|
||||
|
|
@ -282,6 +303,10 @@ class ConversationViewModel: ObservableObject {
|
|||
reactionsTmp.append(chatMessageReaction.body)
|
||||
})
|
||||
|
||||
if !attachmentNameList.isEmpty {
|
||||
attachmentNameList = String(attachmentNameList.dropFirst(2))
|
||||
}
|
||||
|
||||
if eventLog.chatMessage != nil {
|
||||
conversationMessage.append(
|
||||
Message(
|
||||
|
|
@ -292,6 +317,7 @@ class ConversationViewModel: ObservableObject {
|
|||
address: addressCleaned?.asStringUriOnly() ?? "",
|
||||
isFirstMessage: isFirstMessageTmp,
|
||||
text: contentText,
|
||||
attachmentsNames: attachmentNameList,
|
||||
attachments: attachmentList,
|
||||
ownReaction: eventLog.chatMessage?.ownReaction?.body ?? "",
|
||||
reactions: reactionsTmp
|
||||
|
|
@ -334,6 +360,7 @@ class ConversationViewModel: ObservableObject {
|
|||
var conversationMessagesTmp: [Message] = []
|
||||
|
||||
historyEvents.enumerated().reversed().forEach { index, eventLog in
|
||||
var attachmentNameList: String = ""
|
||||
var attachmentList: [Attachment] = []
|
||||
var contentText = ""
|
||||
|
||||
|
|
@ -350,9 +377,11 @@ class ConversationViewModel: ObservableObject {
|
|||
let attachment =
|
||||
Attachment(
|
||||
id: UUID().uuidString,
|
||||
name: content.name!,
|
||||
url: path!,
|
||||
type: .image
|
||||
)
|
||||
attachmentNameList += ", \(content.name!)"
|
||||
attachmentList.append(attachment)
|
||||
}
|
||||
} else {
|
||||
|
|
@ -363,9 +392,11 @@ class ConversationViewModel: ObservableObject {
|
|||
let attachment =
|
||||
Attachment(
|
||||
id: UUID().uuidString,
|
||||
name: content.name!,
|
||||
url: path!,
|
||||
type: (content.name?.lowercased().hasSuffix("gif"))! ? .gif : .image
|
||||
)
|
||||
attachmentNameList += ", \(content.name!)"
|
||||
attachmentList.append(attachment)
|
||||
}
|
||||
} else if content.type == "video" {
|
||||
|
|
@ -376,10 +407,12 @@ class ConversationViewModel: ObservableObject {
|
|||
let attachment =
|
||||
Attachment(
|
||||
id: UUID().uuidString,
|
||||
name: content.name!,
|
||||
thumbnail: pathThumbnail!,
|
||||
full: path!,
|
||||
type: .video
|
||||
)
|
||||
attachmentNameList += ", \(content.name!)"
|
||||
attachmentList.append(attachment)
|
||||
}
|
||||
}
|
||||
|
|
@ -421,6 +454,10 @@ class ConversationViewModel: ObservableObject {
|
|||
reactionsTmp.append(chatMessageReaction.body)
|
||||
})
|
||||
|
||||
if !attachmentNameList.isEmpty {
|
||||
attachmentNameList = String(attachmentNameList.dropFirst(2))
|
||||
}
|
||||
|
||||
if eventLog.chatMessage != nil {
|
||||
conversationMessagesTmp.insert(
|
||||
Message(
|
||||
|
|
@ -431,6 +468,7 @@ class ConversationViewModel: ObservableObject {
|
|||
address: addressCleaned?.asStringUriOnly() ?? "",
|
||||
isFirstMessage: isFirstMessageTmp,
|
||||
text: contentText,
|
||||
attachmentsNames: attachmentNameList,
|
||||
attachments: attachmentList,
|
||||
ownReaction: eventLog.chatMessage?.ownReaction?.body ?? "",
|
||||
reactions: reactionsTmp
|
||||
|
|
@ -471,6 +509,7 @@ class ConversationViewModel: ObservableObject {
|
|||
|
||||
func getNewMessages(eventLogs: [EventLog]) {
|
||||
eventLogs.enumerated().forEach { index, eventLog in
|
||||
var attachmentNameList: String = ""
|
||||
var attachmentList: [Attachment] = []
|
||||
var contentText = ""
|
||||
|
||||
|
|
@ -487,9 +526,11 @@ class ConversationViewModel: ObservableObject {
|
|||
let attachment =
|
||||
Attachment(
|
||||
id: UUID().uuidString,
|
||||
name: content.name!,
|
||||
url: path!,
|
||||
type: .image
|
||||
)
|
||||
attachmentNameList += ", \(content.name!)"
|
||||
attachmentList.append(attachment)
|
||||
}
|
||||
} else if content.name != nil && !content.name!.isEmpty {
|
||||
|
|
@ -500,9 +541,11 @@ class ConversationViewModel: ObservableObject {
|
|||
let attachment =
|
||||
Attachment(
|
||||
id: UUID().uuidString,
|
||||
name: content.name!,
|
||||
url: path!,
|
||||
type: (content.name?.lowercased().hasSuffix("gif"))! ? .gif : .image
|
||||
)
|
||||
attachmentNameList += ", \(content.name!)"
|
||||
attachmentList.append(attachment)
|
||||
}
|
||||
} else if content.type == "video" {
|
||||
|
|
@ -513,10 +556,12 @@ class ConversationViewModel: ObservableObject {
|
|||
let attachment =
|
||||
Attachment(
|
||||
id: UUID().uuidString,
|
||||
name: content.name!,
|
||||
thumbnail: pathThumbnail!,
|
||||
full: path!,
|
||||
type: .video
|
||||
)
|
||||
attachmentNameList += ", \(content.name!)"
|
||||
attachmentList.append(attachment)
|
||||
}
|
||||
}
|
||||
|
|
@ -573,6 +618,10 @@ class ConversationViewModel: ObservableObject {
|
|||
reactionsTmp.append(chatMessageReaction.body)
|
||||
})
|
||||
|
||||
if !attachmentNameList.isEmpty {
|
||||
attachmentNameList = String(attachmentNameList.dropFirst(2))
|
||||
}
|
||||
|
||||
if eventLog.chatMessage != nil {
|
||||
let message = Message(
|
||||
id: eventLog.chatMessage?.messageId ?? UUID().uuidString,
|
||||
|
|
@ -582,6 +631,7 @@ class ConversationViewModel: ObservableObject {
|
|||
address: addressCleaned?.asStringUriOnly() ?? "",
|
||||
isFirstMessage: isFirstMessageTmp,
|
||||
text: contentText,
|
||||
attachmentsNames: attachmentNameList,
|
||||
attachments: attachmentList,
|
||||
ownReaction: eventLog.chatMessage?.ownReaction?.body ?? "",
|
||||
reactions: reactionsTmp
|
||||
|
|
@ -636,6 +686,17 @@ class ConversationViewModel: ObservableObject {
|
|||
conversationMessagesSection = []
|
||||
}
|
||||
|
||||
func replyToMessage(index: Int) {
|
||||
coreContext.doOnCoreQueue { _ in
|
||||
let messageToReplyTmp = self.conversationMessagesSection[0].rows[index]
|
||||
DispatchQueue.main.async {
|
||||
withAnimation(.linear(duration: 0.15)) {
|
||||
self.messageToReply = messageToReplyTmp
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sendMessage() {
|
||||
coreContext.doOnCoreQueue { _ in
|
||||
//val messageToReplyTo = chatMessageToReplyTo
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ struct PhotoPicker: UIViewControllerRepresentable {
|
|||
let dataResult = try Data(contentsOf: urlFile!)
|
||||
let urlImage = self.saveMedia(name: urlFile!.lastPathComponent, data: dataResult, type: .image)
|
||||
if urlImage != nil {
|
||||
let attachment = Attachment(id: UUID().uuidString, url: urlImage!, type: .image)
|
||||
let attachment = Attachment(id: UUID().uuidString, name: urlFile!.lastPathComponent, url: urlImage!, type: .image)
|
||||
medias.append(attachment)
|
||||
}
|
||||
} catch {
|
||||
|
|
@ -98,7 +98,7 @@ struct PhotoPicker: UIViewControllerRepresentable {
|
|||
let urlThumbnail = getURLThumbnail(name: urlFile!.lastPathComponent)
|
||||
|
||||
if urlImage != nil {
|
||||
let attachment = Attachment(id: UUID().uuidString, thumbnail: urlThumbnail, full: urlImage!, type: .video)
|
||||
let attachment = Attachment(id: UUID().uuidString, name: urlFile!.lastPathComponent, thumbnail: urlThumbnail, full: urlImage!, type: .video)
|
||||
medias.append(attachment)
|
||||
}
|
||||
} catch {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue