Add ability to see only specific decks
This commit is contained in:
@@ -61,7 +61,7 @@ Why settle for pricey apps when you can have WordAX? We're here to prove that ef
|
|||||||
- [x] Create flashcards
|
- [x] Create flashcards
|
||||||
- [ ] Add tags to cards
|
- [ ] Add tags to cards
|
||||||
- [ ] Specify how often you want each card to be seen (per folder/tag?)
|
- [ ] Specify how often you want each card to be seen (per folder/tag?)
|
||||||
- [ ] Add tags/folders
|
- [x] Add tags/folders - added decks to group cards
|
||||||
- [x] Store cards persistently
|
- [x] Store cards persistently
|
||||||
- [x] For start store them on the phone - stored using Core Data
|
- [x] For start store them on the phone - stored using Core Data
|
||||||
- [ ] Maybe later add a storage in cloud, so that you can sync with other devices if the app is multi-platform?
|
- [ ] Maybe later add a storage in cloud, so that you can sync with other devices if the app is multi-platform?
|
||||||
@@ -70,6 +70,7 @@ Why settle for pricey apps when you can have WordAX? We're here to prove that ef
|
|||||||
- [ ] Make an apple watch version of the app
|
- [ ] Make an apple watch version of the app
|
||||||
- [ ] Option to add a hint
|
- [ ] Option to add a hint
|
||||||
- [ ] Add haptic touch
|
- [ ] Add haptic touch
|
||||||
|
- [ ] Add emoji to decks? design:)
|
||||||
|
|
||||||
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
<p align="right">(<a href="#readme-top">back to top</a>)</p>
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
6C4A87D22BE25D260074E0A9 /* Deck+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C4A87D02BE25D260074E0A9 /* Deck+CoreDataClass.swift */; };
|
6C4A87D22BE25D260074E0A9 /* Deck+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C4A87D02BE25D260074E0A9 /* Deck+CoreDataClass.swift */; };
|
||||||
6C4A87D32BE25D260074E0A9 /* Deck+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C4A87D12BE25D260074E0A9 /* Deck+CoreDataProperties.swift */; };
|
6C4A87D32BE25D260074E0A9 /* Deck+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C4A87D12BE25D260074E0A9 /* Deck+CoreDataProperties.swift */; };
|
||||||
|
6C7B6D5D2BE69FA900FB6ECC /* DeckSelectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C7B6D5C2BE69FA900FB6ECC /* DeckSelectView.swift */; };
|
||||||
|
6C7B6D5F2BE6A1C100FB6ECC /* DeckRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C7B6D5E2BE6A1C100FB6ECC /* DeckRowView.swift */; };
|
||||||
6C8184FE2B88C9580033CF46 /* WordAX.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8184FD2B88C9580033CF46 /* WordAX.swift */; };
|
6C8184FE2B88C9580033CF46 /* WordAX.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8184FD2B88C9580033CF46 /* WordAX.swift */; };
|
||||||
6C8185002B88C9660033CF46 /* Miscellaneous.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8184FF2B88C9660033CF46 /* Miscellaneous.swift */; };
|
6C8185002B88C9660033CF46 /* Miscellaneous.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8184FF2B88C9660033CF46 /* Miscellaneous.swift */; };
|
||||||
6C8185022B88C9FB0033CF46 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8185012B88C9FB0033CF46 /* SettingsView.swift */; };
|
6C8185022B88C9FB0033CF46 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8185012B88C9FB0033CF46 /* SettingsView.swift */; };
|
||||||
@@ -34,6 +36,8 @@
|
|||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
6C4A87D02BE25D260074E0A9 /* Deck+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Deck+CoreDataClass.swift"; sourceTree = "<group>"; };
|
6C4A87D02BE25D260074E0A9 /* Deck+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Deck+CoreDataClass.swift"; sourceTree = "<group>"; };
|
||||||
6C4A87D12BE25D260074E0A9 /* Deck+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Deck+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
6C4A87D12BE25D260074E0A9 /* Deck+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Deck+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||||
|
6C7B6D5C2BE69FA900FB6ECC /* DeckSelectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = DeckSelectView.swift; path = WordAX/Views/Deck/DeckSelectView.swift; sourceTree = SOURCE_ROOT; };
|
||||||
|
6C7B6D5E2BE6A1C100FB6ECC /* DeckRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeckRowView.swift; sourceTree = "<group>"; };
|
||||||
6C8184FD2B88C9580033CF46 /* WordAX.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordAX.swift; sourceTree = "<group>"; };
|
6C8184FD2B88C9580033CF46 /* WordAX.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordAX.swift; sourceTree = "<group>"; };
|
||||||
6C8184FF2B88C9660033CF46 /* Miscellaneous.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Miscellaneous.swift; sourceTree = "<group>"; };
|
6C8184FF2B88C9660033CF46 /* Miscellaneous.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Miscellaneous.swift; sourceTree = "<group>"; };
|
||||||
6C8185012B88C9FB0033CF46 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
6C8185012B88C9FB0033CF46 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
||||||
@@ -109,7 +113,9 @@
|
|||||||
6CD2D7C92BE26FFB0079C4FA /* Deck */ = {
|
6CD2D7C92BE26FFB0079C4FA /* Deck */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
6C7B6D5E2BE6A1C100FB6ECC /* DeckRowView.swift */,
|
||||||
6CB09C2C2BE28C3E0020FE0C /* AddDeckView.swift */,
|
6CB09C2C2BE28C3E0020FE0C /* AddDeckView.swift */,
|
||||||
|
6C7B6D5C2BE69FA900FB6ECC /* DeckSelectView.swift */,
|
||||||
6CD2D7CA2BE270240079C4FA /* DeckListView.swift */,
|
6CD2D7CA2BE270240079C4FA /* DeckListView.swift */,
|
||||||
);
|
);
|
||||||
path = Deck;
|
path = Deck;
|
||||||
@@ -257,6 +263,7 @@
|
|||||||
6CF439542B83541D004C3543 /* MainView.swift in Sources */,
|
6CF439542B83541D004C3543 /* MainView.swift in Sources */,
|
||||||
6C8185082B8B523E0033CF46 /* NextRepetitionButtonView.swift in Sources */,
|
6C8185082B8B523E0033CF46 /* NextRepetitionButtonView.swift in Sources */,
|
||||||
6C8185062B8A537F0033CF46 /* FlashCardView.swift in Sources */,
|
6C8185062B8A537F0033CF46 /* FlashCardView.swift in Sources */,
|
||||||
|
6C7B6D5F2BE6A1C100FB6ECC /* DeckRowView.swift in Sources */,
|
||||||
6CEF7FA32BC88F6000E205F6 /* Flashcard+CoreDataProperties.swift in Sources */,
|
6CEF7FA32BC88F6000E205F6 /* Flashcard+CoreDataProperties.swift in Sources */,
|
||||||
6C81850A2B8BA5740033CF46 /* FlashCardListView.swift in Sources */,
|
6C81850A2B8BA5740033CF46 /* FlashCardListView.swift in Sources */,
|
||||||
6CEF7FA62BC96F2B00E205F6 /* ButtonHStackView.swift in Sources */,
|
6CEF7FA62BC96F2B00E205F6 /* ButtonHStackView.swift in Sources */,
|
||||||
@@ -271,6 +278,7 @@
|
|||||||
6CB09C2D2BE28C3E0020FE0C /* AddDeckView.swift in Sources */,
|
6CB09C2D2BE28C3E0020FE0C /* AddDeckView.swift in Sources */,
|
||||||
6C4A87D32BE25D260074E0A9 /* Deck+CoreDataProperties.swift in Sources */,
|
6C4A87D32BE25D260074E0A9 /* Deck+CoreDataProperties.swift in Sources */,
|
||||||
6CEF7F812BC4694900E205F6 /* WordAXCD.xcdatamodeld in Sources */,
|
6CEF7F812BC4694900E205F6 /* WordAXCD.xcdatamodeld in Sources */,
|
||||||
|
6C7B6D5D2BE69FA900FB6ECC /* DeckSelectView.swift in Sources */,
|
||||||
6C81850C2B8BA6BC0033CF46 /* FlashCardListRowView.swift in Sources */,
|
6C81850C2B8BA6BC0033CF46 /* FlashCardListRowView.swift in Sources */,
|
||||||
6CEF7F842BC46B5900E205F6 /* Flashcard+CoreDataClass.swift in Sources */,
|
6CEF7F842BC46B5900E205F6 /* Flashcard+CoreDataClass.swift in Sources */,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -16,24 +16,32 @@ struct AnkiView: View {
|
|||||||
@State var flashcards: [Flashcard] = []
|
@State var flashcards: [Flashcard] = []
|
||||||
@State var soonestFlashcards: [Flashcard] = []
|
@State var soonestFlashcards: [Flashcard] = []
|
||||||
@State var showDescription = false
|
@State var showDescription = false
|
||||||
|
@State var decksToDisplay = Set<Deck>()
|
||||||
|
@State var deckViewVisible: Bool = false
|
||||||
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Group {
|
|
||||||
if !flashcards.isEmpty && flashcards.first != nil {
|
|
||||||
GeometryReader { geometry in
|
GeometryReader { geometry in
|
||||||
|
VStack {
|
||||||
|
HStack {
|
||||||
|
Button {
|
||||||
|
self.deckViewVisible = true
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "rectangle.stack.fill")
|
||||||
|
.padding([.leading, .top])
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
if !flashcards.isEmpty && flashcards.first != nil {
|
||||||
VStack {
|
VStack {
|
||||||
if flashcards.first != nil {
|
if flashcards.first != nil {
|
||||||
FlashCardView(flashcard: flashcards.first!, showDescription: $showDescription)
|
FlashCardView(flashcard: flashcards.first!, showDescription: $showDescription)
|
||||||
}
|
}
|
||||||
if showDescription && flashcards.first != nil {
|
if showDescription && flashcards.first != nil {
|
||||||
// Text("How did you do?")
|
|
||||||
// .font(.subheadline)
|
|
||||||
// .foregroundStyle(.gray)
|
|
||||||
ButtonHStackView(flashcard: flashcards.first!, geometry: geometry, reload: refreshFlashcards, showDescription: $showDescription)
|
ButtonHStackView(flashcard: flashcards.first!, geometry: geometry, reload: refreshFlashcards, showDescription: $showDescription)
|
||||||
.padding([.bottom, .trailing, .leading])
|
.padding([.bottom, .trailing, .leading])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if !soonestFlashcards.isEmpty {
|
if !soonestFlashcards.isEmpty {
|
||||||
Group {
|
Group {
|
||||||
@@ -57,6 +65,7 @@ struct AnkiView: View {
|
|||||||
.clipShape(.buttonBorder)
|
.clipShape(.buttonBorder)
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
}
|
}
|
||||||
|
.frame(maxHeight: .infinity)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Text("No flashcards available")
|
Text("No flashcards available")
|
||||||
@@ -70,20 +79,34 @@ struct AnkiView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
refreshFlashcards()
|
refreshFlashcards()
|
||||||
}
|
}
|
||||||
|
.sheet(isPresented: self.$deckViewVisible) {
|
||||||
|
DeckSelectView(selection: $decksToDisplay, active: $deckViewVisible)
|
||||||
|
}
|
||||||
|
.onChange(of: deckViewVisible) { _ in
|
||||||
|
refreshFlashcards()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func refreshFlashcards() {
|
func refreshFlashcards() {
|
||||||
let requestAllFlashcards = NSFetchRequest<Flashcard>(entityName: "Flashcard")
|
let requestAllFlashcards = NSFetchRequest<Flashcard>(entityName: "Flashcard")
|
||||||
requestAllFlashcards.predicate = NSCompoundPredicate(type: .or, subpredicates: [
|
var predicateAllFlashcards = NSCompoundPredicate(type: .or, subpredicates: [
|
||||||
NSCompoundPredicate(type: .and, subpredicates: [
|
NSCompoundPredicate(type: .and, subpredicates: [
|
||||||
NSPredicate(format: "%K != nil", "lastSeenOn"),
|
NSPredicate(format: "%K != nil", "lastSeenOn"),
|
||||||
NSPredicate(format: "lastSeenOn + nextSpacedRepetitionMilestone < %@", Date() as CVarArg)
|
NSPredicate(format: "lastSeenOn + nextSpacedRepetitionMilestone < %@", Date() as CVarArg)
|
||||||
]),
|
]),
|
||||||
NSPredicate(format: "lastSeenOn == nil")
|
NSPredicate(format: "lastSeenOn == nil")
|
||||||
])
|
])
|
||||||
|
if !self.decksToDisplay.isEmpty {
|
||||||
|
predicateAllFlashcards = NSCompoundPredicate(type: .and, subpredicates: [
|
||||||
|
predicateAllFlashcards,
|
||||||
|
NSPredicate(format: "deck IN %@", decksToDisplay)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
requestAllFlashcards.predicate = predicateAllFlashcards
|
||||||
requestAllFlashcards.sortDescriptors = [
|
requestAllFlashcards.sortDescriptors = [
|
||||||
NSSortDescriptor(key: "nextSpacedRepetitionMilestone", ascending: false),
|
NSSortDescriptor(key: "nextSpacedRepetitionMilestone", ascending: false),
|
||||||
NSSortDescriptor(key: "lastSeenOn", ascending: true)
|
NSSortDescriptor(key: "lastSeenOn", ascending: true)
|
||||||
@@ -97,13 +120,16 @@ struct AnkiView: View {
|
|||||||
|
|
||||||
|
|
||||||
let requestSoonestFlashcards = NSFetchRequest<Flashcard>(entityName: "Flashcard")
|
let requestSoonestFlashcards = NSFetchRequest<Flashcard>(entityName: "Flashcard")
|
||||||
requestSoonestFlashcards.predicate = NSPredicate(format: "%K != nil", "lastSeenOn")
|
var predicateSoonestFlashcards = NSPredicate(format: "%K != nil", "lastSeenOn")
|
||||||
|
if !self.decksToDisplay.isEmpty {
|
||||||
|
predicateSoonestFlashcards = NSCompoundPredicate(type: .and, subpredicates: [
|
||||||
|
predicateSoonestFlashcards,
|
||||||
|
NSPredicate(format: "deck IN %@", decksToDisplay)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
requestSoonestFlashcards.predicate = predicateSoonestFlashcards
|
||||||
do {
|
do {
|
||||||
soonestFlashcards = try moc.fetch(requestSoonestFlashcards)
|
soonestFlashcards = try moc.fetch(requestSoonestFlashcards)
|
||||||
// print("This is soonest flashcards")
|
|
||||||
// for flashcard in soonestFlashcards {
|
|
||||||
// print("\(flashcard.name ?? "") is \(Int(flashcard.lastSeenOn!.addSpacedRepetitionMilestone(milestone:flashcard.getSpacedRepetitionMilestone()).timeIntervalSinceNow.rounded()))")
|
|
||||||
// }
|
|
||||||
} catch {
|
} catch {
|
||||||
print("Something went wrong while fetching latest flashcard")
|
print("Something went wrong while fetching latest flashcard")
|
||||||
}
|
}
|
||||||
|
|||||||
27
WordAX/Views/Deck/DeckRowView.swift
Normal file
27
WordAX/Views/Deck/DeckRowView.swift
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
//
|
||||||
|
// DeckRowView.swift
|
||||||
|
// WordAX
|
||||||
|
//
|
||||||
|
// Created by Oliver Hnát on 04.05.2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct DeckRowView: View {
|
||||||
|
var deck: Deck
|
||||||
|
var selected: Bool = false
|
||||||
|
var body: some View {
|
||||||
|
HStack {
|
||||||
|
Text(deck.name ?? "Unknown")
|
||||||
|
Spacer()
|
||||||
|
if selected {
|
||||||
|
Image(systemName: "checkmark")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
let deck = try? DataController.preview.viewContext.fetch(Deck.fetchRequest()).first
|
||||||
|
return DeckRowView(deck: deck!)
|
||||||
|
}
|
||||||
47
WordAX/Views/Deck/DeckSelectView.swift
Normal file
47
WordAX/Views/Deck/DeckSelectView.swift
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
//
|
||||||
|
// DeckSelectView.swift
|
||||||
|
// WordAX
|
||||||
|
//
|
||||||
|
// Created by Oliver Hnát on 04.05.2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct DeckSelectView: View {
|
||||||
|
@FetchRequest(sortDescriptors: [NSSortDescriptor(key: "name", ascending: true)]) var decks: FetchedResults<Deck>
|
||||||
|
@Binding var selection: Set<Deck>
|
||||||
|
@Binding var active: Bool
|
||||||
|
var body: some View {
|
||||||
|
NavigationStack {
|
||||||
|
List(decks) { deck in
|
||||||
|
DeckRowView(deck: deck, selected: selection.contains(deck))
|
||||||
|
.contentShape(Rectangle())
|
||||||
|
.onTapGesture {
|
||||||
|
if !selection.contains(deck) {
|
||||||
|
self.selection.insert(deck)
|
||||||
|
} else {
|
||||||
|
self.selection.remove(deck)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItemGroup(placement: .topBarTrailing) {
|
||||||
|
Button(action: {
|
||||||
|
self.active = false
|
||||||
|
}, label: {
|
||||||
|
Text("Done")
|
||||||
|
.bold()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle("Select Decks to display")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
@State var active = true
|
||||||
|
@State var decksToDisplay = Set<Deck>()
|
||||||
|
return DeckSelectView(selection: $decksToDisplay, active: $active)
|
||||||
|
.environment(\.managedObjectContext, DataController.preview.container.viewContext)
|
||||||
|
}
|
||||||
@@ -89,7 +89,6 @@ struct FlashCardListView: View {
|
|||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
flashcards = try moc.fetch(request)
|
flashcards = try moc.fetch(request)
|
||||||
print(flashcards)
|
|
||||||
} catch {
|
} catch {
|
||||||
print("Something went wrong while fetching the flashcards")
|
print("Something went wrong while fetching the flashcards")
|
||||||
flashcards = []
|
flashcards = []
|
||||||
|
|||||||
Reference in New Issue
Block a user