diff options
| author | Christian Cleberg <[email protected]> | 2026-07-17 10:45:42 -0500 |
|---|---|---|
| committer | Christian Cleberg <[email protected]> | 2026-07-17 10:57:30 -0500 |
| commit | fbf2d9bf2cf50eb681afe18deac06a96c04004e6 (patch) | |
| tree | f337f6826c283d4246cdde824786bd18b6c770b3 /DomainDig/DomainViewModel.swift | |
| parent | 6e273c2676ce29cef057d117e4427e031886e743 (diff) | |
| download | domain-dig-fbf2d9bf2cf50eb681afe18deac06a96c04004e6.tar.gz domain-dig-fbf2d9bf2cf50eb681afe18deac06a96c04004e6.tar.bz2 domain-dig-fbf2d9bf2cf50eb681afe18deac06a96c04004e6.zip | |
v4.7.0: Add watchlist tags and saved filter views
- TrackedDomain.tags: [String] (backward-compatible custom decode), with
updateTags(_:for:) and a normalized/deduplicated write path.
- Tag editing in TrackedDomainDetailView (comma-separated field, chip display).
- Tag filter chips in WatchlistView (TagFilterChipRowView), integrated into
filteredTrackedDomains alongside the existing filter/search.
- Saved views: name + snapshot the current tag/filter/sort as a
WatchlistSavedView preset (UserDefaults-backed, not part of backup/restore),
with a management sheet to apply or delete presets.
Diffstat (limited to 'DomainDig/DomainViewModel.swift')
| -rw-r--r-- | DomainDig/DomainViewModel.swift | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/DomainDig/DomainViewModel.swift b/DomainDig/DomainViewModel.swift index 3c92ff6..33756df 100644 --- a/DomainDig/DomainViewModel.swift +++ b/DomainDig/DomainViewModel.swift @@ -312,6 +312,8 @@ final class DomainViewModel { var watchlistSearchText = "" var watchlistFilter: WatchlistFilterOption = .all var watchlistSortOption: WatchlistSortOption = .pinned + var watchlistTagFilter: String? + var watchlistSavedViews: [WatchlistSavedView] = DomainViewModel.loadWatchlistSavedViews() var dashboardSearchText = "" var dashboardFilter: PortfolioFilterOption = .all var monitoringSettings: MonitoringSettings = MonitoringStorage.loadSettings() @@ -432,6 +434,10 @@ final class DomainViewModel { return false } + if let watchlistTagFilter, !trackedDomain.tags.contains(watchlistTagFilter) { + return false + } + switch watchlistFilter { case .all: return true @@ -888,6 +894,69 @@ final class DomainViewModel { persistTrackedDomains() } + func updateTags(_ tags: [String], for trackedDomain: TrackedDomain) { + guard canEdit(trackedDomain) else { return } + guard let index = trackedDomains.firstIndex(where: { $0.id == trackedDomain.id }) else { return } + let normalized = Self.normalizedTags(tags) + trackedDomains[index].tags = normalized + trackedDomains[index].updatedAt = Date() + persistTrackedDomains() + } + + /// All tags currently in use across the watchlist, sorted for stable display. + var allWatchlistTags: [String] { + Array(Set(trackedDomains.flatMap(\.tags))).sorted() + } + + func saveCurrentWatchlistView(name: String) { + let trimmedName = name.trimmingCharacters(in: .whitespacesAndNewlines) + guard !trimmedName.isEmpty else { return } + let view = WatchlistSavedView( + name: trimmedName, + tag: watchlistTagFilter, + filter: watchlistFilter, + sort: watchlistSortOption + ) + watchlistSavedViews.append(view) + persistWatchlistSavedViews() + } + + func applyWatchlistSavedView(_ view: WatchlistSavedView) { + watchlistTagFilter = view.tag + watchlistFilter = view.filter + watchlistSortOption = view.sort + } + + func deleteWatchlistSavedViews(at offsets: IndexSet) { + watchlistSavedViews.remove(atOffsets: offsets) + persistWatchlistSavedViews() + } + + private static let watchlistSavedViewsKey = "watchlistSavedViews" + + private static func loadWatchlistSavedViews() -> [WatchlistSavedView] { + guard let data = UserDefaults.standard.data(forKey: watchlistSavedViewsKey), + let views = try? JSONDecoder().decode([WatchlistSavedView].self, from: data) + else { return [] } + return views + } + + private func persistWatchlistSavedViews() { + guard let data = try? JSONEncoder().encode(watchlistSavedViews) else { return } + UserDefaults.standard.set(data, forKey: Self.watchlistSavedViewsKey) + } + + private static func normalizedTags(_ tags: [String]) -> [String] { + var seen = Set<String>() + var normalized: [String] = [] + for tag in tags { + let trimmed = tag.trimmingCharacters(in: .whitespacesAndNewlines) + guard !trimmed.isEmpty, seen.insert(trimmed.lowercased()).inserted else { continue } + normalized.append(trimmed) + } + return normalized.sorted() + } + func removeHistoryEntries(at offsets: IndexSet) { history.remove(atOffsets: offsets) persistHistory() |
