Merge branch 'main' of github.com:olinpin/WordAX
This commit is contained in:
@@ -7,16 +7,32 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
6C8184FE2B88C9580033CF46 /* WordAX.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8184FD2B88C9580033CF46 /* WordAX.swift */; };
|
||||
6C8185002B88C9660033CF46 /* WordAXModelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8184FF2B88C9660033CF46 /* WordAXModelView.swift */; };
|
||||
6C8185022B88C9FB0033CF46 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8185012B88C9FB0033CF46 /* SettingsView.swift */; };
|
||||
6C8185042B88CA210033CF46 /* AnkiView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8185032B88CA210033CF46 /* AnkiView.swift */; };
|
||||
6C8185062B8A537F0033CF46 /* WordView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8185052B8A537F0033CF46 /* WordView.swift */; };
|
||||
6C8185082B8B523E0033CF46 /* NextRepetitionButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8185072B8B523D0033CF46 /* NextRepetitionButtonView.swift */; };
|
||||
6C81850A2B8BA5740033CF46 /* WordListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8185092B8BA5740033CF46 /* WordListView.swift */; };
|
||||
6C81850C2B8BA6BC0033CF46 /* WordListRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C81850B2B8BA6BC0033CF46 /* WordListRowView.swift */; };
|
||||
6CF439522B83541D004C3543 /* WordAXApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF439512B83541D004C3543 /* WordAXApp.swift */; };
|
||||
6CF439542B83541D004C3543 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF439532B83541D004C3543 /* ContentView.swift */; };
|
||||
6CF439542B83541D004C3543 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF439532B83541D004C3543 /* MainView.swift */; };
|
||||
6CF439562B83541E004C3543 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6CF439552B83541E004C3543 /* Assets.xcassets */; };
|
||||
6CF439592B83541E004C3543 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6CF439582B83541E004C3543 /* Preview Assets.xcassets */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
6C8184FD2B88C9580033CF46 /* WordAX.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordAX.swift; sourceTree = "<group>"; };
|
||||
6C8184FF2B88C9660033CF46 /* WordAXModelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordAXModelView.swift; sourceTree = "<group>"; };
|
||||
6C8185012B88C9FB0033CF46 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
||||
6C8185032B88CA210033CF46 /* AnkiView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnkiView.swift; sourceTree = "<group>"; };
|
||||
6C8185052B8A537F0033CF46 /* WordView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordView.swift; sourceTree = "<group>"; };
|
||||
6C8185072B8B523D0033CF46 /* NextRepetitionButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextRepetitionButtonView.swift; sourceTree = "<group>"; };
|
||||
6C8185092B8BA5740033CF46 /* WordListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordListView.swift; sourceTree = "<group>"; };
|
||||
6C81850B2B8BA6BC0033CF46 /* WordListRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordListRowView.swift; sourceTree = "<group>"; };
|
||||
6CF4394E2B83541D004C3543 /* WordAX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WordAX.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
6CF439512B83541D004C3543 /* WordAXApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordAXApp.swift; sourceTree = "<group>"; };
|
||||
6CF439532B83541D004C3543 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
6CF439532B83541D004C3543 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; };
|
||||
6CF439552B83541E004C3543 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
6CF439582B83541E004C3543 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
@@ -52,7 +68,15 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6CF439512B83541D004C3543 /* WordAXApp.swift */,
|
||||
6CF439532B83541D004C3543 /* ContentView.swift */,
|
||||
6CF439532B83541D004C3543 /* MainView.swift */,
|
||||
6C8185092B8BA5740033CF46 /* WordListView.swift */,
|
||||
6C81850B2B8BA6BC0033CF46 /* WordListRowView.swift */,
|
||||
6C8185012B88C9FB0033CF46 /* SettingsView.swift */,
|
||||
6C8185032B88CA210033CF46 /* AnkiView.swift */,
|
||||
6C8185052B8A537F0033CF46 /* WordView.swift */,
|
||||
6C8185072B8B523D0033CF46 /* NextRepetitionButtonView.swift */,
|
||||
6C8184FD2B88C9580033CF46 /* WordAX.swift */,
|
||||
6C8184FF2B88C9660033CF46 /* WordAXModelView.swift */,
|
||||
6CF439552B83541E004C3543 /* Assets.xcassets */,
|
||||
6CF439572B83541E004C3543 /* Preview Content */,
|
||||
);
|
||||
@@ -137,8 +161,16 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
6CF439542B83541D004C3543 /* ContentView.swift in Sources */,
|
||||
6C8185022B88C9FB0033CF46 /* SettingsView.swift in Sources */,
|
||||
6CF439542B83541D004C3543 /* MainView.swift in Sources */,
|
||||
6C8185082B8B523E0033CF46 /* NextRepetitionButtonView.swift in Sources */,
|
||||
6C8185062B8A537F0033CF46 /* WordView.swift in Sources */,
|
||||
6C81850A2B8BA5740033CF46 /* WordListView.swift in Sources */,
|
||||
6C8185002B88C9660033CF46 /* WordAXModelView.swift in Sources */,
|
||||
6C8185042B88CA210033CF46 /* AnkiView.swift in Sources */,
|
||||
6CF439522B83541D004C3543 /* WordAXApp.swift in Sources */,
|
||||
6C8184FE2B88C9580033CF46 /* WordAX.swift in Sources */,
|
||||
6C81850C2B8BA6BC0033CF46 /* WordListRowView.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
77
WordAX.xcodeproj/xcshareddata/xcschemes/WordAX.xcscheme
Normal file
77
WordAX.xcodeproj/xcshareddata/xcschemes/WordAX.xcscheme
Normal file
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1520"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "6CF4394D2B83541D004C3543"
|
||||
BuildableName = "WordAX.app"
|
||||
BlueprintName = "WordAX"
|
||||
ReferencedContainer = "container:WordAX.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "6CF4394D2B83541D004C3543"
|
||||
BuildableName = "WordAX.app"
|
||||
BlueprintName = "WordAX"
|
||||
ReferencedContainer = "container:WordAX.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "6CF4394D2B83541D004C3543"
|
||||
BuildableName = "WordAX.app"
|
||||
BlueprintName = "WordAX"
|
||||
ReferencedContainer = "container:WordAX.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -10,5 +10,13 @@
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
<key>6CF4394D2B83541D004C3543</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
70
WordAX/AnkiView.swift
Normal file
70
WordAX/AnkiView.swift
Normal file
@@ -0,0 +1,70 @@
|
||||
//
|
||||
// AnkiView.swift
|
||||
// WordAX
|
||||
//
|
||||
// Created by Oliver Hnát on 23.02.2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct AnkiView: View {
|
||||
@EnvironmentObject var model: WordAXModelView
|
||||
@State var showDescription = true
|
||||
var word: WordAX.Word? {
|
||||
model.getWordToDisplay()
|
||||
}
|
||||
var body: some View {
|
||||
if word != nil {
|
||||
GeometryReader { geometry in
|
||||
VStack {
|
||||
WordView(word: word!, showDescription: $showDescription)
|
||||
if showDescription {
|
||||
// Text("How did you do?")
|
||||
// .font(.subheadline)
|
||||
// .foregroundStyle(.gray)
|
||||
HStack(alignment: .center) {
|
||||
NextRepetitionButtonView(
|
||||
buttonText: "Wrong",
|
||||
nextMilestone: word!.nextSpacedRepetitionMilestone,
|
||||
wordId: word!.id,
|
||||
width: geometry.size.width,
|
||||
color: .red,
|
||||
geometry: geometry,
|
||||
timeText: "1m",
|
||||
showDescription: $showDescription
|
||||
)
|
||||
NextRepetitionButtonView(
|
||||
buttonText: "Correct",
|
||||
nextMilestone: WordAX.SpacedRepetitionMilestoneEnum.getNext(milestone: word!.nextSpacedRepetitionMilestone),
|
||||
wordId: word!.id,
|
||||
width:geometry.size.width,
|
||||
color: .orange,
|
||||
geometry: geometry,
|
||||
timeText: "10m",
|
||||
showDescription: $showDescription
|
||||
)
|
||||
NextRepetitionButtonView(
|
||||
buttonText: "Easy",
|
||||
nextMilestone: WordAX.SpacedRepetitionMilestoneEnum.getNext(milestone: WordAX.SpacedRepetitionMilestoneEnum.getNext(milestone: word!.nextSpacedRepetitionMilestone)),
|
||||
wordId: word!.id,
|
||||
width: geometry.size.width,
|
||||
color: .green,
|
||||
geometry: geometry,
|
||||
timeText: "1h",
|
||||
showDescription: $showDescription
|
||||
)
|
||||
}
|
||||
.padding([.bottom, .trailing, .leading])
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Text("There is no word to display, come back later")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
AnkiView()
|
||||
.environmentObject(WordAXModelView())
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// WordAX
|
||||
//
|
||||
// Created by Oliver Hnát on 19.02.2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ContentView: View {
|
||||
var body: some View {
|
||||
VStack {
|
||||
Image(systemName: "globe")
|
||||
.imageScale(.large)
|
||||
.foregroundStyle(.tint)
|
||||
Text("Hello, world!")
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ContentView()
|
||||
}
|
||||
48
WordAX/MainView.swift
Normal file
48
WordAX/MainView.swift
Normal file
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// WordAX
|
||||
//
|
||||
// Created by Oliver Hnát on 19.02.2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct MainView: View {
|
||||
@State var selectedTab = "Anki"
|
||||
init() {
|
||||
UITabBar.appearance().backgroundColor = UIColor.systemGray5
|
||||
}
|
||||
var body: some View {
|
||||
ZStack {
|
||||
TabView(selection: $selectedTab) {
|
||||
Group {
|
||||
AnkiView()
|
||||
.tag("Anki")
|
||||
.tabItem {
|
||||
Image(systemName: "star")
|
||||
Text("Anki")
|
||||
}
|
||||
WordListView()
|
||||
.tag("Word List")
|
||||
.tabItem {
|
||||
Image(systemName: "list.bullet")
|
||||
Text("Word List")
|
||||
}
|
||||
SettingsView()
|
||||
.tag("Settings")
|
||||
.tabItem {
|
||||
Image(systemName: "gear")
|
||||
Text("Settings")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
MainView()
|
||||
.environmentObject(WordAXModelView())
|
||||
}
|
||||
54
WordAX/NextRepetitionButtonView.swift
Normal file
54
WordAX/NextRepetitionButtonView.swift
Normal file
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// NextRepetitionButtonView.swift
|
||||
// WordAX
|
||||
//
|
||||
// Created by Oliver Hnát on 25.02.2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct NextRepetitionButtonView: View {
|
||||
let buttonText: String
|
||||
let nextMilestone: WordAX.SpacedRepetitionMilestoneEnum?
|
||||
let wordId: Int
|
||||
let width: CGFloat
|
||||
let color: Color
|
||||
let geometry: GeometryProxy
|
||||
let timeText: String
|
||||
// { colorScheme == .light ? .cyan : .darkCyan }
|
||||
@Binding var showDescription: Bool
|
||||
@EnvironmentObject var model: WordAXModelView
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
var body: some View {
|
||||
Button(action: {
|
||||
model.ankiButtonClicked(wordId: wordId, milestone: nextMilestone)
|
||||
self.showDescription = false
|
||||
}) {
|
||||
VStack {
|
||||
Text(buttonText)
|
||||
Text(">" + timeText)
|
||||
.font(.footnote)
|
||||
.bold()
|
||||
}
|
||||
.padding(.vertical, geometry.size.height / 80)
|
||||
.foregroundColor(colorScheme == .light ? .black : .white)
|
||||
.frame(maxWidth: width)
|
||||
}
|
||||
.background(color)
|
||||
.buttonStyle(.plain)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 10))
|
||||
}
|
||||
}
|
||||
|
||||
extension ShapeStyle where Self == Color {
|
||||
// color darker version of cyan from hex palette
|
||||
public static var darkCyan: Color {
|
||||
Color(red: 34/255, green: 121/255, blue: 161/255)
|
||||
}
|
||||
}
|
||||
|
||||
//#Preview {
|
||||
// @State var showDescription = false
|
||||
// return NextRepetitionButtonView(buttonText: "Excellent", nextMilestone: WordAX.SpacedRepetitionMilestoneEnum.OneDay, wordId: 0, showDescription: $showDescription)
|
||||
// .environmentObject(WordAXModelView())
|
||||
//}
|
||||
19
WordAX/SettingsView.swift
Normal file
19
WordAX/SettingsView.swift
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// SettingsView.swift
|
||||
// WordAX
|
||||
//
|
||||
// Created by Oliver Hnát on 23.02.2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SettingsView: View {
|
||||
var body: some View {
|
||||
Text("This is settings")
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
SettingsView()
|
||||
.environmentObject(WordAXModelView())
|
||||
}
|
||||
102
WordAX/WordAX.swift
Normal file
102
WordAX/WordAX.swift
Normal file
@@ -0,0 +1,102 @@
|
||||
//
|
||||
// WordAX.swift
|
||||
// WordAX
|
||||
//
|
||||
// Created by Oliver Hnát on 23.02.2024.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct WordAX {
|
||||
struct Word: Identifiable, Hashable {
|
||||
var id: Int
|
||||
var name: String
|
||||
var description: String
|
||||
var shown: Bool
|
||||
var nextSpacedRepetitionMilestone: SpacedRepetitionMilestoneEnum?
|
||||
var lastSeenOn: Date?
|
||||
var shownCount: Int = 0
|
||||
}
|
||||
enum FrequencyEnum: Int {
|
||||
case Daily = 1
|
||||
case Weekly = 7
|
||||
case BiWeekly = 14
|
||||
case Monthly = 30
|
||||
}
|
||||
|
||||
enum SpacedRepetitionMilestoneEnum: Int, CaseIterable {
|
||||
case OneDay = 1
|
||||
case OneWeek = 7
|
||||
case TwoWeeks = 14
|
||||
case OneMonth = 30
|
||||
case TwoMonths = 60
|
||||
case FiveMonths = 150
|
||||
case OneYear = 365
|
||||
|
||||
static var allCasesSorted: [SpacedRepetitionMilestoneEnum] {
|
||||
allCases.sorted {$0.rawValue < $1.rawValue }
|
||||
}
|
||||
|
||||
static func getNext(milestone: SpacedRepetitionMilestoneEnum?) -> SpacedRepetitionMilestoneEnum? {
|
||||
if milestone == nil {
|
||||
return SpacedRepetitionMilestoneEnum.OneDay
|
||||
}
|
||||
let sorted = WordAX.SpacedRepetitionMilestoneEnum.allCasesSorted
|
||||
let milestoneIndex = sorted.firstIndex(where: {$0.rawValue == milestone!.rawValue})!
|
||||
if milestoneIndex < WordAX.SpacedRepetitionMilestoneEnum.allCasesSorted.count {
|
||||
return sorted[milestoneIndex + 1]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
struct Settings {
|
||||
var frequency: FrequencyEnum = .Daily
|
||||
var lastShownNew: Date?
|
||||
var dateFormatter: DateFormatter
|
||||
}
|
||||
|
||||
public mutating func setNextSpacedRepetitionMilestone(word: Word) {
|
||||
if word.nextSpacedRepetitionMilestone != nil {
|
||||
let current = SpacedRepetitionMilestoneEnum.allCasesSorted.firstIndex(of: word.nextSpacedRepetitionMilestone!) ?? SpacedRepetitionMilestoneEnum.allCases.count
|
||||
let index = words.firstIndex(where:{$0.id == word.id}) ?? nil
|
||||
if current + 1 < SpacedRepetitionMilestoneEnum.allCases.count && index != nil {
|
||||
words[index!].nextSpacedRepetitionMilestone = SpacedRepetitionMilestoneEnum.allCasesSorted[current + 1]
|
||||
} else if index != nil {
|
||||
words[index!].nextSpacedRepetitionMilestone = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public mutating func setSpacedRepetitionMilestone(wordId: Int, milestone: SpacedRepetitionMilestoneEnum?) {
|
||||
let index = words.firstIndex(where:{$0.id == wordId}) ?? nil
|
||||
if index != nil {
|
||||
words[index!].nextSpacedRepetitionMilestone = milestone
|
||||
if !words[index!].shown {
|
||||
words[index!].shown = true
|
||||
}
|
||||
words[index!].lastSeenOn = Date()
|
||||
}
|
||||
}
|
||||
|
||||
public mutating func wordShown(wordId: Int) {
|
||||
let index = words.firstIndex(where:{$0.id == wordId}) ?? nil
|
||||
if index != nil {
|
||||
words[index!].shownCount += 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var words: [Word] = []
|
||||
var settings: Settings
|
||||
|
||||
init() {
|
||||
self.words = []
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = "dd/MM/YYYY"
|
||||
self.settings = Settings(dateFormatter: dateFormatter)
|
||||
self.words.append(Word(id: 0, name: "Magnificent", description: "When something is awesome. Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.", shown: false))
|
||||
self.words.append(Word(id: 1, name: "Mesmerising", description: "When something is beautiful", shown: false))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,9 +9,11 @@ import SwiftUI
|
||||
|
||||
@main
|
||||
struct WordAXApp: App {
|
||||
@StateObject var model = WordAXModelView()
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
MainView()
|
||||
.environmentObject(model)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
8
WordAX/WordAXModel.swift
Normal file
8
WordAX/WordAXModel.swift
Normal file
@@ -0,0 +1,8 @@
|
||||
//
|
||||
// WordAXModel.swift
|
||||
// WordAX
|
||||
//
|
||||
// Created by Oliver Hnát on 23.02.2024.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
111
WordAX/WordAXModelView.swift
Normal file
111
WordAX/WordAXModelView.swift
Normal file
@@ -0,0 +1,111 @@
|
||||
//
|
||||
// WordAXModelView.swift
|
||||
// WordAX
|
||||
//
|
||||
// Created by Oliver Hnát on 23.02.2024.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class WordAXModelView: ObservableObject {
|
||||
@Published private var model: WordAX
|
||||
typealias Word = WordAX.Word
|
||||
init() {
|
||||
model = WordAX()
|
||||
}
|
||||
|
||||
public var words: [Word] {
|
||||
model.words
|
||||
}
|
||||
|
||||
public func getDateFormatter() -> DateFormatter {
|
||||
model.settings.dateFormatter
|
||||
}
|
||||
|
||||
public func getWordToDisplay() -> Word? {
|
||||
let words = model.words
|
||||
|
||||
if words.count > 0 {
|
||||
let notShownWords = words.filter({!$0.shown})
|
||||
if notShownWords.count == 0 {
|
||||
return nil
|
||||
}
|
||||
// if today is the date they're supposed to be shown
|
||||
|
||||
let displayToday = words.filter({ $0.lastSeenOn != nil && $0.lastSeenOn!.addSpacedRepetitionMilestone(milestone: $0.nextSpacedRepetitionMilestone).isBeforeTodayOrToday()})
|
||||
if displayToday.count > 0 {
|
||||
return displayToday.first!
|
||||
}
|
||||
|
||||
// let shownWords = words.filter({ $0.shown })
|
||||
// if shownWords.count == 0 {
|
||||
return notShownWords.sorted(by: {$0.id < $1.id}).first
|
||||
// }
|
||||
// if today is the day to show a new word
|
||||
// let settings = model.settings
|
||||
// if shownWords.count == 0 ||
|
||||
// settings.lastShownNew == nil ||
|
||||
// settings.lastShownNew!.addFrequency(frequency: settings.frequency).isAfterToday() {
|
||||
// return words.first!
|
||||
// }
|
||||
}
|
||||
// otherwise show nothing
|
||||
return nil
|
||||
}
|
||||
|
||||
public func ankiButtonClicked(wordId: Int, milestone: WordAX.SpacedRepetitionMilestoneEnum?) {
|
||||
model.setSpacedRepetitionMilestone(wordId: wordId, milestone: milestone)
|
||||
model.wordShown(wordId: wordId)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension Date {
|
||||
private func getOnlyDate(date: Date) -> DateComponents {
|
||||
Calendar.current.dateComponents([.day, .month, .year], from: date)
|
||||
}
|
||||
func isSameAs(_ date: Date) -> Bool {
|
||||
let selfDate = getOnlyDate(date: self)
|
||||
let paramDate = getOnlyDate(date: date)
|
||||
return selfDate.day == paramDate.day && selfDate.month == paramDate.month && selfDate.year == paramDate.year
|
||||
}
|
||||
|
||||
func isToday() -> Bool {
|
||||
self.isSameAs(Date())
|
||||
}
|
||||
|
||||
func isAfter(_ date: Date) -> Bool {
|
||||
let selfDate = getOnlyDate(date: self)
|
||||
let paramDate = getOnlyDate(date: date)
|
||||
return selfDate.year! > paramDate.year! || selfDate.month! > paramDate.month! || selfDate.day! > paramDate.day!
|
||||
}
|
||||
|
||||
func isBefore(_ date: Date) -> Bool {
|
||||
let selfDate = getOnlyDate(date: self)
|
||||
let paramDate = getOnlyDate(date: date)
|
||||
return selfDate.year! < paramDate.year! || selfDate.month! < paramDate.month! || selfDate.day! < paramDate.day!
|
||||
}
|
||||
|
||||
func addFrequency(frequency: WordAX.FrequencyEnum) -> Date {
|
||||
self.addingTimeInterval(TimeInterval(frequency.rawValue * 24 * 60 * 60))
|
||||
}
|
||||
|
||||
func addSpacedRepetitionMilestone(milestone: WordAX.SpacedRepetitionMilestoneEnum?) -> Date {
|
||||
if milestone == nil {
|
||||
return self
|
||||
}
|
||||
return self.addingTimeInterval(TimeInterval(milestone!.rawValue * 24 * 60 * 60))
|
||||
}
|
||||
|
||||
func isAfterToday() -> Bool {
|
||||
self.isAfter(Date())
|
||||
}
|
||||
|
||||
func isBeforeToday() -> Bool {
|
||||
self.isBefore(Date())
|
||||
}
|
||||
|
||||
func isBeforeTodayOrToday() -> Bool {
|
||||
self.isBeforeToday() || self.isToday()
|
||||
}
|
||||
}
|
||||
57
WordAX/WordListRowView.swift
Normal file
57
WordAX/WordListRowView.swift
Normal file
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// WordListRowView.swift
|
||||
// WordAX
|
||||
//
|
||||
// Created by Oliver Hnát on 25.02.2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct WordListRowView: View {
|
||||
@EnvironmentObject var model: WordAXModelView
|
||||
var word: WordAX.Word
|
||||
@State var favorite = true
|
||||
var body: some View {
|
||||
HStack {
|
||||
Group {
|
||||
if favorite {
|
||||
Image(systemName: "star")
|
||||
} else {
|
||||
ZStack {
|
||||
Image(systemName: "star.fill")
|
||||
.foregroundStyle(.yellow)
|
||||
Image(systemName: "star")
|
||||
.opacity(0.4)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onTapGesture {
|
||||
self.favorite = !self.favorite
|
||||
}
|
||||
.padding(.trailing)
|
||||
VStack {
|
||||
Text(word.name)
|
||||
.bold()
|
||||
.font(.system(size: 19))
|
||||
.multilineTextAlignment(.leading)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
Text(word.description)
|
||||
.multilineTextAlignment(.leading)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
Group {
|
||||
WordListRowView(word: WordAX.Word(id: 0, name: "Mesmerizing", description: "Some very long description like Lorem Ipsum which I'm to lazy to copy", shown: false, nextSpacedRepetitionMilestone: WordAX.SpacedRepetitionMilestoneEnum.OneDay, lastSeenOn: Date(), shownCount: 1))
|
||||
.environmentObject(WordAXModelView())
|
||||
WordListRowView(word: WordAX.Word(id: 0, name: "Mesmerizing", description: "Some very long description like Lorem Ipsum which I'm to lazy to copy", shown: false, nextSpacedRepetitionMilestone: WordAX.SpacedRepetitionMilestoneEnum.OneDay, lastSeenOn: Date(), shownCount: 1))
|
||||
.environmentObject(WordAXModelView())
|
||||
WordListRowView(word: WordAX.Word(id: 0, name: "Mesmerizing", description: "Some very long description like Lorem Ipsum which I'm to lazy to copy", shown: false, nextSpacedRepetitionMilestone: WordAX.SpacedRepetitionMilestoneEnum.OneDay, lastSeenOn: Date(), shownCount: 1))
|
||||
.environmentObject(WordAXModelView())
|
||||
}
|
||||
}
|
||||
32
WordAX/WordListView.swift
Normal file
32
WordAX/WordListView.swift
Normal file
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// AllWordsView.swift
|
||||
// WordAX
|
||||
//
|
||||
// Created by Oliver Hnát on 25.02.2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct WordListView: View {
|
||||
@EnvironmentObject var model: WordAXModelView
|
||||
@State var showDescription = true
|
||||
var body: some View {
|
||||
NavigationSplitView {
|
||||
List(model.words) { word in
|
||||
NavigationLink {
|
||||
WordView(word: word, showDescription: $showDescription)
|
||||
} label: {
|
||||
WordListRowView(word: word)
|
||||
}
|
||||
}
|
||||
.navigationTitle("Word List")
|
||||
} detail: {
|
||||
Text("Select word to get details about")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
WordListView()
|
||||
.environmentObject(WordAXModelView())
|
||||
}
|
||||
52
WordAX/WordView.swift
Normal file
52
WordAX/WordView.swift
Normal file
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// WordView.swift
|
||||
// WordAX
|
||||
//
|
||||
// Created by Oliver Hnát on 24.02.2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
struct WordView: View {
|
||||
var word: WordAX.Word
|
||||
@Binding var showDescription: Bool
|
||||
@EnvironmentObject var model: WordAXModelView
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
|
||||
|
||||
var body: some View {
|
||||
let wordText = Text(word.name)
|
||||
.font(.title)
|
||||
.bold()
|
||||
VStack {
|
||||
if word.shown && word.lastSeenOn != nil {
|
||||
Text("Last seen: " + model.getDateFormatter().string(from: word.lastSeenOn!))
|
||||
.font(.subheadline)
|
||||
}
|
||||
if showDescription {
|
||||
wordText
|
||||
.textSelection(.enabled)
|
||||
Divider()
|
||||
.background(colorScheme == .light ? Color.black : Color.white)
|
||||
.padding(.horizontal)
|
||||
Text(word.description)
|
||||
.multilineTextAlignment(.center)
|
||||
} else {
|
||||
wordText
|
||||
}
|
||||
}
|
||||
.padding([.horizontal, .top])
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
self.showDescription = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
@State var showDescription = false
|
||||
return WordView(word: WordAXModelView().getWordToDisplay()!, showDescription: $showDescription)
|
||||
.environmentObject(WordAXModelView())
|
||||
}
|
||||
Reference in New Issue
Block a user