mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-01-17 11:08:06 +00:00
Add image bubble message
This commit is contained in:
parent
73d6f805d3
commit
af3a0fbd31
6 changed files with 227 additions and 45 deletions
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue