Add rich link previews for shared URLs

- Capture both URL and title when sharing links
- Create LinkPreviewView component with clickable cards
- Support markdown-style links [title](url)
- Auto-detect plain URLs in messages
- Add link metadata fetching on iOS
- Display clean preview cards with title, URL, and description
This commit is contained in:
jack
2025-07-08 04:32:57 +02:00
parent 244af2197c
commit b8d930614e
4 changed files with 225 additions and 16 deletions
+26 -11
View File
@@ -415,18 +415,33 @@ struct ContentView: View {
.frame(maxWidth: .infinity, alignment: .leading)
} else {
// Regular messages with natural text wrapping
HStack(alignment: .top, spacing: 0) {
// Single text view for natural wrapping
Text(viewModel.formatMessageAsText(message, colorScheme: colorScheme))
.textSelection(.enabled)
.fixedSize(horizontal: false, vertical: true)
.frame(maxWidth: .infinity, alignment: .leading)
VStack(alignment: .leading, spacing: 8) {
HStack(alignment: .top, spacing: 0) {
// Single text view for natural wrapping
Text(viewModel.formatMessageAsText(message, colorScheme: colorScheme))
.textSelection(.enabled)
.fixedSize(horizontal: false, vertical: true)
.frame(maxWidth: .infinity, alignment: .leading)
// Delivery status indicator for private messages
if message.isPrivate && message.sender == viewModel.nickname,
let status = message.deliveryStatus {
DeliveryStatusView(status: status, colorScheme: colorScheme)
.padding(.leading, 4)
}
}
// Delivery status indicator for private messages
if message.isPrivate && message.sender == viewModel.nickname,
let status = message.deliveryStatus {
DeliveryStatusView(status: status, colorScheme: colorScheme)
.padding(.leading, 4)
// Check for links and show preview
if let markdownLink = message.content.extractMarkdownLink() {
LinkPreviewView(url: markdownLink.url, title: markdownLink.title)
.padding(.top, 4)
} else {
// Check for plain URLs
let urls = message.content.extractURLs()
ForEach(urls.prefix(3), id: \.url) { urlInfo in
LinkPreviewView(url: urlInfo.url, title: nil)
.padding(.top, 4)
}
}
}
}