diff --git a/Examples/Reminders/ReminderForm.swift b/Examples/Reminders/ReminderForm.swift index 7a346de..23a1312 100644 --- a/Examples/Reminders/ReminderForm.swift +++ b/Examples/Reminders/ReminderForm.swift @@ -57,7 +57,7 @@ struct ReminderFormView: View { } .popover(isPresented: $isPresentingTagsPopover) { NavigationStack { - TagsPopover(selectedTags: $selectedTags) + TagsView(selectedTags: $selectedTags) } } @@ -185,45 +185,6 @@ extension Optional { } } -struct TagsPopover: View { - @SharedReader(.fetchAll(sql: #"SELECT * FROM "tags" ORDER BY "name" ASC"#)) - var availableTags: [Tag] - - @Binding var selectedTags: [Tag] - - @Environment(\.dismiss) var dismiss - - var body: some View { - List { - let selectedTagIDs = Set(selectedTags.map(\.id)) - ForEach(availableTags, id: \.id) { tag in - let tagIsSelected = selectedTagIDs.contains(tag.id) - Button { - if tagIsSelected { - selectedTags.removeAll(where: { $0.id == tag.id }) - } else { - selectedTags.append(tag) - } - } label: { - HStack { - if tagIsSelected { - Image.init(systemName: "checkmark") - } - Text(tag.name) - } - } - .tint(tagIsSelected ? .blue : .black) - } - } - .toolbar { - ToolbarItem { - Button("Done") { dismiss() } - } - } - .navigationTitle(Text("Tags")) - } -} - #Preview { let (remindersList, reminder) = try! prepareDependencies { $0.defaultDatabase = try Reminders.appDatabase() diff --git a/Examples/Reminders/Schema.swift b/Examples/Reminders/Schema.swift index 75cefb1..90d5d98 100644 --- a/Examples/Reminders/Schema.swift +++ b/Examples/Reminders/Schema.swift @@ -216,10 +216,15 @@ func appDatabase() throws -> any DatabaseWriter { _ = try Tag(name: "kids").inserted(self) _ = try Tag(name: "someday").inserted(self) _ = try Tag(name: "optional").inserted(self) + _ = try Tag(name: "social").inserted(self) + _ = try Tag(name: "night").inserted(self) + _ = try Tag(name: "adulting").inserted(self) _ = try ReminderTag(reminderID: 1, tagID: 3).inserted(self) _ = try ReminderTag(reminderID: 1, tagID: 4).inserted(self) + _ = try ReminderTag(reminderID: 1, tagID: 7).inserted(self) _ = try ReminderTag(reminderID: 2, tagID: 3).inserted(self) _ = try ReminderTag(reminderID: 2, tagID: 4).inserted(self) + _ = try ReminderTag(reminderID: 3, tagID: 7).inserted(self) _ = try ReminderTag(reminderID: 4, tagID: 1).inserted(self) _ = try ReminderTag(reminderID: 4, tagID: 2).inserted(self) } diff --git a/Examples/Reminders/TagsForm.swift b/Examples/Reminders/TagsForm.swift new file mode 100644 index 0000000..a387a2d --- /dev/null +++ b/Examples/Reminders/TagsForm.swift @@ -0,0 +1,111 @@ +import SharingGRDB +import SwiftUI + +struct TagsView: View { + @SharedReader(.fetch(Tags())) var tags = Tags.Value() + @Binding var selectedTags: [Tag] + + @Environment(\.dismiss) var dismiss + + var body: some View { + Form { + let selectedTagIDs = Set(selectedTags.map(\.id)) + if !tags.top.isEmpty { + Section { + ForEach(tags.top, id: \.id) { tag in + TagView( + isSelected: selectedTagIDs.contains(tag.id), + selectedTags: $selectedTags, + tag: tag + ) + } + } header: { + Text("Top tags") + } + } + if !tags.rest.isEmpty { + Section { + ForEach(tags.rest, id: \.id) { tag in + TagView( + isSelected: selectedTagIDs.contains(tag.id), + selectedTags: $selectedTags, + tag: tag + ) + } + } + } + } + .toolbar { + ToolbarItem { + Button("Done") { dismiss() } + } + } + .navigationTitle(Text("Tags")) + } + + struct Tags: FetchKeyRequest { + func fetch(_ db: Database) throws -> Value { + let top = try Tag.fetchAll( + db, + sql: """ + SELECT "tags".*, count("reminders"."id") + FROM "tags" + LEFT JOIN "remindersTags" + ON "tags"."id" = "remindersTags"."tagID" + LEFT JOIN "reminders" + ON "remindersTags"."reminderID" = "reminders"."id" + GROUP BY "tags"."id" + HAVING count("reminders"."id") > 0 + ORDER BY count("reminders"."id") DESC, "name" + LIMIT 3 + """) + let rest = try Tag.fetchAll( + db, + SQLRequest(literal: """ + SELECT "tags".* + FROM "tags" + WHERE "id" NOT IN \(top.compactMap(\.id)) + ORDER BY "name" + """) + ) + return Value(rest: rest, top: top) + } + struct Value { + var rest: [Tag] = [] + var top: [Tag] = [] + } + } +} + +private struct TagView: View { + let isSelected: Bool + @Binding var selectedTags: [Tag] + let tag: Tag + + var body: some View { + Button { + if isSelected { + selectedTags.removeAll(where: { $0.id == tag.id }) + } else { + selectedTags.append(tag) + } + } label: { + HStack { + if isSelected { + Image.init(systemName: "checkmark") + } + Text(tag.name) + } + } + .tint(isSelected ? .blue : .black) + } +} + +#Preview { + @Previewable @State var tags: [Tag] = [] + let _ = prepareDependencies { + $0.defaultDatabase = try! Reminders.appDatabase(inMemory: true) + } + + TagsView(selectedTags: $tags) +}