From ad196b08c53f4fdbe50edea517cc70f82435596e Mon Sep 17 00:00:00 2001 From: Trey t Date: Thu, 10 Feb 2022 23:30:19 -0600 Subject: [PATCH] code clean up --- Feels.xcodeproj/project.pbxproj | 4 + Shared/Random.swift | 21 +++ Shared/views/ContentView.swift | 317 ++++++++++++++------------------ Shared/views/EmptyView.swift | 45 +++++ 4 files changed, 213 insertions(+), 174 deletions(-) create mode 100644 Shared/views/EmptyView.swift diff --git a/Feels.xcodeproj/project.pbxproj b/Feels.xcodeproj/project.pbxproj index 36ce2d7..83a2d11 100644 --- a/Feels.xcodeproj/project.pbxproj +++ b/Feels.xcodeproj/project.pbxproj @@ -41,6 +41,7 @@ 1CAD603B27A5C1C800C520BD /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CAD603227A5C1C800C520BD /* ContentView.swift */; }; 1CAD603C27A5C1C800C520BD /* HeaderStatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CAD603327A5C1C800C520BD /* HeaderStatsView.swift */; }; 1CAD603E27A6ECCD00C520BD /* SwitchableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CAD603D27A6ECCD00C520BD /* SwitchableView.swift */; }; + 1CB101C527B62A2D00D1C033 /* EmptyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CB101C427B62A2D00D1C033 /* EmptyView.swift */; }; 1CC469AA278F30A0003E0C6E /* BGTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CC469A9278F30A0003E0C6E /* BGTask.swift */; }; 1CC469AC27907D48003E0C6E /* DayChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CC469AB27907D48003E0C6E /* DayChartView.swift */; }; 1CD90B07278C7DE0001C4FEA /* Tests_iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B06278C7DE0001C4FEA /* Tests_iOS.swift */; }; @@ -142,6 +143,7 @@ 1CAD603227A5C1C800C520BD /* ContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 1CAD603327A5C1C800C520BD /* HeaderStatsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderStatsView.swift; sourceTree = ""; }; 1CAD603D27A6ECCD00C520BD /* SwitchableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchableView.swift; sourceTree = ""; }; + 1CB101C427B62A2D00D1C033 /* EmptyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyView.swift; sourceTree = ""; }; 1CC03FA627B5865600B530AF /* Shared 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Shared 2.xcdatamodel"; sourceTree = ""; }; 1CC469A9278F30A0003E0C6E /* BGTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGTask.swift; sourceTree = ""; }; 1CC469AB27907D48003E0C6E /* DayChartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayChartView.swift; sourceTree = ""; }; @@ -258,6 +260,7 @@ 1CAD602F27A5C1C800C520BD /* AddMoodHeaderView.swift */, 1CAD603127A5C1C800C520BD /* BGView.swift */, 1CAD603227A5C1C800C520BD /* ContentView.swift */, + 1CB101C427B62A2D00D1C033 /* EmptyView.swift */, 1CAD602E27A5C1C800C520BD /* FilterView.swift */, 1CAD602D27A5C1C800C520BD /* GraphView.swift */, 1CAD603027A5C1C800C520BD /* HeaderPercView.swift */, @@ -606,6 +609,7 @@ 1CD90B53278C7E7A001C4FEA /* FeelsWidget.intentdefinition in Sources */, 1CC469AC27907D48003E0C6E /* DayChartView.swift in Sources */, 1C26190327960CE500FDC148 /* ChartDataBuildable.swift in Sources */, + 1CB101C527B62A2D00D1C033 /* EmptyView.swift in Sources */, 1CAD603627A5C1C800C520BD /* GraphView.swift in Sources */, 1CD90B66278C7EBA001C4FEA /* MoodEntryExtension.swift in Sources */, 1CD90B1C278C7DE0001C4FEA /* Persistence.swift in Sources */, diff --git a/Shared/Random.swift b/Shared/Random.swift index ba44317..9a53a1b 100644 --- a/Shared/Random.swift +++ b/Shared/Random.swift @@ -28,6 +28,27 @@ class Random { } return updateTime } + + static func weekdayName(fromDate date: Date) -> String { + let weekday = Calendar.current.component(.weekday, from: date) + let calendar = Calendar.current + let dayIndex = ((weekday - 1) + (calendar.firstWeekday - 1)) % 7 + return calendar.weekdaySymbols[dayIndex] + } + + static func monthName(fromMonthInt: Int) -> String { + let monthName = DateFormatter().monthSymbols[fromMonthInt-1] + return monthName + } + + static func dayFormat(fromDate date: Date) -> String { + let components = Calendar.current.dateComponents([.day], from: date) + let day = components.day! + + let formatter = NumberFormatter() + formatter.numberStyle = .ordinal + return formatter.string(from: NSNumber(integerLiteral: day)) ?? "" + } } extension Date: RawRepresentable { diff --git a/Shared/views/ContentView.swift b/Shared/views/ContentView.swift index 9000f22..f4dcec5 100644 --- a/Shared/views/ContentView.swift +++ b/Shared/views/ContentView.swift @@ -16,23 +16,34 @@ struct ContentViewConstants { struct ContentView: View { @Environment(\.managedObjectContext) private var viewContext + @AppStorage(UserDefaultsStore.Keys.needsOnboarding.rawValue, store: GroupUserDefaults.groupDefaults) private var needsOnboarding = true + @AppStorage(UserDefaultsStore.Keys.deleteEnable.rawValue, store: GroupUserDefaults.groupDefaults) private var deleteEnabled = true - @AppStorage(UserDefaultsStore.Keys.mainViewTopHeaderIndex.rawValue, store: GroupUserDefaults.groupDefaults) private var mainViewTopHeaderIndex = 0 + @AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system + + // top header storage @AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var currentSelectedHeaderViewBackDays: Int = 30 @AppStorage(UserDefaultsStore.Keys.contentViewHeaderTagViewOneViewType.rawValue, store: GroupUserDefaults.groupDefaults) private var firstSwichableHeaderViewType: MainSwitchableViewType = .total @AppStorage(UserDefaultsStore.Keys.contentViewHeaderTagViewTwoViewType.rawValue, store: GroupUserDefaults.groupDefaults) private var secondSwichableHeaderViewType: MainSwitchableViewType = .total @AppStorage(UserDefaultsStore.Keys.contentViewHeaderTag.rawValue, store: GroupUserDefaults.groupDefaults) private var switchableViewSelectedIndex = 1 - + // + + // edit row @State private var showingSheet = false - @State private var showTodayInput = true @State private var selectedEntry: MoodEntry? + // + + @State private var showTodayInput = true @State private var showUpdateEntryAlert = false + + // header properties @State private var headerHeight: CGFloat = ContentViewConstants.maxHeaderHeight @State private var headerViewType: MainSwitchableViewType = .total @State private var currentSelectedHeaderViewViewType: MainSwitchableViewType = .total @State private var headerOpacity: Double = 1.0 + // let minHeaderHeight = ContentViewConstants.minHeaderHeight let maxHeaderHeight = ContentViewConstants.maxHeaderHeight @@ -97,67 +108,7 @@ struct ContentView: View { } } - private func updateTitleHeader(forEntry entry: MoodEntry?) -> String { - guard let entry = entry else { - return "" - } - - let components = Calendar.current.dateComponents([.day, .month, .year], from: entry.forDate!) -// let day = components.day! - let month = components.month! - let year = components.year! - - let monthName = monthName(fromMonthInt: month) - let weekday = weekdayName(fromDate:entry.forDate!) - let dayz = dayFormat(fromDate:entry.forDate!) - - let string = weekday + " " + monthName + " " + dayz + " " + String(year) - - return String(format: String(localized: "content_view_fill_in_missing_entry"), string) - } - - private var settingsButtonView: some View { - HStack { - Spacer() - Button(action: { - showingSheet.toggle() - }, label: { - Image(systemName: "gear") - .foregroundColor(Color(UIColor.darkGray)) - .font(.system(size: 20)) - }).sheet(isPresented: $showingSheet) { - SettingsView(editedDataClosure: { - withAnimation{ - viewModel.updateData() - } - }, updateBoardingDataClosure: { onboardingData in - viewModel.updateOnboardingData(onboardingData: onboardingData) - }) - }.padding(.trailing) - } - } - - private func weekdayName(fromDate date: Date) -> String { - let weekday = Calendar.current.component(.weekday, from: date) - let calendar = Calendar.current - let dayIndex = ((weekday - 1) + (calendar.firstWeekday - 1)) % 7 - return calendar.weekdaySymbols[dayIndex] - } - - private func monthName(fromMonthInt: Int) -> String { - let monthName = DateFormatter().monthSymbols[fromMonthInt-1] - return monthName - } - - private func dayFormat(fromDate date: Date) -> String { - let components = Calendar.current.dateComponents([.day], from: date) - let day = components.day! - - let formatter = NumberFormatter() - formatter.numberStyle = .ordinal - return formatter.string(from: NSNumber(integerLiteral: day)) ?? "" - } - + // MARK: functions that do view type work func calcuateViewAlpha() { let perc = (((Double(headerHeight) - minHeaderHeight) * 100) / (maxHeaderHeight - minHeaderHeight)) / 100 headerOpacity = perc @@ -186,98 +137,44 @@ struct ContentView: View { headerHeight = newValue } - 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) - } - ) + private func updateTitleHeader(forEntry entry: MoodEntry?) -> String { + guard let entry = entry else { + return "" } - .background( - Color(theme.currentTheme.secondaryBGColor) - ) - .coordinateSpace(name: "scroll") - .onPreferenceChange(ViewOffsetKey.self) { value in - if viewModel.numberOfItems > 10 { - calculateHeight(minHeight: ContentViewConstants.minHeaderHeight, - maxHeight: ContentViewConstants.maxHeaderHeight, - yOffset: value) - } - } - .cornerRadius(10, corners: [.topLeft, .topRight]) + + let components = Calendar.current.dateComponents([.day, .month, .year], from: entry.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) } - private func SectionHeaderView(month: Int, year: Int) -> some View { - Text("\(monthName(fromMonthInt: month)) \(String(year))") - .font(.title) - .foregroundColor(Color(UIColor.label)) - .frame(maxWidth: .infinity, alignment: .leading) - .padding() - .background( - Color(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 - entryListView(entry: entry) - .contentShape(Rectangle()) - .onTapGesture(perform: { - selectedEntry = entry - showUpdateEntryAlert = true - }) - } - } - } - - private func entryListView(entry: MoodEntry) -> some View { + // MARK: Views + private var settingsButtonView: some View { HStack { - entry.mood.icon - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: 40, height: 40, alignment: .center) - .foregroundColor(entry.mood.color) - .padding(.leading, 5) - - VStack { - HStack { - Text(weekdayName(fromDate:entry.forDate!)) - .font(.title3) - .foregroundColor(Color(UIColor.label)) - Text(" - ") - .padding([.leading, .trailing], -10) - Text(dayFormat(fromDate:entry.forDate!)) - .font(.title3) - .foregroundColor(Color(UIColor.label)) - Spacer() - } - .multilineTextAlignment(.leading) - - Text(entry.moodValue == Mood.missing.rawValue ? String(localized: "mood_value_missing_tap_to_add") : "\(entry.moodString)") - .font(.body) - .foregroundColor(Color(UIColor.systemGray)) - .frame(maxWidth: .infinity, alignment: .leading) - } + Spacer() + Button(action: { + showingSheet.toggle() + }, label: { + Image(systemName: "gear") + .foregroundColor(Color(UIColor.darkGray)) + .font(.system(size: 20)) + }).sheet(isPresented: $showingSheet) { + SettingsView(editedDataClosure: { + withAnimation{ + viewModel.updateData() + } + }, updateBoardingDataClosure: { onboardingData in + viewModel.updateOnboardingData(onboardingData: onboardingData) + }) + }.padding(.trailing) } } @@ -333,30 +230,41 @@ struct ContentView: View { } } - private var emptyView: some View { - ZStack { - Color(theme.currentTheme.secondaryBGColor) - - VStack { - Text(String(localized: "content_view_empty_title")) - .font(.title) - .foregroundColor(Color(UIColor.label)) - .padding() - - Text(String(localized: "content_view_empty_title")) - .font(.body) - .foregroundColor(Color(UIColor.label)) - .padding() - AddMoodHeaderView(addItemHeaderClosure: { (mood, date) in - withAnimation { - 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) + } } - }, overrideDay: viewModel.shouldShowVotingHeader() ? .Today : .Previous) + } + }.background( + GeometryReader { proxy in + let offset = proxy.frame(in: .named("scroll")).minY + Color.clear.preference(key: ViewOffsetKey.self, value: offset) + } + ) + } + .background( + Color(theme.currentTheme.secondaryBGColor) + ) + .coordinateSpace(name: "scroll") + .onPreferenceChange(ViewOffsetKey.self) { value in + if viewModel.numberOfItems > 10 { + calculateHeight(minHeight: ContentViewConstants.minHeaderHeight, + maxHeight: ContentViewConstants.maxHeaderHeight, + yOffset: value) } } - .fixedSize(horizontal: false, vertical: true) - .cornerRadius(10, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) - .padding() + .cornerRadius(10, corners: [.topLeft, .topRight]) } private var mainView: some View { @@ -364,7 +272,7 @@ struct ContentView: View { settingsButtonView if viewModel.hasNoData { Spacer() - emptyView + EmptyView(viewModel: viewModel) Spacer() } else { ZStack { @@ -407,6 +315,67 @@ struct ContentView: View { } } +// view that make up the list body +extension ContentView { + private func SectionHeaderView(month: Int, year: Int) -> some View { + Text("\(Random.monthName(fromMonthInt: month)) \(String(year))") + .font(.title) + .foregroundColor(Color(UIColor.label)) + .frame(maxWidth: .infinity, alignment: .leading) + .padding() + .background( + Color(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 + entryListView(entry: entry) + .contentShape(Rectangle()) + .onTapGesture(perform: { + selectedEntry = entry + showUpdateEntryAlert = true + }) + } + } + } + + private func entryListView(entry: MoodEntry) -> some View { + HStack { + entry.mood.icon + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 40, height: 40, alignment: .center) + .foregroundColor(entry.mood.color) + .padding(.leading, 5) + + VStack { + HStack { + Text(Random.weekdayName(fromDate:entry.forDate!)) + .font(.title3) + .foregroundColor(Color(UIColor.label)) + Text(" - ") + .padding([.leading, .trailing], -10) + Text(Random.dayFormat(fromDate:entry.forDate!)) + .font(.title3) + .foregroundColor(Color(UIColor.label)) + Spacer() + } + .multilineTextAlignment(.leading) + + Text(entry.moodValue == Mood.missing.rawValue ? String(localized: "mood_value_missing_tap_to_add") : "\(entry.moodString)") + .font(.body) + .foregroundColor(Color(UIColor.systemGray)) + .frame(maxWidth: .infinity, alignment: .leading) + } + } + } +} + struct ViewOffsetKey: PreferenceKey { typealias Value = CGFloat static var defaultValue = CGFloat.zero diff --git a/Shared/views/EmptyView.swift b/Shared/views/EmptyView.swift new file mode 100644 index 0000000..afe8b64 --- /dev/null +++ b/Shared/views/EmptyView.swift @@ -0,0 +1,45 @@ +// +// EmptyView.swift +// Feels (iOS) +// +// Created by Trey Tartt on 2/10/22. +// + +import SwiftUI + +struct EmptyView: View { + @AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system + + let viewModel: ContentModeViewModel + + var body: some View { + ZStack { + Color(theme.currentTheme.secondaryBGColor) + + VStack { + Text(String(localized: "content_view_empty_title")) + .font(.title) + .foregroundColor(Color(UIColor.label)) + .padding() + + Text(String(localized: "content_view_empty_title")) + .font(.body) + .foregroundColor(Color(UIColor.label)) + .padding() + AddMoodHeaderView(addItemHeaderClosure: { (mood, date) in + withAnimation { + viewModel.add(mood: mood, forDate: date, entryType: .header) + } + }, overrideDay: viewModel.shouldShowVotingHeader() ? .Today : .Previous) + } + } + .fixedSize(horizontal: false, vertical: true) + .cornerRadius(10, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) + } +} + +struct EmptyView_Previews: PreviewProvider { + static var previews: some View { + EmptyView(viewModel: ContentModeViewModel()) + } +}