From aae0b6c87ec3c565b797458cd1e98654c94c2cb8 Mon Sep 17 00:00:00 2001 From: Madeline Date: Sat, 8 Oct 2022 06:36:19 -0400 Subject: [PATCH] Add dictionary view and change how phrase lookup works --- Toki Trainer.xcodeproj/project.pbxproj | 4 + .../ViewModels/TokiDictionaryViewModel.swift | 48 ++++- Toki Trainer/Views/ContentView.swift | 190 +++++++++++++++--- Toki Trainer/Views/DictionaryView.swift | 34 ++++ 4 files changed, 234 insertions(+), 42 deletions(-) create mode 100644 Toki Trainer/Views/DictionaryView.swift diff --git a/Toki Trainer.xcodeproj/project.pbxproj b/Toki Trainer.xcodeproj/project.pbxproj index 3ad2f2b..3abb989 100644 --- a/Toki Trainer.xcodeproj/project.pbxproj +++ b/Toki Trainer.xcodeproj/project.pbxproj @@ -42,6 +42,7 @@ 7E943A2D273211C300E7DDF4 /* Toki_Trainer.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 7E943A2B273211C300E7DDF4 /* Toki_Trainer.xcdatamodeld */; }; 7EBAE6AA273D65FD00BCFA09 /* toki-lessons.json in Resources */ = {isa = PBXBuildFile; fileRef = 7EBAE6A9273D65FD00BCFA09 /* toki-lessons.json */; }; 7EF546162737B8FB00537AE6 /* FlashCardResultsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EF546152737B8FA00537AE6 /* FlashCardResultsView.swift */; }; + E1D79AE328EC396200A104BF /* DictionaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1D79AE228EC396200A104BF /* DictionaryView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -100,6 +101,7 @@ 7E943A2C273211C300E7DDF4 /* Toki_Trainer.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Toki_Trainer.xcdatamodel; sourceTree = ""; }; 7EBAE6A9273D65FD00BCFA09 /* toki-lessons.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "toki-lessons.json"; sourceTree = ""; }; 7EF546152737B8FA00537AE6 /* FlashCardResultsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlashCardResultsView.swift; sourceTree = ""; }; + E1D79AE228EC396200A104BF /* DictionaryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DictionaryView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -143,6 +145,7 @@ 7EF546152737B8FA00537AE6 /* FlashCardResultsView.swift */, 7E716B4127398CDF009E2CF6 /* FlashCardLessonsView.swift */, 7E716B452739B968009E2CF6 /* FlashCardLessonResultsView.swift */, + E1D79AE228EC396200A104BF /* DictionaryView.swift */, ); path = Views; sourceTree = ""; @@ -358,6 +361,7 @@ 7E716B4427398D3D009E2CF6 /* FlashCardLessonsViewModel.swift in Sources */, 7E2811192733027F0063DC78 /* TokiPartOfSpeech.swift in Sources */, 7E449780275AA0620016B6DC /* Toki_Trainer_Widgets.intentdefinition in Sources */, + E1D79AE328EC396200A104BF /* DictionaryView.swift in Sources */, 7E943A23273211C200E7DDF4 /* ContentView.swift in Sources */, 7EF546162737B8FB00537AE6 /* FlashCardResultsView.swift in Sources */, 7E716B4227398CDF009E2CF6 /* FlashCardLessonsView.swift in Sources */, diff --git a/Toki Trainer/ViewModels/TokiDictionaryViewModel.swift b/Toki Trainer/ViewModels/TokiDictionaryViewModel.swift index b55dd8b..f26d088 100644 --- a/Toki Trainer/ViewModels/TokiDictionaryViewModel.swift +++ b/Toki Trainer/ViewModels/TokiDictionaryViewModel.swift @@ -17,6 +17,8 @@ class TokiDictionaryViewModel: ObservableObject { @Published var dictionary: [TokiDictEntry] = [] @Published var partsOfSpeech: [TokiPartOfSpeech] = [] + @Published var translatedDictionary: [TokiSubWordListEntry] = [] + init() { if let safeDictionary = jsonLoader.loadDictionary() { dictionary = safeDictionary @@ -30,17 +32,32 @@ class TokiDictionaryViewModel: ObservableObject { func filterDictionary(_ input: String) { dictionary = [] - if(input.isEmpty) { - dictionary = fullDictionary - } else { - let capturePattern = #"(\w+)"# - let captures = self.searchStringForRegex(string: input, regex: capturePattern) - for capture in captures { - print(capture) - for value in fullDictionary { - if value.word == capture { - dictionary.append(value) - } + + for value in fullDictionary { + if value.word.hasPrefix(input) { + dictionary.append(value) + } + } + } + + func translatePhrase(_ input: String) { + dictionary = [] + translatedDictionary = [] + + let capturePattern = #"(\w+)"# + let captures = self.searchStringForRegex(string: input, regex: capturePattern) + for capture in captures { + let translatedWord = TokiSubWordListEntry(capture) + translatedDictionary.append(translatedWord) + for value in fullDictionary { + if value.word == capture { + dictionary.append(value) + translatedWord.subDictionary.removeAll() + translatedWord.subDictionary.append(value) + break + } else if value.word.hasPrefix(capture) { + dictionary.append(value) + translatedWord.subDictionary.append(value) } } } @@ -58,3 +75,12 @@ class TokiDictionaryViewModel: ObservableObject { } } } + +class TokiSubWordListEntry: Identifiable { + let header: String + var subDictionary: [TokiDictEntry] = [] + + init(_ header: String) { + self.header = header + } +} diff --git a/Toki Trainer/Views/ContentView.swift b/Toki Trainer/Views/ContentView.swift index 35107fb..d64a930 100644 --- a/Toki Trainer/Views/ContentView.swift +++ b/Toki Trainer/Views/ContentView.swift @@ -19,6 +19,11 @@ struct ContentView: View { var body: some View { TabView { + DictionaryView() + .tabItem { + Image(systemName: "book") + Text("Dictionary") + } TranslatorView() .tabItem { Image(systemName: "message") @@ -29,11 +34,11 @@ struct ContentView: View { Image(systemName: "character.textbox") Text("Flash Cards") } -// FlashCardResultsView() -// .tabItem { -// Image(systemName: "number.circle") -// Text("Flash Card Results") -// } + // FlashCardResultsView() + // .tabItem { + // Image(systemName: "number.circle") + // Text("Flash Card Results") + // } } } @@ -46,48 +51,171 @@ struct TranslatorView: View { @ObservedObject var tokiDictViewModel = TokiDictionaryViewModel() @State private var selectedPartOfSpeech: String? @State private var tokiInput: String = "" + @State private var translateToTokiPona: Bool = false var body: some View { VStack { + Button(action: changeTranslationDirection) { + if translateToTokiPona == true { + LanguageDirectionView(from: "English", to: "Toki Pona", fromColor: .blue, toColor: .cyan) + } else { + LanguageDirectionView(from: "Toki Pona", to: "English", fromColor: .cyan, toColor: .blue) + } + } TextField("Enter Toki Pona Word or Phrase", text: $tokiInput) .multilineTextAlignment(.center) .textInputAutocapitalization(.never) .disableAutocorrection(true) .padding(8) .onSubmit { - tokiDictViewModel.filterDictionary(tokiInput) + tokiDictViewModel.translatePhrase(tokiInput) } - List(tokiDictViewModel.dictionary, id: \.word) { entry in - VStack(alignment: .leading) { - Text(entry.word) - .font(.title) - ForEach(entry.definitions, id: \.pos) { definition in - HStack(alignment: .top) { - Button(action: { - self.selectedPartOfSpeech = String(definition.pos) - }) { - Text(definition.pos) - .frame(width: 45, height: 22, alignment: .center) - .foregroundColor(.black) - .background(Color(K.posColors[definition.pos]!)) - .cornerRadius(5.0) - .padding(4) - } - .buttonStyle(BorderlessButtonStyle()) - Text(definition.definition) - .fixedSize(horizontal: false, vertical: true) + if tokiInput.count == 0 { + List(tokiDictViewModel.dictionary, id: \.word) { entry in + TokiWordsListEntryView(entry: entry, selectedPartOfSpeech: $selectedPartOfSpeech) + } + //TokiWordsView(tokiDictViewModel: tokiDictViewModel, selectedPartOfSpeech: selectedPartOfSpeech, tokiInput: $tokiInput) + } else { + List(tokiDictViewModel.translatedDictionary, id: \.header) { section in + Section { + ForEach(section.subDictionary, id: \.word) { entry in + TokiWordsListEntryView(entry: entry, selectedPartOfSpeech: $selectedPartOfSpeech) + } + } header: { + Text(section.header) + } + } +// ForEach(tokiDictViewModel.translatedDictionary) { entry in +// TokiSectionedWordsView(tokiDictViewModel: tokiDictViewModel, tokiInput: $tokiInput) +// } + } + } + .onChange(of: tokiInput) { newValue in + tokiDictViewModel.translatePhrase(newValue) + } + } + + func changeTranslationDirection() { + translateToTokiPona.toggle() + } +} + +struct TokiSectionedWordsView: View { + @ObservedObject var tokiDictViewModel: TokiDictionaryViewModel + @Binding var tokiInput: String + + var body: some View { + Section(header: Text(tokiInput)) { + TokiWordsView(tokiDictViewModel: tokiDictViewModel, tokiInput: $tokiInput) + } + } +} + +struct TokiWordsListEntryView: View { + @State var entry: TokiDictEntry + @Binding var selectedPartOfSpeech: String? + + var body: some View { + VStack(alignment: .leading) { + Text(entry.word) + .font(.title) + ForEach(entry.definitions, id: \.pos) { definition in + HStack(alignment: .top) { + Button(action: { + self.selectedPartOfSpeech = String(definition.pos) + }) { + Text(definition.pos) + .frame(width: 45, height: 22, alignment: .center) + .foregroundColor(.black) + .background(Color(K.posColors[definition.pos]!)) + .cornerRadius(5.0) + .padding(4) + } + .buttonStyle(BorderlessButtonStyle()) + Text(definition.definition) + .fixedSize(horizontal: false, vertical: true) + .padding(4) + } + } + } + } +} + +struct TokiWordsView: View { + @ObservedObject var tokiDictViewModel: TokiDictionaryViewModel + @State var selectedPartOfSpeech: String? + + @Binding var tokiInput: String + + var body: some View { + List(tokiDictViewModel.dictionary, id: \.word) { entry in + VStack(alignment: .leading) { + Text(entry.word) + .font(.title) + ForEach(entry.definitions, id: \.pos) { definition in + HStack(alignment: .top) { + Button(action: { + self.selectedPartOfSpeech = String(definition.pos) + }) { + Text(definition.pos) + .frame(width: 45, height: 22, alignment: .center) + .foregroundColor(.black) + .background(Color(K.posColors[definition.pos]!)) + .cornerRadius(5.0) .padding(4) } + .buttonStyle(BorderlessButtonStyle()) + Text(definition.definition) + .fixedSize(horizontal: false, vertical: true) + .padding(4) } } } - .sheet(item: $selectedPartOfSpeech) { selectedPOS in - PartsOfSpeechView(selectedPartOfSpeech: selectedPOS, partsOfSpeech: tokiDictViewModel.partsOfSpeech) - } - .onChange(of: tokiInput) { newValue in - tokiDictViewModel.filterDictionary(newValue) - } } + .sheet(item: $selectedPartOfSpeech) { selectedPOS in + PartsOfSpeechView(selectedPartOfSpeech: selectedPOS, partsOfSpeech: tokiDictViewModel.partsOfSpeech) + } + .onChange(of: tokiInput) { newValue in + tokiDictViewModel.translatePhrase(newValue) + } + } +} + +struct LanguageDirectionView: View { + private var fromText: String = "" + private var toText: String = "" + private var fromColor: Color = .black + private var toColor: Color = .black + + init(from: String, to: String, fromColor: Color, toColor: Color) { + self.fromText = from + self.toText = to + self.fromColor = fromColor + self.toColor = toColor + } + + var body: some View { + HStack { + Text(fromText) + .bold() + .multilineTextAlignment(.center) + .foregroundColor(.white) + .frame(width: 100) + .background(fromColor) + .border(fromColor) + .cornerRadius(5) + Image(systemName: "chevron.right") + //rforegroundColor(.black) + Text(toText) + .bold() + .multilineTextAlignment(.center) + .foregroundColor(.white) + .frame(width: 100) + .background(toColor) + .border(toColor) + .cornerRadius(5) + } + .padding(.top, 4) } } diff --git a/Toki Trainer/Views/DictionaryView.swift b/Toki Trainer/Views/DictionaryView.swift new file mode 100644 index 0000000..dc791d7 --- /dev/null +++ b/Toki Trainer/Views/DictionaryView.swift @@ -0,0 +1,34 @@ +// +// DictionaryView.swift +// Toki Trainer +// +// Created by maddiefuzz on 10/4/22. +// + +import SwiftUI + +struct DictionaryView: View { + @ObservedObject var tokiDictViewModel = TokiDictionaryViewModel() + + @State var tokiInput: String = "" + @State var selectedPartOfSpeech: String? + + var body: some View { + VStack { + TextField("Search", text: $tokiInput) + .multilineTextAlignment(.center) + .textInputAutocapitalization(.never) + .disableAutocorrection(true) + .padding(8) + .onSubmit { + tokiDictViewModel.filterDictionary(tokiInput) + } + List(tokiDictViewModel.dictionary, id: \.word) { entry in + TokiWordsListEntryView(entry: entry, selectedPartOfSpeech: $selectedPartOfSpeech) + } + .onChange(of: tokiInput) { newValue in + tokiDictViewModel.filterDictionary(newValue) + } + } + } +}