Message bubbles for different file types

This commit is contained in:
Benoit Martins 2024-09-10 17:48:17 +02:00
parent 29d3770280
commit d6293be80f
13 changed files with 300 additions and 13 deletions

View file

@ -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
}
}

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M224,144v64a8,8,0,0,1-8,8H40a8,8,0,0,1-8-8V144a8,8,0,0,1,16,0v56H208V144a8,8,0,0,1,16,0Zm-101.66,5.66a8,8,0,0,0,11.32,0l40-40a8,8,0,0,0-11.32-11.32L136,124.69V32a8,8,0,0,0-16,0v92.69L93.66,98.34a8,8,0,0,0-11.32,11.32Z"></path></svg>

After

Width:  |  Height:  |  Size: 341 B

View file

@ -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
}
}

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M99.06,128.61a8,8,0,0,0-8.72,1.73L68.69,152H48a8,8,0,0,0-8,8v40a8,8,0,0,0,8,8H68.69l21.65,21.66A8,8,0,0,0,104,224V136A8,8,0,0,0,99.06,128.61ZM88,204.69,77.66,194.34A8,8,0,0,0,72,192H56V168H72a8,8,0,0,0,5.66-2.34L88,155.31ZM152,180a40.55,40.55,0,0,1-20,34.91A8,8,0,0,1,124,201.09a24.49,24.49,0,0,0,0-42.18A8,8,0,0,1,132,145.09,40.55,40.55,0,0,1,152,180Zm61.66-97.66-56-56A8,8,0,0,0,152,24H56A16,16,0,0,0,40,40v80a8,8,0,0,0,16,0V40h88V88a8,8,0,0,0,8,8h48V216H168a8,8,0,0,0,0,16h32a16,16,0,0,0,16-16V88A8,8,0,0,0,213.66,82.34ZM160,51.31,188.69,80H160Z"></path></svg>

After

Width:  |  Height:  |  Size: 672 B

View file

@ -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
}
}

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M224,152a8,8,0,0,1-8,8H192v16h16a8,8,0,0,1,0,16H192v16a8,8,0,0,1-16,0V152a8,8,0,0,1,8-8h32A8,8,0,0,1,224,152ZM92,172a28,28,0,0,1-28,28H56v8a8,8,0,0,1-16,0V152a8,8,0,0,1,8-8H64A28,28,0,0,1,92,172Zm-16,0a12,12,0,0,0-12-12H56v24h8A12,12,0,0,0,76,172Zm88,8a36,36,0,0,1-36,36H112a8,8,0,0,1-8-8V152a8,8,0,0,1,8-8h16A36,36,0,0,1,164,180Zm-16,0a20,20,0,0,0-20-20h-8v40h8A20,20,0,0,0,148,180ZM40,112V40A16,16,0,0,1,56,24h96a8,8,0,0,1,5.66,2.34l56,56A8,8,0,0,1,216,88v24a8,8,0,0,1-16,0V96H152a8,8,0,0,1-8-8V40H56v72a8,8,0,0,1-16,0ZM160,80h28.69L160,51.31Z"></path></svg>

After

Width:  |  Height:  |  Size: 669 B

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="#4e6074" viewBox="0 0 256 256"><path d="M213.66,82.34l-56-56A8,8,0,0,0,152,24H56A16,16,0,0,0,40,40V216a16,16,0,0,0,16,16H200a16,16,0,0,0,16-16V88A8,8,0,0,0,213.66,82.34ZM160,51.31,188.69,80H160ZM200,216H56V40h88V88a8,8,0,0,0,8,8h48V216Zm-32-80a8,8,0,0,1-8,8H96a8,8,0,0,1,0-16h64A8,8,0,0,1,168,136Zm0,32a8,8,0,0,1-8,8H96a8,8,0,0,1,0-16h64A8,8,0,0,1,168,168Z"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M213.66,82.34l-56-56A8,8,0,0,0,152,24H56A16,16,0,0,0,40,40V216a16,16,0,0,0,16,16H200a16,16,0,0,0,16-16V88A8,8,0,0,0,213.66,82.34ZM160,51.31,188.69,80H160ZM200,216H56V40h88V88a8,8,0,0,0,8,8h48V216Zm-32-80a8,8,0,0,1-8,8H96a8,8,0,0,1,0-16h64A8,8,0,0,1,168,136Zm0,32a8,8,0,0,1-8,8H96a8,8,0,0,1,0-16h64A8,8,0,0,1,168,168Z"></path></svg>

Before

Width:  |  Height:  |  Size: 440 B

After

Width:  |  Height:  |  Size: 440 B

View file

@ -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
}
}

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="#000000" viewBox="0 0 256 256"><path d="M213.66,82.34l-56-56A8,8,0,0,0,152,24H56A16,16,0,0,0,40,40V216a16,16,0,0,0,16,16H200a16,16,0,0,0,16-16V88A8,8,0,0,0,213.66,82.34ZM160,51.31,188.69,80H160ZM200,216H56V40h88V88a8,8,0,0,0,8,8h48V216Z"></path></svg>

After

Width:  |  Height:  |  Size: 320 B

View file

@ -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 {

View file

@ -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
}
}
}

View file

@ -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 {

View file

@ -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<Double>
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