diff --git a/Linphone/Assets.xcassets/download-simple.imageset/Contents.json b/Linphone/Assets.xcassets/download-simple.imageset/Contents.json new file mode 100644 index 000000000..cc9e90c47 --- /dev/null +++ b/Linphone/Assets.xcassets/download-simple.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "download-simple.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/download-simple.imageset/download-simple.svg b/Linphone/Assets.xcassets/download-simple.imageset/download-simple.svg new file mode 100644 index 000000000..60d202b45 --- /dev/null +++ b/Linphone/Assets.xcassets/download-simple.imageset/download-simple.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Linphone/Assets.xcassets/file-audio.imageset/Contents.json b/Linphone/Assets.xcassets/file-audio.imageset/Contents.json new file mode 100644 index 000000000..8c55b02f2 --- /dev/null +++ b/Linphone/Assets.xcassets/file-audio.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "file-audio.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/file-audio.imageset/file-audio.svg b/Linphone/Assets.xcassets/file-audio.imageset/file-audio.svg new file mode 100644 index 000000000..a2bb0c309 --- /dev/null +++ b/Linphone/Assets.xcassets/file-audio.imageset/file-audio.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Linphone/Assets.xcassets/file-pdf.imageset/Contents.json b/Linphone/Assets.xcassets/file-pdf.imageset/Contents.json new file mode 100644 index 000000000..f946df429 --- /dev/null +++ b/Linphone/Assets.xcassets/file-pdf.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "file-pdf.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/file-pdf.imageset/file-pdf.svg b/Linphone/Assets.xcassets/file-pdf.imageset/file-pdf.svg new file mode 100644 index 000000000..63fc1ae2e --- /dev/null +++ b/Linphone/Assets.xcassets/file-pdf.imageset/file-pdf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Linphone/Assets.xcassets/file-text.imageset/file-text.svg b/Linphone/Assets.xcassets/file-text.imageset/file-text.svg index 0b256101e..05971352c 100644 --- a/Linphone/Assets.xcassets/file-text.imageset/file-text.svg +++ b/Linphone/Assets.xcassets/file-text.imageset/file-text.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/Linphone/Assets.xcassets/file.imageset/Contents.json b/Linphone/Assets.xcassets/file.imageset/Contents.json new file mode 100644 index 000000000..d33e50463 --- /dev/null +++ b/Linphone/Assets.xcassets/file.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "file.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/file.imageset/file.svg b/Linphone/Assets.xcassets/file.imageset/file.svg new file mode 100644 index 000000000..85a754433 --- /dev/null +++ b/Linphone/Assets.xcassets/file.imageset/file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Linphone/UI/Main/Conversations/Fragments/ChatBubbleView.swift b/Linphone/UI/Main/Conversations/Fragments/ChatBubbleView.swift index 2da7dab3e..3cfb8e069 100644 --- a/Linphone/UI/Main/Conversations/Fragments/ChatBubbleView.swift +++ b/Linphone/UI/Main/Conversations/Fragments/ChatBubbleView.swift @@ -33,6 +33,7 @@ struct ChatBubbleView: View { @State private var ticker = Ticker() @State private var isPressed: Bool = false @State private var timePassed: TimeInterval? + @State private var sliderValue: Double = 0.5 var body: some View { HStack { @@ -394,6 +395,47 @@ struct ChatBubbleView: View { } .clipShape(RoundedRectangle(cornerRadius: 4)) .clipped() + } else if eventLogMessage.message.attachments.first!.type == .voiceRecording { + CustomSlider( + value: $sliderValue, + range: 0...1, + thumbColor: .blue, + trackColor: .gray, + trackHeight: 8, + cornerRadius: 10 + ) + .padding() + } else { + HStack { + VStack { + Image(getImageOfType(type: eventLogMessage.message.attachments.first!.type)) + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c700) + .frame(width: 60, height: 60, alignment: .leading) + } + .frame(width: 100, height: 100) + .background(Color.grayMain2c200) + + VStack { + Text(eventLogMessage.message.attachments.first!.name) + .foregroundStyle(Color.grayMain2c700) + .default_text_style_600(styleSize: 16) + .truncationMode(.middle) + .frame(maxWidth: .infinity, alignment: .leading) + .lineLimit(1) + + Text("2,2 Mo") + .default_text_style_300(styleSize: 16) + .frame(maxWidth: .infinity, alignment: .leading) + .lineLimit(1) + } + .padding(.horizontal, 10) + .frame(maxWidth: .infinity, alignment: .leading) + } + .frame(width: geometryProxy.size.width - 110, height: 100) + .background(.white) + .clipShape(RoundedRectangle(cornerRadius: 10)) } } else if eventLogMessage.message.attachments.count > 1 { let isGroup = conversationViewModel.displayedConversation != nil && conversationViewModel.displayedConversation!.isGroup @@ -472,6 +514,20 @@ struct ChatBubbleView: View { } return (100, 100) } + + func getImageOfType(type: AttachmentType) -> String { + if type == .audio { + return "file-audio" + } else if type == .pdf { + return "file-pdf" + } else if type == .text { + return "file-text" + } else if type == .fileTransfer { + return "download-simple" + } else { + return "file" + } + } } enum URLType { diff --git a/Linphone/UI/Main/Conversations/Model/Attachment.swift b/Linphone/UI/Main/Conversations/Model/Attachment.swift index 0e84668f1..6b57fd927 100644 --- a/Linphone/UI/Main/Conversations/Model/Attachment.swift +++ b/Linphone/UI/Main/Conversations/Model/Attachment.swift @@ -21,15 +21,35 @@ import Foundation public enum AttachmentType: String, Codable { case image - case video case gif + case video + case audio + case voiceRecording + case pdf + case text + case fileTransfer + case other public var title: String { switch self { case .image: return "Image" - default: + case .gif: + return "GIF" + case .video: return "Video" + case .audio: + return "Audio" + case .voiceRecording: + return "Voice Recording" + case .pdf: + return "PDF" + case .text: + return "Text" + case .fileTransfer: + return "File Transfer" + default: + return "Other" } } @@ -37,8 +57,22 @@ public enum AttachmentType: String, Codable { switch mediaType { case .image: self = .image - default: + case .gif: + self = .gif + case .video: self = .video + case .audio: + self = .audio + case .voiceRecording: + self = .voiceRecording + case .pdf: + self = .pdf + case .text: + self = .text + case .fileTransfer: + self = .fileTransfer + default: + self = .other } } } diff --git a/Linphone/UI/Main/Conversations/Model/Message.swift b/Linphone/UI/Main/Conversations/Model/Message.swift index 4e53d314b..94644eaae 100644 --- a/Linphone/UI/Main/Conversations/Model/Message.swift +++ b/Linphone/UI/Main/Conversations/Model/Message.swift @@ -125,7 +125,7 @@ public struct Message: Identifiable, Hashable { guard let thumbnailURL = await media.getThumbnailURL() else { return nil } - + switch media.type { case .image: return Attachment(id: UUID().uuidString, name: "", url: thumbnailURL, type: .image) @@ -134,6 +134,10 @@ public struct Message: Identifiable, Hashable { return nil } return Attachment(id: UUID().uuidString, name: "", thumbnail: thumbnailURL, full: fullURL, type: .video) + case .audio: + return Attachment(id: UUID().uuidString, name: "", url: thumbnailURL, type: .audio) + default: + return Attachment(id: UUID().uuidString, name: "", url: thumbnailURL, type: .other) } } @@ -272,7 +276,14 @@ public struct DraftMessage { public enum MediaType { case image + case gif case video + case audio + case voiceRecording + case pdf + case text + case fileTransfer + case other } public struct Media: Identifiable, Equatable { diff --git a/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift b/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift index 6d1adbedb..8c17d611b 100644 --- a/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift +++ b/Linphone/UI/Main/Conversations/ViewModel/ConversationViewModel.swift @@ -288,7 +288,7 @@ class ConversationViewModel: ObservableObject { id: UUID().uuidString, name: content.name!, url: path!, - type: .image + type: .fileTransfer ) attachmentNameList += ", \(content.name!)" attachmentList.append(attachment) @@ -296,6 +296,20 @@ class ConversationViewModel: ObservableObject { } else { if content.type != "video" { let path = URL(string: self.getNewFilePath(name: content.name ?? "")) + var typeTmp: AttachmentType = .other + + switch content.type { + case "image": + typeTmp = (content.name?.lowercased().hasSuffix("gif"))! ? .gif : .image + case "audio": + typeTmp = content.isVoiceRecording ? .voiceRecording : .audio + case "application": + typeTmp = content.subtype.lowercased() == "pdf" ? .pdf : .other + case "text": + typeTmp = .text + default: + typeTmp = .other + } if path != nil { let attachment = @@ -303,7 +317,7 @@ class ConversationViewModel: ObservableObject { id: UUID().uuidString, name: content.name!, url: path!, - type: (content.name?.lowercased().hasSuffix("gif"))! ? .gif : .image + type: typeTmp ) attachmentNameList += ", \(content.name!)" attachmentList.append(attachment) @@ -489,7 +503,7 @@ class ConversationViewModel: ObservableObject { id: UUID().uuidString, name: content.name!, url: path!, - type: .image + type: .fileTransfer ) attachmentNameList += ", \(content.name!)" attachmentList.append(attachment) @@ -497,6 +511,20 @@ class ConversationViewModel: ObservableObject { } else { if content.type != "video" { let path = URL(string: self.getNewFilePath(name: content.name ?? "")) + var typeTmp: AttachmentType = .other + + switch content.type { + case "image": + typeTmp = (content.name?.lowercased().hasSuffix("gif"))! ? .gif : .image + case "audio": + typeTmp = content.isVoiceRecording ? .voiceRecording : .audio + case "application": + typeTmp = content.subtype.lowercased() == "pdf" ? .pdf : .other + case "text": + typeTmp = .text + default: + typeTmp = .other + } if path != nil { let attachment = @@ -504,7 +532,7 @@ class ConversationViewModel: ObservableObject { id: UUID().uuidString, name: content.name!, url: path!, - type: (content.name?.lowercased().hasSuffix("gif"))! ? .gif : .image + type: typeTmp ) attachmentNameList += ", \(content.name!)" attachmentList.append(attachment) @@ -687,7 +715,7 @@ class ConversationViewModel: ObservableObject { id: UUID().uuidString, name: content.name ?? "???", url: path!, - type: .image + type: .fileTransfer ) attachmentNameList += ", \(content.name ?? "???")" attachmentList.append(attachment) @@ -695,6 +723,20 @@ class ConversationViewModel: ObservableObject { } else if content.name != nil && !content.name!.isEmpty { if content.type != "video" { let path = URL(string: self.getNewFilePath(name: content.name ?? "")) + var typeTmp: AttachmentType = .other + + switch content.type { + case "image": + typeTmp = (content.name?.lowercased().hasSuffix("gif"))! ? .gif : .image + case "audio": + typeTmp = content.isVoiceRecording ? .voiceRecording : .audio + case "application": + typeTmp = content.subtype.lowercased() == "pdf" ? .pdf : .other + case "text": + typeTmp = .text + default: + typeTmp = .other + } if path != nil { let attachment = @@ -702,7 +744,7 @@ class ConversationViewModel: ObservableObject { id: UUID().uuidString, name: content.name!, url: path!, - type: (content.name?.lowercased().hasSuffix("gif"))! ? .gif : .image + type: typeTmp ) attachmentNameList += ", \(content.name!)" attachmentList.append(attachment) @@ -958,7 +1000,7 @@ class ConversationViewModel: ObservableObject { id: UUID().uuidString, name: content.name!, url: path!, - type: .image + type: .fileTransfer ) attachmentNameList += ", \(content.name!)" attachmentList.append(attachment) @@ -966,6 +1008,20 @@ class ConversationViewModel: ObservableObject { } else { if content.type != "video" { let path = URL(string: self.getNewFilePath(name: content.name ?? "")) + var typeTmp: AttachmentType = .other + + switch content.type { + case "image": + typeTmp = (content.name?.lowercased().hasSuffix("gif"))! ? .gif : .image + case "audio": + typeTmp = content.isVoiceRecording ? .voiceRecording : .audio + case "application": + typeTmp = content.subtype.lowercased() == "pdf" ? .pdf : .other + case "text": + typeTmp = .text + default: + typeTmp = .other + } if path != nil { let attachment = @@ -973,7 +1029,7 @@ class ConversationViewModel: ObservableObject { id: UUID().uuidString, name: content.name!, url: path!, - type: (content.name?.lowercased().hasSuffix("gif"))! ? .gif : .image + type: typeTmp ) attachmentNameList += ", \(content.name!)" attachmentList.append(attachment) @@ -1566,6 +1622,48 @@ class ConversationViewModel: ObservableObject { } } } + +struct CustomSlider: View { + @Binding var value: Double + var range: ClosedRange + var thumbColor: Color + var trackColor: Color + var trackHeight: CGFloat + var cornerRadius: CGFloat + + var body: some View { + VStack { + ZStack { + // Slider track with rounded corners + Rectangle() + .fill(trackColor) + .frame(height: trackHeight) + .cornerRadius(cornerRadius) + + // Progress track to show the current value + Rectangle() + .fill(thumbColor.opacity(0.5)) + .frame(width: CGFloat((value - range.lowerBound) / (range.upperBound - range.lowerBound)) * UIScreen.main.bounds.width, height: trackHeight) + .cornerRadius(cornerRadius) + + // Thumb (handle) with rounded appearance + Circle() + .fill(thumbColor) + .frame(width: 30, height: 30) + .offset(x: CGFloat((value - range.lowerBound) / (range.upperBound - range.lowerBound)) * UIScreen.main.bounds.width - 20) + .gesture(DragGesture(minimumDistance: 0) + .onChanged { gesture in + let sliderWidth = UIScreen.main.bounds.width + let dragX = gesture.location.x + let newValue = range.lowerBound + Double(dragX / sliderWidth) * (range.upperBound - range.lowerBound) + value = min(max(newValue, range.lowerBound), range.upperBound) + } + ) + } + } + .padding(.horizontal, 20) + } +} // swiftlint:enable line_length // swiftlint:enable type_body_length // swiftlint:enable cyclomatic_complexity