Lessons implementation

This commit is contained in:
Avery Pace 2021-11-08 12:24:46 -05:00
parent c2d0dfa5e6
commit 3bce439bc4
7 changed files with 142 additions and 30 deletions

View File

@ -15,6 +15,10 @@
7E28111C273302860063DC78 /* toki-partsofspeech.json in Resources */ = {isa = PBXBuildFile; fileRef = 7E28111A273302860063DC78 /* toki-partsofspeech.json */; }; 7E28111C273302860063DC78 /* toki-partsofspeech.json in Resources */ = {isa = PBXBuildFile; fileRef = 7E28111A273302860063DC78 /* toki-partsofspeech.json */; };
7E28111D273302860063DC78 /* toki-dictionary.json in Resources */ = {isa = PBXBuildFile; fileRef = 7E28111B273302860063DC78 /* toki-dictionary.json */; }; 7E28111D273302860063DC78 /* toki-dictionary.json in Resources */ = {isa = PBXBuildFile; fileRef = 7E28111B273302860063DC78 /* toki-dictionary.json */; };
7E28112227330DD30063DC78 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E28112127330DD20063DC78 /* Constants.swift */; }; 7E28112227330DD30063DC78 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E28112127330DD20063DC78 /* Constants.swift */; };
7E716B3E273986E5009E2CF6 /* TokiLesson.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E716B3D273986E5009E2CF6 /* TokiLesson.swift */; };
7E716B4027398ABD009E2CF6 /* toki-lessons.json in Resources */ = {isa = PBXBuildFile; fileRef = 7E716B3F27398ABD009E2CF6 /* toki-lessons.json */; };
7E716B4227398CDF009E2CF6 /* FlashCardLessonsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E716B4127398CDF009E2CF6 /* FlashCardLessonsView.swift */; };
7E716B4427398D3D009E2CF6 /* FlashCardLessonsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E716B4327398D3D009E2CF6 /* FlashCardLessonsViewModel.swift */; };
7E71E6ED2735D70C007CFF72 /* FlashCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E71E6EC2735D70C007CFF72 /* FlashCardView.swift */; }; 7E71E6ED2735D70C007CFF72 /* FlashCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E71E6EC2735D70C007CFF72 /* FlashCardView.swift */; };
7E71E6F12736DAE4007CFF72 /* FlashCardsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E71E6F02736DAE4007CFF72 /* FlashCardsViewModel.swift */; }; 7E71E6F12736DAE4007CFF72 /* FlashCardsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E71E6F02736DAE4007CFF72 /* FlashCardsViewModel.swift */; };
7E943A21273211C200E7DDF4 /* Toki_TrainerApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E943A20273211C200E7DDF4 /* Toki_TrainerApp.swift */; }; 7E943A21273211C200E7DDF4 /* Toki_TrainerApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E943A20273211C200E7DDF4 /* Toki_TrainerApp.swift */; };
@ -35,6 +39,10 @@
7E28111A273302860063DC78 /* toki-partsofspeech.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "toki-partsofspeech.json"; sourceTree = "<group>"; }; 7E28111A273302860063DC78 /* toki-partsofspeech.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "toki-partsofspeech.json"; sourceTree = "<group>"; };
7E28111B273302860063DC78 /* toki-dictionary.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "toki-dictionary.json"; sourceTree = "<group>"; }; 7E28111B273302860063DC78 /* toki-dictionary.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "toki-dictionary.json"; sourceTree = "<group>"; };
7E28112127330DD20063DC78 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; }; 7E28112127330DD20063DC78 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
7E716B3D273986E5009E2CF6 /* TokiLesson.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokiLesson.swift; sourceTree = "<group>"; };
7E716B3F27398ABD009E2CF6 /* toki-lessons.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = "toki-lessons.json"; path = "../../../../../Desktop/toki-pona-dict-json/output/toki-lessons.json"; sourceTree = "<group>"; };
7E716B4127398CDF009E2CF6 /* FlashCardLessonsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlashCardLessonsView.swift; sourceTree = "<group>"; };
7E716B4327398D3D009E2CF6 /* FlashCardLessonsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlashCardLessonsViewModel.swift; sourceTree = "<group>"; };
7E71E6EC2735D70C007CFF72 /* FlashCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlashCardView.swift; sourceTree = "<group>"; }; 7E71E6EC2735D70C007CFF72 /* FlashCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlashCardView.swift; sourceTree = "<group>"; };
7E71E6F02736DAE4007CFF72 /* FlashCardsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlashCardsViewModel.swift; sourceTree = "<group>"; }; 7E71E6F02736DAE4007CFF72 /* FlashCardsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlashCardsViewModel.swift; sourceTree = "<group>"; };
7E943A1D273211C200E7DDF4 /* Toki Trainer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Toki Trainer.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 7E943A1D273211C200E7DDF4 /* Toki Trainer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Toki Trainer.app"; sourceTree = BUILT_PRODUCTS_DIR; };
@ -64,6 +72,7 @@
7E2811142733027F0063DC78 /* TokiDictionary.swift */, 7E2811142733027F0063DC78 /* TokiDictionary.swift */,
7E2811152733027F0063DC78 /* TokiJSONLoader.swift */, 7E2811152733027F0063DC78 /* TokiJSONLoader.swift */,
7E2811162733027F0063DC78 /* TokiPartOfSpeech.swift */, 7E2811162733027F0063DC78 /* TokiPartOfSpeech.swift */,
7E716B3D273986E5009E2CF6 /* TokiLesson.swift */,
); );
path = Models; path = Models;
sourceTree = "<group>"; sourceTree = "<group>";
@ -76,6 +85,7 @@
7E20D5FE2733AFE700D75B9A /* PartsOfSpeechView.swift */, 7E20D5FE2733AFE700D75B9A /* PartsOfSpeechView.swift */,
7E71E6EC2735D70C007CFF72 /* FlashCardView.swift */, 7E71E6EC2735D70C007CFF72 /* FlashCardView.swift */,
7EF546152737B8FA00537AE6 /* FlashCardResultsView.swift */, 7EF546152737B8FA00537AE6 /* FlashCardResultsView.swift */,
7E716B4127398CDF009E2CF6 /* FlashCardLessonsView.swift */,
); );
path = Views; path = Views;
sourceTree = "<group>"; sourceTree = "<group>";
@ -85,6 +95,7 @@
children = ( children = (
7E20D6002734466800D75B9A /* TokiDictionaryViewModel.swift */, 7E20D6002734466800D75B9A /* TokiDictionaryViewModel.swift */,
7E71E6F02736DAE4007CFF72 /* FlashCardsViewModel.swift */, 7E71E6F02736DAE4007CFF72 /* FlashCardsViewModel.swift */,
7E716B4327398D3D009E2CF6 /* FlashCardLessonsViewModel.swift */,
); );
path = ViewModels; path = ViewModels;
sourceTree = "<group>"; sourceTree = "<group>";
@ -93,6 +104,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7E28111B273302860063DC78 /* toki-dictionary.json */, 7E28111B273302860063DC78 /* toki-dictionary.json */,
7E716B3F27398ABD009E2CF6 /* toki-lessons.json */,
7E28111A273302860063DC78 /* toki-partsofspeech.json */, 7E28111A273302860063DC78 /* toki-partsofspeech.json */,
); );
path = "JSON Data"; path = "JSON Data";
@ -197,6 +209,7 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
7E943A28273211C300E7DDF4 /* Preview Assets.xcassets in Resources */, 7E943A28273211C300E7DDF4 /* Preview Assets.xcassets in Resources */,
7E716B4027398ABD009E2CF6 /* toki-lessons.json in Resources */,
7E943A25273211C300E7DDF4 /* Assets.xcassets in Resources */, 7E943A25273211C300E7DDF4 /* Assets.xcassets in Resources */,
7E28111D273302860063DC78 /* toki-dictionary.json in Resources */, 7E28111D273302860063DC78 /* toki-dictionary.json in Resources */,
7E28111C273302860063DC78 /* toki-partsofspeech.json in Resources */, 7E28111C273302860063DC78 /* toki-partsofspeech.json in Resources */,
@ -215,13 +228,16 @@
7E20D5FF2733AFE700D75B9A /* PartsOfSpeechView.swift in Sources */, 7E20D5FF2733AFE700D75B9A /* PartsOfSpeechView.swift in Sources */,
7E71E6F12736DAE4007CFF72 /* FlashCardsViewModel.swift in Sources */, 7E71E6F12736DAE4007CFF72 /* FlashCardsViewModel.swift in Sources */,
7E20D6012734466800D75B9A /* TokiDictionaryViewModel.swift in Sources */, 7E20D6012734466800D75B9A /* TokiDictionaryViewModel.swift in Sources */,
7E716B4427398D3D009E2CF6 /* FlashCardLessonsViewModel.swift in Sources */,
7E2811192733027F0063DC78 /* TokiPartOfSpeech.swift in Sources */, 7E2811192733027F0063DC78 /* TokiPartOfSpeech.swift in Sources */,
7E943A23273211C200E7DDF4 /* ContentView.swift in Sources */, 7E943A23273211C200E7DDF4 /* ContentView.swift in Sources */,
7EF546162737B8FB00537AE6 /* FlashCardResultsView.swift in Sources */, 7EF546162737B8FB00537AE6 /* FlashCardResultsView.swift in Sources */,
7E716B4227398CDF009E2CF6 /* FlashCardLessonsView.swift in Sources */,
7E2811172733027F0063DC78 /* TokiDictionary.swift in Sources */, 7E2811172733027F0063DC78 /* TokiDictionary.swift in Sources */,
7E71E6ED2735D70C007CFF72 /* FlashCardView.swift in Sources */, 7E71E6ED2735D70C007CFF72 /* FlashCardView.swift in Sources */,
7E943A21273211C200E7DDF4 /* Toki_TrainerApp.swift in Sources */, 7E943A21273211C200E7DDF4 /* Toki_TrainerApp.swift in Sources */,
7E2811182733027F0063DC78 /* TokiJSONLoader.swift in Sources */, 7E2811182733027F0063DC78 /* TokiJSONLoader.swift in Sources */,
7E716B3E273986E5009E2CF6 /* TokiLesson.swift in Sources */,
7E28112227330DD30063DC78 /* Constants.swift in Sources */, 7E28112227330DD30063DC78 /* Constants.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;

View File

@ -31,6 +31,17 @@ class TokiJSONLoader: ObservableObject {
} }
} }
func loadLessons() -> [TokiLesson]? {
let jsonData = loadJSON("toki-lessons")
do {
let decodedData = try JSONDecoder().decode([TokiLesson].self, from: jsonData!)
return decodedData
} catch {
print("Decode error: \(error)")
return nil
}
}
func loadJSON(_ resource: String) -> Data? { func loadJSON(_ resource: String) -> Data? {
do { do {
if let bundlePath = Bundle.main.path(forResource: resource, ofType: "json"), let jsonData = try String(contentsOfFile: bundlePath).data(using: .utf8) { if let bundlePath = Bundle.main.path(forResource: resource, ofType: "json"), let jsonData = try String(contentsOfFile: bundlePath).data(using: .utf8) {

View File

@ -0,0 +1,15 @@
//
// TokiLesson.swift
// Toki Trainer
//
// Created by Avery Ada Pace on 11/8/21.
//
import Foundation
struct TokiLesson: Decodable {
var lesson: String
var words: [TokiDictEntry]
}

View File

@ -0,0 +1,22 @@
//
// FlashCardLessonsViewModel.swift
// Toki Trainer
//
// Created by Avery Ada Pace on 11/8/21.
//
import Foundation
class FlashCardLessonsViewModel: ObservableObject {
let jsonLoader: TokiJSONLoader = TokiJSONLoader()
@Published var lessons: [TokiLesson] = []
init() {
if let safeLessons = jsonLoader.loadLessons() {
lessons = safeLessons
}
}
}

View File

@ -24,7 +24,7 @@ struct ContentView: View {
Image(systemName: "pencil") Image(systemName: "pencil")
Text("Phrase Lookup") Text("Phrase Lookup")
} }
FlashCardView() FlashCardLessonsView()
.tabItem { .tabItem {
Image(systemName: "character.textbox") Image(systemName: "character.textbox")
Text("Flash Cards") Text("Flash Cards")

View File

@ -0,0 +1,30 @@
//
// FlashCardLessonsView.swift
// Toki Trainer
//
// Created by Avery Ada Pace on 11/8/21.
//
import SwiftUI
struct FlashCardLessonsView: View {
@ObservedObject var flashCardLessonsVM = FlashCardLessonsViewModel()
var body: some View {
NavigationView {
List(flashCardLessonsVM.lessons, id: \.lesson) { lesson in
NavigationLink(destination: FlashCardView(lesson.words)) {
Text(lesson.lesson)
.bold()
}
}
.navigationBarTitle("Lessons")
}
}
}
struct FlashCardLessonsView_Previews: PreviewProvider {
static var previews: some View {
FlashCardLessonsView()
}
}

View File

@ -18,9 +18,27 @@ struct FlashCardView: View {
@ObservedObject var flashCardsViewModel = FlashCardsViewModel() @ObservedObject var flashCardsViewModel = FlashCardsViewModel()
@State var dictionary: [TokiDictEntry]?
init(_ passedDictionary: [TokiDictEntry]?) {
if passedDictionary != nil {
if let safePassedDictionary = passedDictionary {
self.dictionary = safePassedDictionary
}
}
}
var body: some View { var body: some View {
VStack { VStack {
FlashCardStack(dictionary: flashCardsViewModel.randomDictionary) FlashCardStack(dictionary: getDictionary())
}
}
func getDictionary() -> [TokiDictEntry] {
if dictionary != nil {
return dictionary ?? []
} else {
return flashCardsViewModel.randomDictionary
} }
} }
} }
@ -59,7 +77,7 @@ struct FlashCardStack: View {
ForEach(flashCards.indices, id: \.self) { index in ForEach(flashCards.indices, id: \.self) { index in
flashCards[index] flashCards[index]
.offset(x: 0, y: flashCardsVertOffset[index]) .offset(x: 0, y: flashCardsVertOffset[index])
.zIndex(-(CGFloat(index * 100))) .zIndex(-(CGFloat(index * 10)))
} }
} }
} }
@ -72,9 +90,9 @@ struct FlashCardStack: View {
}.opacity(fadeOutOverlay ? 0.0 : 1.0), alignment: .top) }.opacity(fadeOutOverlay ? 0.0 : 1.0), alignment: .top)
} }
Spacer() Spacer()
.onAppear { .onAppear {
initFlashCards() initFlashCards()
} }
} }
func initFlashCards() { func initFlashCards() {
@ -83,17 +101,17 @@ struct FlashCardStack: View {
flashCardsAreInteractive.append(false) flashCardsAreInteractive.append(false)
flashCardsResults.append(FlashCardResult.Unanswered) flashCardsResults.append(FlashCardResult.Unanswered)
flashCards.append(FlashCard(isInteractive: $flashCardsAreInteractive[index], result: $flashCardsResults[index].onChange(cardAnswerReceived), dictionaryEntry: dictionary[index])) flashCards.append(FlashCard(isInteractive: $flashCardsAreInteractive[index], result: $flashCardsResults[index].onChange(cardAnswerReceived), dictionaryEntry: dictionary[index]))
flashCardsVertOffset.append(470) flashCardsVertOffset.append(370)
} }
if flashCards.count - currentFlashCard >= 3 { if flashCards.count - currentFlashCard >= 3 {
flashCardsVertOffset[currentFlashCard + 1] = 410 flashCardsVertOffset[currentFlashCard + 1] = 310
flashCardsVertOffset[currentFlashCard + 2] = 440 flashCardsVertOffset[currentFlashCard + 2] = 340
flashCardsVertOffset[currentFlashCard + 3] = 470 flashCardsVertOffset[currentFlashCard + 3] = 370
} else if flashCards.count - currentFlashCard == 2 { } else if flashCards.count - currentFlashCard == 2 {
flashCardsVertOffset[currentFlashCard + 1] = 410 flashCardsVertOffset[currentFlashCard + 1] = 310
flashCardsVertOffset[currentFlashCard + 2] = 440 flashCardsVertOffset[currentFlashCard + 2] = 340
} else if flashCards.count - currentFlashCard == 1 { } else if flashCards.count - currentFlashCard == 1 {
flashCardsVertOffset[currentFlashCard + 1] = 410 flashCardsVertOffset[currentFlashCard + 1] = 310
} }
flashCardsVertOffset[currentFlashCard] = 100 flashCardsVertOffset[currentFlashCard] = 100
@ -126,15 +144,15 @@ struct FlashCardStack: View {
print("answer not found in database") print("answer not found in database")
} }
// for answer in flashCardAnswers { // for answer in flashCardAnswers {
// if answer.word == dictionary[currentFlashCard].word { // if answer.word == dictionary[currentFlashCard].word {
// flashCardAnswer.word = answer.word // flashCardAnswer.word = answer.word
// flashCardAnswer.triesCount = answer.triesCount + 1 // flashCardAnswer.triesCount = answer.triesCount + 1
// if correct { // if correct {
// flashCardAnswer.correctCount = answer.correctCount + 1 // flashCardAnswer.correctCount = answer.correctCount + 1
// } // }
// } // }
// } // }
try? viewContext.save() try? viewContext.save()
} }
@ -160,14 +178,14 @@ struct FlashCardStack: View {
self.fadeOutOverlay = true self.fadeOutOverlay = true
if flashCards.count - currentFlashCard >= 3 { if flashCards.count - currentFlashCard >= 3 {
flashCardsVertOffset[currentFlashCard + 1] = 410 flashCardsVertOffset[currentFlashCard + 1] = 310
flashCardsVertOffset[currentFlashCard + 2] = 440 flashCardsVertOffset[currentFlashCard + 2] = 340
flashCardsVertOffset[currentFlashCard + 3] = 470 flashCardsVertOffset[currentFlashCard + 3] = 370
} else if flashCards.count - currentFlashCard == 2 { } else if flashCards.count - currentFlashCard == 2 {
flashCardsVertOffset[currentFlashCard + 1] = 410 flashCardsVertOffset[currentFlashCard + 1] = 310
flashCardsVertOffset[currentFlashCard + 2] = 440 flashCardsVertOffset[currentFlashCard + 2] = 340
} else if flashCards.count - currentFlashCard == 1 { } else if flashCards.count - currentFlashCard == 1 {
flashCardsVertOffset[currentFlashCard + 1] = 410 flashCardsVertOffset[currentFlashCard + 1] = 310
} }
} }
@ -220,7 +238,7 @@ struct FlashCard: View {
Text("") Text("")
.modifier(CardFlipModifier(isFaceDown: isFaceDown, frontText: dictionaryEntry.word, backText: concatenateDefinitions())) .modifier(CardFlipModifier(isFaceDown: isFaceDown, frontText: dictionaryEntry.word, backText: concatenateDefinitions()))
.frame(width: 0.8 * screen.width, height: 200.0) .frame(width: 0.8 * screen.width)
.offset(x: isFaceDown ? -dragAmount : dragAmount, y: abs(dragAmount) / 10) .offset(x: isFaceDown ? -dragAmount : dragAmount, y: abs(dragAmount) / 10)
.rotationEffect(.degrees(isFaceDown ? -(dragAmount / 50) : dragAmount / 50)) .rotationEffect(.degrees(isFaceDown ? -(dragAmount / 50) : dragAmount / 50))
.font(.title) .font(.title)
@ -298,6 +316,6 @@ struct CardFlipModifier: AnimatableModifier {
struct FlashCardView_Previews: PreviewProvider { struct FlashCardView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
FlashCardView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext) FlashCardView(nil).environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
} }
} }