This commit is contained in:
Trey t
2025-12-09 23:37:04 -06:00
parent 3a10b4b8d6
commit f2565678be
1587 changed files with 7747 additions and 647 deletions

View File

@@ -0,0 +1,206 @@
//
// HomeView.swift
// Shared
//
// Created by Trey Tartt on 1/5/22.
//
import SwiftUI
import CoreData
import Charts
struct DayViewConstants {
static let maxHeaderHeight = 200.0
static let minHeaderHeight = 120.0
}
struct DayView: View {
@Environment(\.managedObjectContext) private var viewContext
@AppStorage(UserDefaultsStore.Keys.deleteEnable.rawValue, store: GroupUserDefaults.groupDefaults) private var deleteEnabled = true
@AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system
@AppStorage(UserDefaultsStore.Keys.moodImages.rawValue, store: GroupUserDefaults.groupDefaults) private var imagePack: MoodImages = .FontAwesome
@AppStorage(UserDefaultsStore.Keys.moodTint.rawValue, store: GroupUserDefaults.groupDefaults) private var moodTint: MoodTints = .Default
@AppStorage(UserDefaultsStore.Keys.textColor.rawValue, store: GroupUserDefaults.groupDefaults) private var textColor: Color = DefaultTextColor.textColor
// store a value that gets changed when user updates custom colors to update the view since the moodTint doesn't change
@AppStorage(UserDefaultsStore.Keys.customMoodTintUpdateNumber.rawValue, store: GroupUserDefaults.groupDefaults) private var customMoodTintUpdateNumber: Int = 0
// MARK: edit row properties
@State private var showingSheet = false
@State private var selectedEntry: MoodEntry?
//
// MARK: ?? properties
@State private var showTodayInput = true
@State private var showUpdateEntryAlert = false
@StateObject private var onboardingData = OnboardingDataDataManager.shared
@StateObject private var filteredDays = DaysFilterClass.shared
@EnvironmentObject var iapManager: IAPManager
@ObservedObject var viewModel: DayViewViewModel
var body: some View {
ZStack {
Text(String(customMoodTintUpdateNumber))
.hidden()
mainView
.onAppear(perform: {
EventLogger.log(event: "show_home_view")
})
.sheet(isPresented: $showingSheet) {
SettingsView()
}
.alert(DayViewViewModel.updateTitleHeader(forEntry: selectedEntry),
isPresented: $showUpdateEntryAlert) {
ForEach(Mood.allValues) { mood in
Button(mood.strValue, action: {
if let selectedEntry = selectedEntry {
viewModel.update(entry: selectedEntry, toMood: mood)
}
showUpdateEntryAlert = false
selectedEntry = nil
})
}
if let selectedEntry = selectedEntry,
deleteEnabled,
selectedEntry.mood != .missing {
Button(String(localized: "content_view_delete_entry"), action: {
viewModel.update(entry: selectedEntry, toMood: Mood.missing)
showUpdateEntryAlert = false
})
}
Button(String(localized: "content_view_fill_in_missing_entry_cancel"), role: .cancel, action: {
selectedEntry = nil
showUpdateEntryAlert = false
})
}
}
.padding([.top])
}
// MARK: Views
public var mainView: some View {
VStack {
if viewModel.hasNoData {
Spacer()
EmptyHomeView(showVote: true, viewModel: viewModel)
Spacer()
} else {
VStack {
headerView
listView
}
}
}
.padding([.leading, .trailing])
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
PersistenceController.shared.fillInMissingDates()
viewModel.updateData()
}
.background(
theme.currentTheme.bg
)
}
private var headerView: some View {
VStack {
if ShowBasedOnVoteLogics.isMissingCurrentVote(onboardingData: UserDefaultsStore.getOnboarding()) {
AddMoodHeaderView(addItemHeaderClosure: { (mood, date) in
viewModel.add(mood: mood, forDate: date, entryType: .header)
})
}
}
}
private var listView: some View {
ScrollView {
LazyVStack(spacing: 5, pinnedViews: [.sectionHeaders]) {
ForEach(viewModel.grouped.sorted(by: {
$0.key > $1.key
}), id: \.key) { year, months in
// for reach month
ForEach(months.sorted(by: {
$0.key > $1.key
}), id: \.key) { month, entries in
Section(header: SectionHeaderView(month: month, year: year)) {
monthListView(month: month, year: year, entries: entries)
}
}
}
}
.background(
GeometryReader { proxy in
let offset = proxy.frame(in: .named("scroll")).minY
Color.clear.preference(key: ViewOffsetKey.self, value: offset)
}
)
}
.background(
theme.currentTheme.secondaryBGColor
)
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight])
}
}
// view that make up the list body
extension DayView {
private func SectionHeaderView(month: Int, year: Int) -> some View {
Text("\(Random.monthName(fromMonthInt: month)) \(String(year))")
.font(.title)
.foregroundColor(textColor)
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
.background(
theme.currentTheme.secondaryBGColor
)
}
private func monthListView(month: Int, year: Int, entries: [MoodEntry]) -> some View {
VStack {
// for reach all entries
ForEach(entries.sorted(by: {
return $0.forDate! > $1.forDate!
}), id: \.self) { entry in
if filteredDays.currentFilters.contains(Int(entry.weekDay)) {
// let _ = print(entry.forDate, entry.weekDay, filteredDays.currentFilters)
EntryListView(entry: entry)
.contentShape(Rectangle())
.onTapGesture(perform: {
selectedEntry = entry
showUpdateEntryAlert = true
})
}
}
}
}
}
struct ViewOffsetKey: PreferenceKey {
typealias Value = CGFloat
static var defaultValue = CGFloat.zero
static func reduce(value: inout Value, nextValue: () -> Value) {
value += nextValue()
}
}
struct DayView_Previews: PreviewProvider {
static var previews: some View {
DayView(viewModel: DayViewViewModel(addMonthStartWeekdayPadding: false)).environment(\.managedObjectContext, PersistenceController.shared.viewContext)
.onAppear(perform: {
PersistenceController.shared.populateMemory()
})
DayView(viewModel: DayViewViewModel(addMonthStartWeekdayPadding: false))
.preferredColorScheme(.dark)
.environment(\.managedObjectContext, PersistenceController.shared.viewContext)
}
}

View File

@@ -0,0 +1,127 @@
//
// ContentModeViewModel.swift
// Feels (iOS)
//
// Created by Trey Tartt on 1/20/22.
//
import SwiftUI
import CoreData
class DayViewViewModel: ObservableObject {
@Published var grouped = [Int: [Int: [MoodEntry]]]()
@Published var numberOfItems = 0
let addMonthStartWeekdayPadding: Bool
var hasNoData: Bool {
grouped.isEmpty
}
private var numberOfEntries: Int {
var num = 0
grouped.keys.forEach({
let year = grouped[$0]
let monthKeys = year?.keys
monthKeys?.forEach({
num += year![$0]!.count
})
})
return num
// grouped.keys.map{
// grouped[$0]!.values.reduce(0) { sum, array in
// sum + array.count
// }
// }.reduce(0, +)
}
init(addMonthStartWeekdayPadding: Bool) {
self.addMonthStartWeekdayPadding = addMonthStartWeekdayPadding
PersistenceController.shared.switchContainerListeners.append {
self.getGroupedData(addMonthStartWeekdayPadding: self.addMonthStartWeekdayPadding)
}
PersistenceController.shared.addNewDataListener {
withAnimation{
self.updateData()
}
}
updateData()
}
private func getGroupedData(addMonthStartWeekdayPadding: Bool) {
var newStuff = PersistenceController.shared.splitIntoYearMonth(includedDays: [1,2,3,4,5,6,7])
if addMonthStartWeekdayPadding {
newStuff = MoodEntryFunctions.padMoodEntriesForCalendar(entries: newStuff)
}
grouped = newStuff
numberOfItems = numberOfEntries
}
public func updateData() {
getGroupedData(addMonthStartWeekdayPadding: self.addMonthStartWeekdayPadding)
}
public func add(mood: Mood, forDate date: Date, entryType: EntryType) {
PersistenceController.shared.add(mood: mood, forDate: date, entryType: entryType)
}
public func update(entry: MoodEntry, toMood mood: Mood) {
if !PersistenceController.shared.update(entryDate: entry.forDate!, withModd: mood) {
#warning("show error")
}
}
public func delete(offsets: IndexSet, inMonth month: Int, inYear year: Int) {
if let monthEntries = grouped[year],
let entries = monthEntries[month] {
var mutableEntries = entries.sorted(by: {
$0.forDate! > $1.forDate!
})
var entriesToDelete = [MoodEntry]()
for idx in offsets {
let obj = mutableEntries.remove(at: idx)
entriesToDelete.append(obj)
}
entriesToDelete.forEach({ entry in
let entryDate = entry.forDate!
PersistenceController.shared.viewContext.delete(entry)
self.add(mood: .missing, forDate: entryDate, entryType: .listView)
})
}
do {
try PersistenceController.shared.viewContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
static func updateTitleHeader(forEntry entry: MoodEntry?) -> String {
guard let entry = entry else {
return ""
}
guard let forDate = entry.forDate else {
return ""
}
let components = Calendar.current.dateComponents([.day, .month, .year], from: forDate)
// let day = components.day!
let month = components.month!
let year = components.year!
let monthName = Random.monthName(fromMonthInt: month)
let weekday = Random.weekdayName(fromDate:entry.forDate!)
let dayz = Random.dayFormat(fromDate:entry.forDate!)
let string = weekday + " " + monthName + " " + dayz + " " + String(year)
return String(format: String(localized: "content_view_fill_in_missing_entry"), string)
}
}