import WidgetKit import SwiftUI struct RecentBookmarksEntry: TimelineEntry { let date: Date let bookmarks: [WidgetBookmark] static let placeholder = RecentBookmarksEntry(date: .now, bookmarks: [ WidgetBookmark(url: "https://example.com/a", title: "The Future of AI in 2025", domain: "example.com", faviconUrl: nil), WidgetBookmark(url: "https://news.com/b", title: "Why Swift Concurrency Matters", domain: "news.com", faviconUrl: nil), WidgetBookmark(url: "https://blog.dev/c", title: "Building Great iOS Apps", domain: "blog.dev", faviconUrl: nil), ]) } struct RecentBookmarksProvider: TimelineProvider { func placeholder(in context: Context) -> RecentBookmarksEntry { .placeholder } func getSnapshot(in context: Context, completion: @escaping (RecentBookmarksEntry) -> Void) { let bookmarks = WidgetDataStore.loadBookmarks() completion(RecentBookmarksEntry(date: .now, bookmarks: Array(bookmarks.prefix(3)))) } func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) { let bookmarks = WidgetDataStore.loadBookmarks() let entry = RecentBookmarksEntry(date: .now, bookmarks: Array(bookmarks.prefix(3))) let next = Calendar.current.date(byAdding: .minute, value: 30, to: .now)! completion(Timeline(entries: [entry], policy: .after(next))) } } struct RecentBookmarksWidgetView: View { let entry: RecentBookmarksEntry var body: some View { VStack(alignment: .leading, spacing: 0) { HStack { Text("Recent") .font(.system(size: 12, weight: .semibold)) .foregroundStyle(.secondary) Spacer() Image(systemName: "bookmark.fill") .font(.system(size: 11)) .foregroundStyle(.blue) } .padding(.bottom, 8) if entry.bookmarks.isEmpty { Spacer() Text("Save bookmarks to see them here.") .font(.system(size: 13)) .foregroundStyle(.secondary) Spacer() } else { ForEach(Array(entry.bookmarks.enumerated()), id: \.element.id) { idx, bookmark in if idx > 0 { Divider().padding(.vertical, 5) } Link(destination: .marksBookmark(bookmark.url)) { WidgetBookmarkRow(bookmark: bookmark) } } Spacer(minLength: 0) } } .padding(14) .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) .containerBackground(.fill.tertiary, for: .widget) } } struct WidgetBookmarkRow: View { let bookmark: WidgetBookmark var body: some View { HStack(spacing: 8) { RoundedRectangle(cornerRadius: 5) .fill(Color(.systemGray5)) .frame(width: 26, height: 26) .overlay { Image(systemName: "bookmark") .font(.system(size: 10, weight: .medium)) .foregroundStyle(Color(.systemGray2)) } VStack(alignment: .leading, spacing: 1) { Text(bookmark.title) .font(.system(size: 13, weight: .medium)) .foregroundStyle(.primary) .lineLimit(1) Text(bookmark.domain) .font(.system(size: 11)) .foregroundStyle(.secondary) } Spacer(minLength: 0) } } } struct RecentBookmarksWidget: Widget { var body: some WidgetConfiguration { StaticConfiguration( kind: "com.magicive.marks.RecentBookmarks", provider: RecentBookmarksProvider() ) { entry in RecentBookmarksWidgetView(entry: entry) } .configurationDisplayName("Recent Bookmarks") .description("Your latest saved bookmarks.") .supportedFamilies([.systemMedium]) } }