AtTable iOS app with multipeer connectivity for mesh messaging. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
85 lines
3.2 KiB
Swift
85 lines
3.2 KiB
Swift
import SwiftUI
|
|
|
|
struct MessageBubble: View {
|
|
let message: MeshMessage
|
|
let isMyMessage: Bool
|
|
|
|
var body: some View {
|
|
HStack(alignment: .bottom, spacing: 10) {
|
|
if isMyMessage { Spacer() }
|
|
|
|
if !isMyMessage {
|
|
// Avatar Placeholder
|
|
Circle()
|
|
.fill(Color(hex: message.senderColorHex))
|
|
.frame(width: 30, height: 30)
|
|
.shadow(color: Color(hex: message.senderColorHex).opacity(0.5), radius: 5)
|
|
}
|
|
|
|
VStack(alignment: isMyMessage ? .trailing : .leading, spacing: 4) {
|
|
if !isMyMessage {
|
|
Text(message.senderID)
|
|
.font(.caption2)
|
|
.fontWeight(.bold)
|
|
.foregroundColor(.white.opacity(0.7))
|
|
}
|
|
|
|
HStack(alignment: .top) {
|
|
if message.isTranscription {
|
|
Image(systemName: "waveform")
|
|
.font(.caption)
|
|
.foregroundColor(.white.opacity(0.6))
|
|
}
|
|
|
|
Text(message.content)
|
|
.font(.system(.body, design: .rounded))
|
|
.foregroundColor(.white)
|
|
.fixedSize(horizontal: false, vertical: true)
|
|
}
|
|
.padding(12)
|
|
.background(
|
|
ZStack {
|
|
if isMyMessage {
|
|
LinearGradient(
|
|
colors: [Color.blue.opacity(0.8), Color.purple.opacity(0.8)],
|
|
startPoint: .topLeading,
|
|
endPoint: .bottomTrailing
|
|
)
|
|
} else {
|
|
Color(hex: message.senderColorHex).opacity(0.3)
|
|
}
|
|
}
|
|
)
|
|
.background(.ultraThinMaterial)
|
|
.cornerRadius(18, corners: isMyMessage ? [.topLeft, .topRight, .bottomLeft] : [.topLeft, .topRight, .bottomRight])
|
|
.shadow(color: isMyMessage ? .blue.opacity(0.3) : Color(hex: message.senderColorHex).opacity(0.2), radius: 5, x: 0, y: 2)
|
|
.overlay(
|
|
RoundedCorner(radius: 18, corners: isMyMessage ? [.topLeft, .topRight, .bottomLeft] : [.topLeft, .topRight, .bottomRight])
|
|
.stroke(.white.opacity(0.2), lineWidth: 1)
|
|
)
|
|
}
|
|
|
|
if !isMyMessage { Spacer() }
|
|
}
|
|
.padding(.horizontal)
|
|
.padding(.vertical, 4)
|
|
}
|
|
}
|
|
|
|
// Rounded Corner Shape for Bubble
|
|
struct RoundedCorner: Shape {
|
|
var radius: CGFloat = .infinity
|
|
var corners: UIRectCorner = .allCorners
|
|
|
|
func path(in rect: CGRect) -> Path {
|
|
let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
|
|
return Path(path.cgPath)
|
|
}
|
|
}
|
|
|
|
extension View {
|
|
func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View {
|
|
clipShape(RoundedCorner(radius: radius, corners: corners))
|
|
}
|
|
}
|