Add image bubble message

This commit is contained in:
Benoit Martins 2024-03-08 17:30:25 +01:00
parent 73d6f805d3
commit af3a0fbd31
6 changed files with 227 additions and 45 deletions

View file

@ -92,6 +92,7 @@ final class CoreContext: ObservableObject {
path: "\(configDir)/linphonerc",
factoryPath: Bundle.main.path(forResource: "linphonerc-factory", ofType: nil)
)
if config != nil {
self.mCore = try? Factory.Instance.createCoreWithConfig(config: config!, systemContext: nil)
}

View file

@ -95,10 +95,24 @@ struct ChatBubbleView: View {
}
VStack {
Text(message.text
)
.foregroundStyle(Color.grayMain2c700)
.default_text_style(styleSize: 16)
if !message.attachments.isEmpty {
AsyncImage(url: message.attachments.first!.full) { image in
image.resizable()
.scaledToFill()
//.aspectRatio(1.5, contentMode: .fill)
//.clipped()
} placeholder: {
ProgressView()
}
.frame(maxHeight: 400)
//.frame(width: 50, height: 50)
}
if !message.text.isEmpty {
Text(message.text)
.foregroundStyle(Color.grayMain2c700)
.default_text_style(styleSize: 16)
}
}
.padding(.all, 15)
.background(message.isOutgoing ? Color.orangeMain100 : Color.grayMain2c100)

View file

@ -215,7 +215,6 @@ class ConversationModel: ObservableObject {
}
}
func getUnreadMessagesCount() {
coreContext.doOnCoreQueue { _ in
self.unreadMessagesCount = self.chatRoom.unreadMessagesCount
@ -242,6 +241,13 @@ class ConversationModel: ObservableObject {
}
}
func downloadContent(chatMessage: ChatMessage, content: Content) {
coreContext.doOnCoreQueue { _ in
let result = chatMessage.downloadContent(content: content)
print("resultresult download \(result)")
}
}
func deleteChatRoom() {
CoreContext.shared.doOnCoreQueue { core in
core.deleteChatRoom(chatRoom: self.chatRoom)

View file

@ -66,15 +66,16 @@ public struct Message: Identifiable, Hashable {
public var recording: Recording?
public var replyMessage: ReplyMessage?
public init(id: String,
status: Status? = nil,
createdAt: Date = Date(),
isOutgoing: Bool,
text: String = "",
attachments: [Attachment] = [],
recording: Recording? = nil,
replyMessage: ReplyMessage? = nil) {
public init(
id: String,
status: Status? = nil,
createdAt: Date = Date(),
isOutgoing: Bool,
text: String = "",
attachments: [Attachment] = [],
recording: Recording? = nil,
replyMessage: ReplyMessage? = nil
) {
self.id = id
self.status = status
self.createdAt = createdAt

View file

@ -21,6 +21,7 @@ import Foundation
import linphonesw
import Combine
import SwiftUI
import AVFoundation
class ConversationViewModel: ObservableObject {
@ -113,10 +114,30 @@ class ConversationViewModel: ObservableObject {
DispatchQueue.main.async {
self.conversationMessagesList.append(LinphoneCustomEventLog(eventLog: eventLog))
}
var attachmentList: [Attachment] = []
var contentText = ""
if eventLog.chatMessage != nil && !eventLog.chatMessage!.contents.isEmpty {
eventLog.chatMessage!.contents.forEach { content in
if content.isText {
contentText = content.utf8Text ?? ""
} else {
if content.filePath == nil || content.filePath!.isEmpty {
self.downloadContent(chatMessage: eventLog.chatMessage!, content: content)
} else {
let attachment = Attachment(id: UUID().uuidString, url: URL(string: "file://" + content.filePath!)!, type: .image)
attachmentList.append(attachment)
}
}
}
}
conversationMessage.append(Message(
id: UUID().uuidString,
isOutgoing: eventLog.chatMessage?.isOutgoing ?? false,
text: eventLog.chatMessage?.utf8Text ?? ""))
text: contentText,
attachments: attachmentList))
DispatchQueue.main.async {
if index == historyEvents.count - 1 {
@ -133,28 +154,29 @@ class ConversationViewModel: ObservableObject {
coreContext.doOnCoreQueue { _ in
if self.displayedConversation != nil {
let historyEvents = self.displayedConversation!.chatRoom.getHistoryRangeEvents(begin: self.conversationMessagesList.count, end: self.conversationMessagesList.count + 30)
//For List
/*
historyEvents.reversed().forEach { eventLog in
DispatchQueue.main.async {
self.conversationMessagesList.append(LinphoneCustomEventLog(eventLog: eventLog))
}
}
*/
//For ScrollView
var conversationMessagesListTmp: [LinphoneCustomEventLog] = []
var conversationMessagesTmp: [Message] = []
historyEvents.reversed().forEach { eventLog in
conversationMessagesListTmp.insert(LinphoneCustomEventLog(eventLog: eventLog), at: 0)
var attachmentList: [Attachment] = []
var contentText = ""
if eventLog.chatMessage != nil && !eventLog.chatMessage!.contents.isEmpty {
eventLog.chatMessage!.contents.forEach { content in
if content.isText {
contentText = content.utf8Text ?? ""
}
}
}
conversationMessagesTmp.insert(
Message(
id: UUID().uuidString,
isOutgoing: eventLog.chatMessage?.isOutgoing ?? false,
text: eventLog.chatMessage?.utf8Text ?? ""
text: contentText,
attachments: attachmentList
), at: 0
)
}
@ -162,8 +184,6 @@ class ConversationViewModel: ObservableObject {
if !conversationMessagesTmp.isEmpty {
DispatchQueue.main.async {
self.conversationMessagesList.insert(contentsOf: conversationMessagesListTmp, at: 0)
//self.conversationMessagesSection.append(MessagesSection(date: Date(), rows: conversationMessagesTmp.reversed()))
//self.conversationMessagesIds.append(UUID().uuidString)
self.conversationMessagesSection[0].rows.append(contentsOf: conversationMessagesTmp.reversed())
}
}
@ -175,26 +195,28 @@ class ConversationViewModel: ObservableObject {
var conversationMessage: [Message] = []
eventLogs.enumerated().forEach { index, eventLog in
DispatchQueue.main.async {
//withAnimation {
//For List
//self.conversationMessagesList.insert(LinphoneCustomEventLog(eventLog: eventLog), at: 0)
//For ScrollView
self.conversationMessagesList.append(LinphoneCustomEventLog(eventLog: eventLog))
/*
conversationMessage.append(Message(
id: UUID().uuidString,
isOutgoing: eventLog.chatMessage?.isOutgoing ?? false,
text: eventLog.chatMessage?.utf8Text ?? ""
)
)
*/
}
var attachmentList: [Attachment] = []
var contentText = ""
if eventLog.chatMessage != nil && !eventLog.chatMessage!.contents.isEmpty {
eventLog.chatMessage!.contents.forEach { content in
if content.isText {
print("contentscontents text")
contentText = content.utf8Text ?? ""
} else {
print("contentscontents \(content.isText)")
}
}
}
let message = Message(
id: UUID().uuidString,
isOutgoing: eventLog.chatMessage?.isOutgoing ?? false,
text: eventLog.chatMessage?.utf8Text ?? ""
text: contentText,
attachments: attachmentList
)
DispatchQueue.main.async {
@ -309,6 +331,38 @@ class ConversationViewModel: ObservableObject {
func changeDisplayedChatRoom(conversationModel: ConversationModel) {
self.displayedConversation = conversationModel
}
func downloadContent(chatMessage: ChatMessage, content: Content) {
//Log.debug("[ConversationViewModel] Starting downloading content for file \(model.fileName)")
if content.filePath == nil || content.filePath!.isEmpty {
let contentName = content.name
if contentName != nil {
let isImage = FileUtil.isExtensionImage(path: contentName!)
let file = FileUtil.getFileStoragePath(fileName: contentName!, isImage: isImage)
content.filePath = file
Log.info(
"[ConversationViewModel] File \(contentName) will be downloaded at \(content.filePath)"
)
self.displayedConversation?.downloadContent(chatMessage: chatMessage, content: content)
} else {
Log.error("[ConversationViewModel] Content name is null, can't download it!")
}
}
}
func generateThumbnail(path: URL) -> UIImage? {
do {
let asset = AVURLAsset(url: path, options: nil)
let imgGenerator = AVAssetImageGenerator(asset: asset)
imgGenerator.appliesPreferredTrackTransform = true
let cgImage = try imgGenerator.copyCGImage(at: CMTimeMake(value: 0, timescale: 1), actualTime: nil)
let thumbnail = UIImage(cgImage: cgImage)
return thumbnail
} catch let error {
print("*** Error generating thumbnail: \(error.localizedDescription)")
return nil
}
}
}
struct LinphoneCustomEventLog: Hashable {
var id = UUID()

View file

@ -19,8 +19,19 @@
import UIKit
import linphonesw
import UniformTypeIdentifiers
class FileUtil: NSObject {
public enum MimeType {
case plainText
case pdf
case image
case video
case audio
case unknown
}
public class func bundleFilePath(_ file: NSString) -> String? {
return Bundle.main.path(forResource: file.deletingPathExtension, ofType: file.pathExtension)
}
@ -82,7 +93,7 @@ class FileUtil: NSObject {
return false
}
}
public class func write(string: String, toPath: String) {
do {
try string.write(to: URL(fileURLWithPath: toPath), atomically: true, encoding: String.Encoding.utf8)
@ -122,4 +133,99 @@ class FileUtil: NSObject {
}
}
public class func isExtensionImage(path: String) -> Bool {
let extensionName = getExtensionFromFileName(fileName: path)
let typeExtension = getMimeTypeFromExtension(urlString: extensionName)
return getMimeType(type: typeExtension) == MimeType.image
}
public class func getExtensionFromFileName(fileName: String) -> String {
let url: URL? = URL(string: fileName)
let urlExtension: String? = url?.pathExtension
return urlExtension?.lowercased() ?? ""
}
public class func getMimeTypeFromExtension(urlString: String?) -> String? {
if urlString == nil || urlString!.isEmpty {
return nil
}
return urlString!.mimeType()
}
public class func getMimeType(type: String?) -> MimeType {
if type == nil || type!.isEmpty {
return MimeType.unknown
}
switch type {
case let str where str!.starts(with: "image/"):
return MimeType.image
case let str where str!.starts(with: "text/"):
return MimeType.plainText
case let str where str!.starts(with: "/log"):
return MimeType.plainText
case let str where str!.starts(with: "video/"):
return MimeType.video
case let str where str!.starts(with: "audio/"):
return MimeType.audio
case let str where str!.starts(with: "application/pdf"):
return MimeType.pdf
default:
return MimeType.unknown
}
}
public class func getFileStoragePath(
fileName: String,
isImage: Bool = false,
overrideExisting: Bool = false
) -> String {
return getFileStorageDir(fileName: fileName, isPicture: isImage)
}
public class func getFileStorageDir(fileName: String, isPicture: Bool = false) -> String {
return Factory.Instance.getDownloadDir(context: nil) + fileName
}
}
extension NSURL {
public func mimeType() -> String {
if let pathExt = self.pathExtension,
let mimeType = UTType(filenameExtension: pathExt)?.preferredMIMEType {
return mimeType
}
else {
return "application/octet-stream"
}
}
}
extension URL {
public func mimeType() -> String {
if let mimeType = UTType(filenameExtension: self.pathExtension)?.preferredMIMEType {
return mimeType
}
else {
return "application/octet-stream"
}
}
}
extension NSString {
public func mimeType() -> String {
if let mimeType = UTType(filenameExtension: self.pathExtension)?.preferredMIMEType {
return mimeType
}
else {
return "application/octet-stream"
}
}
}
extension String {
public func mimeType() -> String {
return (self as NSString).mimeType()
}
}