blob: 77a4172375bc008cf286ddabf8140dcfa3767a67 (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
import Foundation
enum ProjectPinStore {
static func loadPinnedProjectIDs(
for userKey: String,
defaults: UserDefaults = .standard
) -> [String] {
let pinnedProjects = loadAll(defaults: defaults)
return normalizedProjectIDs(pinnedProjects[userKey] ?? [])
}
static func isPinned(
projectID: String,
for userKey: String,
defaults: UserDefaults = .standard
) -> Bool {
loadPinnedProjectIDs(for: userKey, defaults: defaults).contains(projectID)
}
static func togglePin(
projectID: String,
for userKey: String,
defaults: UserDefaults = .standard
) {
var pinnedProjects = loadAll(defaults: defaults)
var projectIDs = normalizedProjectIDs(pinnedProjects[userKey] ?? [])
if let index = projectIDs.firstIndex(of: projectID) {
projectIDs.remove(at: index)
} else {
if let normalizedProjectID = normalizedProjectID(projectID) {
projectIDs.append(normalizedProjectID)
}
}
pinnedProjects[userKey] = projectIDs
save(pinnedProjects, defaults: defaults)
}
private static func loadAll(defaults: UserDefaults) -> [String: [String]] {
guard let data = defaults.data(forKey: AppStorageKeys.pinnedHomeProjects) else {
return [:]
}
let decoded = (try? JSONDecoder().decode([String: [String]].self, from: data)) ?? [:]
return decoded.reduce(into: [String: [String]]()) { result, entry in
let normalizedUserKey = entry.key.trimmingCharacters(in: .whitespacesAndNewlines)
guard !normalizedUserKey.isEmpty else { return }
let normalizedProjectIDs = normalizedProjectIDs(entry.value)
if !normalizedProjectIDs.isEmpty {
result[normalizedUserKey] = normalizedProjectIDs
}
}
}
private static func save(_ pinnedProjects: [String: [String]], defaults: UserDefaults) {
guard let data = try? JSONEncoder().encode(pinnedProjects) else { return }
defaults.set(data, forKey: AppStorageKeys.pinnedHomeProjects)
}
private static func normalizedProjectIDs(_ projectIDs: [String]) -> [String] {
var seen = Set<String>()
return projectIDs.compactMap { projectID in
guard let normalizedProjectID = normalizedProjectID(projectID) else { return nil }
guard seen.insert(normalizedProjectID).inserted else { return nil }
return normalizedProjectID
}
}
private static func normalizedProjectID(_ projectID: String) -> String? {
let trimmed = projectID.trimmingCharacters(in: .whitespacesAndNewlines)
return trimmed.isEmpty ? nil : trimmed
}
}
|