From 0035f6120451b170baba9ccc311557a0bf7aa1d7 Mon Sep 17 00:00:00 2001 From: Trey t Date: Sun, 20 Feb 2022 14:33:58 -0600 Subject: [PATCH] everything changed --- Feels.xcodeproj/project.pbxproj | 164 ++++++++--- FeelsWidget/FeelsWidget.swift | 117 +++++--- Shared/AppDelegate.swift | 9 + Shared/Date+Extensions.swift | 87 ++++++ Shared/LocalNotification.swift | 10 +- Shared/Models/CenterTiledImage.swift | 37 --- Shared/Models/Mood.swift | 37 +-- Shared/Models/MoodImagable.swift | 104 +++++++ Shared/Models/MoodTintable.swift | 266 ++++++++++++++++++ Shared/Models/OnboardingDataDataManager.swift | 5 +- Shared/Models/PersonalityPackable.swift | 91 ++++++ Shared/Models/Theme.swift | 101 ++++++- Shared/Models/UserDefaultsStore.swift | 41 ++- Shared/MoodEntryFunctions.swift | 66 +++++ Shared/Persisence/PersistenceADD.swift | 3 +- Shared/Persisence/PersistenceGET.swift | 9 +- Shared/Persisence/PersistenceHelper.swift | 6 +- Shared/Protocols/ChartDataBuildable.swift | 4 +- Shared/Random.swift | 109 ++++--- Shared/ShowBasedOnVoteLogics.swift | 37 +-- Shared/views/AddMoodHeaderView.swift | 16 +- Shared/views/BGView.swift | 29 +- .../CreateWidgetView.swift} | 96 +++---- Shared/views/CustomIcon/IconView.swift | 124 ++++++++ Shared/views/CustomIcon/IconViewModel.swift | 80 ++++++ .../CustomWidget/CustomWidgetModel.swift} | 64 ++--- .../CustomWidgetView.swift} | 85 +++--- .../views/CustomizeView/CustomizeView.swift | 239 ++++++++++++++++ Shared/{Models => views}/DayChartView.swift | 0 Shared/views/EmptyView.swift | 10 +- .../views/{ => FilterView}/FilterView.swift | 28 +- .../FilterView}/FilterViewModel.swift | 0 Shared/views/HeaderPercView.swift | 41 ++- Shared/views/HeaderStatsView.swift | 8 +- Shared/views/{ => HomeView}/HomeView.swift | 41 ++- .../HomeView/HomeViewTwo/HomeViewTwo.swift | 170 +++++++++++ .../HomeViewTwo/MonthDetailView.swift | 171 +++++++++++ .../HomeView/HomeViewViewModel.swift} | 30 +- Shared/views/HomeViewTwo/HomeViewTwo.swift | 110 -------- .../views/HomeViewTwo/MonthDetailView.swift | 122 -------- Shared/views/MainTabView.swift | 9 +- .../{ => SettingsView}/SettingsView.swift | 117 +------- .../views/{ => Sharing}/SharingListView.swift | 14 +- .../AllMoodsTotalTemplate.swift | 25 +- .../CurrentStreakTemplate.swift | 12 +- .../LongestStreakTemplate.swift | 18 +- .../SharingTemplates/MonthTotalTemplate.swift | 25 +- Shared/views/SmallRollUpHeaderView.swift | 33 ++- Shared/views/SwitchableView.swift | 7 +- en.lproj/Localizable.strings | 3 + 50 files changed, 2155 insertions(+), 875 deletions(-) create mode 100644 Shared/Date+Extensions.swift delete mode 100644 Shared/Models/CenterTiledImage.swift create mode 100644 Shared/Models/MoodImagable.swift create mode 100644 Shared/Models/MoodTintable.swift create mode 100644 Shared/Models/PersonalityPackable.swift create mode 100644 Shared/MoodEntryFunctions.swift rename Shared/views/{CreateIconView.swift => CustomIcon/CreateWidgetView.swift} (71%) create mode 100644 Shared/views/CustomIcon/IconView.swift create mode 100644 Shared/views/CustomIcon/IconViewModel.swift rename Shared/{Models/CustomIcon.swift => views/CustomWidget/CustomWidgetModel.swift} (54%) rename Shared/views/{IconView.swift => CustomWidget/CustomWidgetView.swift} (62%) create mode 100644 Shared/views/CustomizeView/CustomizeView.swift rename Shared/{Models => views}/DayChartView.swift (100%) rename Shared/views/{ => FilterView}/FilterView.swift (87%) rename Shared/{Models => views/FilterView}/FilterViewModel.swift (100%) rename Shared/views/{ => HomeView}/HomeView.swift (91%) create mode 100644 Shared/views/HomeView/HomeViewTwo/HomeViewTwo.swift create mode 100644 Shared/views/HomeView/HomeViewTwo/MonthDetailView.swift rename Shared/{Models/ContentModeViewModel.swift => views/HomeView/HomeViewViewModel.swift} (76%) delete mode 100644 Shared/views/HomeViewTwo/HomeViewTwo.swift delete mode 100644 Shared/views/HomeViewTwo/MonthDetailView.swift rename Shared/views/{ => SettingsView}/SettingsView.swift (62%) rename Shared/views/{ => Sharing}/SharingListView.swift (93%) diff --git a/Feels.xcodeproj/project.pbxproj b/Feels.xcodeproj/project.pbxproj index 79d0fba..dc08ca8 100644 --- a/Feels.xcodeproj/project.pbxproj +++ b/Feels.xcodeproj/project.pbxproj @@ -7,12 +7,27 @@ objects = { /* Begin PBXBuildFile section */ - 1C02589C27B9677A00EB91AC /* CreateIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C02589B27B9677A00EB91AC /* CreateIconView.swift */; }; - 1C02589E27B9821700EB91AC /* CenterTiledImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C02589D27B9821700EB91AC /* CenterTiledImage.swift */; }; + 1C02589C27B9677A00EB91AC /* CreateWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C02589B27B9677A00EB91AC /* CreateWidgetView.swift */; }; + 1C04488727C1C81D00D22444 /* PersonalityPackable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C04488627C1C81D00D22444 /* PersonalityPackable.swift */; }; + 1C04488827C1CD8C00D22444 /* PersonalityPackable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C04488627C1C81D00D22444 /* PersonalityPackable.swift */; }; + 1C04488A27C2ABD500D22444 /* IconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C04488927C2ABD500D22444 /* IconView.swift */; }; + 1C04488B27C2ABDE00D22444 /* IconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C04488927C2ABD500D22444 /* IconView.swift */; }; + 1C04488D27C2ADDB00D22444 /* IconViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C04488C27C2ADDB00D22444 /* IconViewModel.swift */; }; + 1C04488E27C2AE5200D22444 /* IconViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C04488C27C2ADDB00D22444 /* IconViewModel.swift */; }; + 1C04489627C2DB0100D22444 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C358FAC27ADD0C3002C83A6 /* Theme.swift */; }; + 1C04489727C2DB9B00D22444 /* BGView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CAD603127A5C1C800C520BD /* BGView.swift */; }; 1C0DAB45279DB0FB003B1F21 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 1C0DAB47279DB0FB003B1F21 /* Localizable.strings */; }; 1C10E24E27A1AB110047948B /* UserDefaultsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C5F4977279C945E0092F1B4 /* UserDefaultsStore.swift */; }; 1C10E25027A1AB220047948B /* OnboardingDay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CA03774279A294800D26164 /* OnboardingDay.swift */; }; 1C10E25127A1AB320047948B /* OnboardingTitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CA03776279A295600D26164 /* OnboardingTitle.swift */; }; + 1C2162EB27C14EFA004353D1 /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C2162EA27C14EFA004353D1 /* Date+Extensions.swift */; }; + 1C2162EC27C14FC5004353D1 /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C2162EA27C14EFA004353D1 /* Date+Extensions.swift */; }; + 1C2162EE27C15191004353D1 /* MoodEntryFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C2162ED27C15191004353D1 /* MoodEntryFunctions.swift */; }; + 1C2162F227C156E6004353D1 /* CustomizeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C2162F127C156E6004353D1 /* CustomizeView.swift */; }; + 1C2162F427C1602D004353D1 /* MoodImagable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C2162F327C1602D004353D1 /* MoodImagable.swift */; }; + 1C2162F527C16061004353D1 /* MoodImagable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C2162F327C1602D004353D1 /* MoodImagable.swift */; }; + 1C2162F727C16D11004353D1 /* MoodTintable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C2162F627C16D11004353D1 /* MoodTintable.swift */; }; + 1C2162F827C16E3C004353D1 /* MoodTintable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C2162F627C16D11004353D1 /* MoodTintable.swift */; }; 1C2618FA2795E41D00FDC148 /* Charts in Frameworks */ = {isa = PBXBuildFile; productRef = 1C2618F92795E41D00FDC148 /* Charts */; }; 1C2618FE27960A4F00FDC148 /* FilterViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C2618FD27960A4F00FDC148 /* FilterViewModel.swift */; }; 1C26190327960CE500FDC148 /* ChartDataBuildable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C26190227960CE500FDC148 /* ChartDataBuildable.swift */; }; @@ -52,7 +67,7 @@ 1C683FCC2792281400745862 /* Stats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C683FC92792281400745862 /* Stats.swift */; }; 1C744F2C278CE15600953A57 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C744F2B278CE15600953A57 /* AppDelegate.swift */; }; 1C747CC9279F06EB00762CBD /* CloudKitSyncMonitor in Frameworks */ = {isa = PBXBuildFile; productRef = 1C747CC8279F06EB00762CBD /* CloudKitSyncMonitor */; }; - 1CA037702799FFA600D26164 /* ContentModeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CA0376F2799FFA600D26164 /* ContentModeViewModel.swift */; }; + 1CA037702799FFA600D26164 /* HomeViewViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CA0376F2799FFA600D26164 /* HomeViewViewModel.swift */; }; 1CA03773279A293D00D26164 /* OnboardingTime.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CA03772279A293D00D26164 /* OnboardingTime.swift */; }; 1CA03775279A294800D26164 /* OnboardingDay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CA03774279A294800D26164 /* OnboardingDay.swift */; }; 1CA03777279A295600D26164 /* OnboardingTitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CA03776279A295600D26164 /* OnboardingTitle.swift */; }; @@ -104,10 +119,10 @@ 1CD90B6E278C7F8B001C4FEA /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CD90B6B278C7F78001C4FEA /* CloudKit.framework */; }; 1CD90B71278C80CA001C4FEA /* Feels.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90AEB278C7DDF001C4FEA /* Feels.xcdatamodeld */; }; 1CD90B76278C8119001C4FEA /* LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B75278C8119001C4FEA /* LocalNotification.swift */; }; - 1CEC966F27B9C29300CC8688 /* IconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CEC966E27B9C29300CC8688 /* IconView.swift */; }; - 1CEC967127B9C2BB00CC8688 /* CustomIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CEC967027B9C2BB00CC8688 /* CustomIcon.swift */; }; - 1CEC967227B9C9FB00CC8688 /* IconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CEC966E27B9C29300CC8688 /* IconView.swift */; }; - 1CEC967327B9CA0C00CC8688 /* CustomIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CEC967027B9C2BB00CC8688 /* CustomIcon.swift */; }; + 1CEC966F27B9C29300CC8688 /* CustomWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CEC966E27B9C29300CC8688 /* CustomWidgetView.swift */; }; + 1CEC967127B9C2BB00CC8688 /* CustomWidgetModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CEC967027B9C2BB00CC8688 /* CustomWidgetModel.swift */; }; + 1CEC967227B9C9FB00CC8688 /* CustomWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CEC966E27B9C29300CC8688 /* CustomWidgetView.swift */; }; + 1CEC967327B9CA0C00CC8688 /* CustomWidgetModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CEC967027B9C2BB00CC8688 /* CustomWidgetModel.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -149,10 +164,17 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 1C02589B27B9677A00EB91AC /* CreateIconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateIconView.swift; sourceTree = ""; }; - 1C02589D27B9821700EB91AC /* CenterTiledImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CenterTiledImage.swift; sourceTree = ""; }; + 1C02589B27B9677A00EB91AC /* CreateWidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = CreateWidgetView.swift; path = ../CustomIcon/CreateWidgetView.swift; sourceTree = ""; }; + 1C04488627C1C81D00D22444 /* PersonalityPackable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersonalityPackable.swift; sourceTree = ""; }; + 1C04488927C2ABD500D22444 /* IconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconView.swift; sourceTree = ""; }; + 1C04488C27C2ADDB00D22444 /* IconViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconViewModel.swift; sourceTree = ""; }; 1C0DAB46279DB0FB003B1F21 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 1C0DAB48279DB116003B1F21 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; + 1C2162EA27C14EFA004353D1 /* Date+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extensions.swift"; sourceTree = ""; }; + 1C2162ED27C15191004353D1 /* MoodEntryFunctions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoodEntryFunctions.swift; sourceTree = ""; }; + 1C2162F127C156E6004353D1 /* CustomizeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomizeView.swift; sourceTree = ""; }; + 1C2162F327C1602D004353D1 /* MoodImagable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoodImagable.swift; sourceTree = ""; }; + 1C2162F627C16D11004353D1 /* MoodTintable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoodTintable.swift; sourceTree = ""; }; 1C2618FD27960A4F00FDC148 /* FilterViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterViewModel.swift; sourceTree = ""; }; 1C26190227960CE500FDC148 /* ChartDataBuildable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartDataBuildable.swift; sourceTree = ""; }; 1C26190627960DC900FDC148 /* ChartViewItemBuildable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartViewItemBuildable.swift; sourceTree = ""; }; @@ -179,7 +201,7 @@ 1C658D7627C0744D003231EE /* PersistenceUPDATE.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersistenceUPDATE.swift; sourceTree = ""; }; 1C683FC92792281400745862 /* Stats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stats.swift; sourceTree = ""; }; 1C744F2B278CE15600953A57 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 1CA0376F2799FFA600D26164 /* ContentModeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentModeViewModel.swift; sourceTree = ""; }; + 1CA0376F2799FFA600D26164 /* HomeViewViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewViewModel.swift; sourceTree = ""; }; 1CA03772279A293D00D26164 /* OnboardingTime.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingTime.swift; sourceTree = ""; }; 1CA03774279A294800D26164 /* OnboardingDay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingDay.swift; sourceTree = ""; }; 1CA03776279A295600D26164 /* OnboardingTitle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingTitle.swift; sourceTree = ""; }; @@ -230,8 +252,8 @@ 1CD90B6F278C8000001C4FEA /* FeelsWidgetExtensionDev.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = FeelsWidgetExtensionDev.entitlements; sourceTree = ""; }; 1CD90B70278C8000001C4FEA /* Feels (iOS)Dev.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = "Feels (iOS)Dev.entitlements"; sourceTree = ""; }; 1CD90B75278C8119001C4FEA /* LocalNotification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalNotification.swift; sourceTree = ""; }; - 1CEC966E27B9C29300CC8688 /* IconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconView.swift; sourceTree = ""; }; - 1CEC967027B9C2BB00CC8688 /* CustomIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomIcon.swift; sourceTree = ""; }; + 1CEC966E27B9C29300CC8688 /* CustomWidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomWidgetView.swift; sourceTree = ""; }; + 1CEC967027B9C2BB00CC8688 /* CustomWidgetModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomWidgetModel.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -279,6 +301,68 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 1C04488F27C2CA9C00D22444 /* HomeView */ = { + isa = PBXGroup; + children = ( + 1CAD603227A5C1C800C520BD /* HomeView.swift */, + 1CA0376F2799FFA600D26164 /* HomeViewViewModel.swift */, + 1C361F1827C046E400E832FC /* HomeViewTwo */, + ); + path = HomeView; + sourceTree = ""; + }; + 1C04489027C2CAAC00D22444 /* CustomIcon */ = { + isa = PBXGroup; + children = ( + 1C04488927C2ABD500D22444 /* IconView.swift */, + 1C04488C27C2ADDB00D22444 /* IconViewModel.swift */, + ); + path = CustomIcon; + sourceTree = ""; + }; + 1C04489127C2CAB100D22444 /* CustomWidget */ = { + isa = PBXGroup; + children = ( + 1CEC966E27B9C29300CC8688 /* CustomWidgetView.swift */, + 1C02589B27B9677A00EB91AC /* CreateWidgetView.swift */, + 1CEC967027B9C2BB00CC8688 /* CustomWidgetModel.swift */, + ); + path = CustomWidget; + sourceTree = ""; + }; + 1C04489227C2CAB700D22444 /* CustomizeView */ = { + isa = PBXGroup; + children = ( + 1C2162F127C156E6004353D1 /* CustomizeView.swift */, + ); + path = CustomizeView; + sourceTree = ""; + }; + 1C04489327C2CABF00D22444 /* SettingsView */ = { + isa = PBXGroup; + children = ( + 1CAD602C27A5C1C800C520BD /* SettingsView.swift */, + ); + path = SettingsView; + sourceTree = ""; + }; + 1C04489427C2CAD100D22444 /* FilterView */ = { + isa = PBXGroup; + children = ( + 1C2618FD27960A4F00FDC148 /* FilterViewModel.swift */, + 1CAD602E27A5C1C800C520BD /* FilterView.swift */, + ); + path = FilterView; + sourceTree = ""; + }; + 1C04489527C2CB1A00D22444 /* Sharing */ = { + isa = PBXGroup; + children = ( + 1C358FB027B0AD87002C83A6 /* SharingListView.swift */, + ); + path = Sharing; + sourceTree = ""; + }; 1C26190127960CDA00FDC148 /* Protocols */ = { isa = PBXGroup; children = ( @@ -317,8 +401,8 @@ 1C4FF3C627BEE09E00BE8F34 /* PersistenceADD.swift */, 1C4FF3C227BEE07200BE8F34 /* PersistenceDELETE.swift */, 1C4FF3BF27BEE06900BE8F34 /* PersistenceGET.swift */, - 1C4FF3BD27BEDF9100BE8F34 /* PersistenceHelper.swift */, 1C658D7627C0744D003231EE /* PersistenceUPDATE.swift */, + 1C4FF3BD27BEDF9100BE8F34 /* PersistenceHelper.swift */, ); path = Persisence; sourceTree = ""; @@ -350,18 +434,19 @@ 1C358FB927B35252002C83A6 /* ActivityViewController.swift */, 1CAD602F27A5C1C800C520BD /* AddMoodHeaderView.swift */, 1CAD603127A5C1C800C520BD /* BGView.swift */, - 1C02589B27B9677A00EB91AC /* CreateIconView.swift */, + 1C04489027C2CAAC00D22444 /* CustomIcon */, + 1C04489227C2CAB700D22444 /* CustomizeView */, + 1C04489127C2CAB100D22444 /* CustomWidget */, + 1CC469AB27907D48003E0C6E /* DayChartView.swift */, 1CB101C427B62A2D00D1C033 /* EmptyView.swift */, - 1CAD602E27A5C1C800C520BD /* FilterView.swift */, + 1C04489427C2CAD100D22444 /* FilterView */, 1CAD602D27A5C1C800C520BD /* GraphView.swift */, 1CAD603027A5C1C800C520BD /* HeaderPercView.swift */, 1CAD603327A5C1C800C520BD /* HeaderStatsView.swift */, - 1CAD603227A5C1C800C520BD /* HomeView.swift */, - 1C361F1827C046E400E832FC /* HomeViewTwo */, - 1CEC966E27B9C29300CC8688 /* IconView.swift */, + 1C04488F27C2CA9C00D22444 /* HomeView */, 1C361F0B27C0356B00E832FC /* MainTabView.swift */, - 1CAD602C27A5C1C800C520BD /* SettingsView.swift */, - 1C358FB027B0AD87002C83A6 /* SharingListView.swift */, + 1C04489327C2CABF00D22444 /* SettingsView */, + 1C04489527C2CB1A00D22444 /* Sharing */, 1C358FB427B0ADF3002C83A6 /* SharingTemplates */, 1CAD602B27A5C1C800C520BD /* SmallRollUpHeaderView.swift */, 1CAD603D27A6ECCD00C520BD /* SwitchableView.swift */, @@ -397,6 +482,8 @@ 1CD90B75278C8119001C4FEA /* LocalNotification.swift */, 1C4FF3C527BEE07800BE8F34 /* Persisence */, 1CD90B5C278C7EAD001C4FEA /* Random.swift */, + 1C2162EA27C14EFA004353D1 /* Date+Extensions.swift */, + 1C2162ED27C15191004353D1 /* MoodEntryFunctions.swift */, 1C683FC92792281400745862 /* Stats.swift */, 1C4FF3BA27BEDDF000BE8F34 /* ShowBasedOnVoteLogics.swift */, 1CA03771279A291F00D26164 /* Onboarding */, @@ -471,15 +558,13 @@ 1CD90B60278C7EBA001C4FEA /* Models */ = { isa = PBXGroup; children = ( - 1C02589D27B9821700EB91AC /* CenterTiledImage.swift */, - 1CA0376F2799FFA600D26164 /* ContentModeViewModel.swift */, - 1CEC967027B9C2BB00CC8688 /* CustomIcon.swift */, - 1CC469AB27907D48003E0C6E /* DayChartView.swift */, - 1C2618FD27960A4F00FDC148 /* FilterViewModel.swift */, 1CD90B61278C7EBA001C4FEA /* Mood.swift */, 1CD90B62278C7EBA001C4FEA /* MoodEntryExtension.swift */, + 1C2162F327C1602D004353D1 /* MoodImagable.swift */, 1CB101C627B81CAC00D1C033 /* MoodMetrics.swift */, + 1C2162F627C16D11004353D1 /* MoodTintable.swift */, 1C361F1327C03C8600E832FC /* OnboardingDataDataManager.swift */, + 1C04488627C1C81D00D22444 /* PersonalityPackable.swift */, 1C358FAC27ADD0C3002C83A6 /* Theme.swift */, 1C5F4977279C945E0092F1B4 /* UserDefaultsStore.swift */, ); @@ -685,7 +770,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 1CA037702799FFA600D26164 /* ContentModeViewModel.swift in Sources */, + 1CA037702799FFA600D26164 /* HomeViewViewModel.swift in Sources */, 1CA03773279A293D00D26164 /* OnboardingTime.swift in Sources */, 1CAD603927A5C1C800C520BD /* HeaderPercView.swift in Sources */, 1CAD603C27A5C1C800C520BD /* HeaderStatsView.swift in Sources */, @@ -694,11 +779,14 @@ 1C4FF3C327BEE07200BE8F34 /* PersistenceDELETE.swift in Sources */, 1CA03775279A294800D26164 /* OnboardingDay.swift in Sources */, 1CAD603727A5C1C800C520BD /* FilterView.swift in Sources */, + 1C04488D27C2ADDB00D22444 /* IconViewModel.swift in Sources */, + 1C2162F227C156E6004353D1 /* CustomizeView.swift in Sources */, 1C683FCA2792281400745862 /* Stats.swift in Sources */, 1CAD603E27A6ECCD00C520BD /* SwitchableView.swift in Sources */, 1CD90B76278C8119001C4FEA /* LocalNotification.swift in Sources */, 1C358FB627B0AE15002C83A6 /* AllMoodsTotalTemplate.swift in Sources */, 1CD90B16278C7DE0001C4FEA /* Feels.xcdatamodeld in Sources */, + 1C04488727C1C81D00D22444 /* PersonalityPackable.swift in Sources */, 1C4FF3BE27BEDF9100BE8F34 /* PersistenceHelper.swift in Sources */, 1CC469AA278F30A0003E0C6E /* BGTask.swift in Sources */, 1CAD603B27A5C1C800C520BD /* HomeView.swift in Sources */, @@ -707,7 +795,6 @@ 1CAD603A27A5C1C800C520BD /* BGView.swift in Sources */, 1C26190727960DC900FDC148 /* ChartViewItemBuildable.swift in Sources */, 1CD90B5D278C7EAD001C4FEA /* Random.swift in Sources */, - 1C02589E27B9821700EB91AC /* CenterTiledImage.swift in Sources */, 1C2618FE27960A4F00FDC148 /* FilterViewModel.swift in Sources */, 1C744F2C278CE15600953A57 /* AppDelegate.swift in Sources */, 1CD90B63278C7EBA001C4FEA /* Mood.swift in Sources */, @@ -720,7 +807,9 @@ 1C26190327960CE500FDC148 /* ChartDataBuildable.swift in Sources */, 1CB101C527B62A2D00D1C033 /* EmptyView.swift in Sources */, 1CB101C727B81CAC00D1C033 /* MoodMetrics.swift in Sources */, + 1C2162F427C1602D004353D1 /* MoodImagable.swift in Sources */, 1CAD603627A5C1C800C520BD /* GraphView.swift in Sources */, + 1C2162F727C16D11004353D1 /* MoodTintable.swift in Sources */, 1C361F1727C046D800E832FC /* MonthDetailView.swift in Sources */, 1CD90B66278C7EBA001C4FEA /* MoodEntryExtension.swift in Sources */, 1C658D7727C0744D003231EE /* PersistenceUPDATE.swift in Sources */, @@ -728,7 +817,7 @@ 1C358FB827B0AEE3002C83A6 /* LongestStreakTemplate.swift in Sources */, 1C358FB127B0AD87002C83A6 /* SharingListView.swift in Sources */, 1CD90B1C278C7DE0001C4FEA /* Persistence.swift in Sources */, - 1CEC966F27B9C29300CC8688 /* IconView.swift in Sources */, + 1CEC966F27B9C29300CC8688 /* CustomWidgetView.swift in Sources */, 1CA0377A279A296E00D26164 /* OnboardingMain.swift in Sources */, 1C358FBA27B35252002C83A6 /* ActivityViewController.swift in Sources */, 1C5F4978279C945E0092F1B4 /* UserDefaultsStore.swift in Sources */, @@ -736,12 +825,15 @@ 1C358FC027B4D20C002C83A6 /* MonthTotalTemplate.swift in Sources */, 1CA03777279A295600D26164 /* OnboardingTitle.swift in Sources */, 1C4FF3C027BEE06900BE8F34 /* PersistenceGET.swift in Sources */, + 1C04488A27C2ABD500D22444 /* IconView.swift in Sources */, 1C361F0C27C0356B00E832FC /* MainTabView.swift in Sources */, - 1CEC967127B9C2BB00CC8688 /* CustomIcon.swift in Sources */, + 1CEC967127B9C2BB00CC8688 /* CustomWidgetModel.swift in Sources */, + 1C2162EE27C15191004353D1 /* MoodEntryFunctions.swift in Sources */, 1C361F0A27C0356000E832FC /* HomeViewTwo.swift in Sources */, 1C361F1427C03C8600E832FC /* OnboardingDataDataManager.swift in Sources */, 1C358FAD27ADD0C3002C83A6 /* Theme.swift in Sources */, - 1C02589C27B9677A00EB91AC /* CreateIconView.swift in Sources */, + 1C2162EB27C14EFA004353D1 /* Date+Extensions.swift in Sources */, + 1C02589C27B9677A00EB91AC /* CreateWidgetView.swift in Sources */, 1C358FC227B4D227002C83A6 /* WeekTotalTemplate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -781,8 +873,10 @@ buildActionMask = 2147483647; files = ( 1CD90B65278C7EBA001C4FEA /* Mood.swift in Sources */, + 1C04488B27C2ABDE00D22444 /* IconView.swift in Sources */, 1C361F1127C03C3D00E832FC /* OnboardingTime.swift in Sources */, - 1CEC967227B9C9FB00CC8688 /* IconView.swift in Sources */, + 1CEC967227B9C9FB00CC8688 /* CustomWidgetView.swift in Sources */, + 1C2162F827C16E3C004353D1 /* MoodTintable.swift in Sources */, 1C4FF3BC27BEDF6600BE8F34 /* ShowBasedOnVoteLogics.swift in Sources */, 1C4FF3C927BEE0C300BE8F34 /* PersistenceHelper.swift in Sources */, 1CA2662D2793908700C0E12C /* Persistence.swift in Sources */, @@ -791,16 +885,22 @@ 1CD90B71278C80CA001C4FEA /* Feels.xcdatamodeld in Sources */, 1C658D7827C0744D003231EE /* PersistenceUPDATE.swift in Sources */, 1C10E25127A1AB320047948B /* OnboardingTitle.swift in Sources */, + 1C04488E27C2AE5200D22444 /* IconViewModel.swift in Sources */, + 1C04489727C2DB9B00D22444 /* BGView.swift in Sources */, 1CB101C827B81CAC00D1C033 /* MoodMetrics.swift in Sources */, 1C683FCB2792281400745862 /* Stats.swift in Sources */, - 1CEC967327B9CA0C00CC8688 /* CustomIcon.swift in Sources */, + 1CEC967327B9CA0C00CC8688 /* CustomWidgetModel.swift in Sources */, 1C10E25027A1AB220047948B /* OnboardingDay.swift in Sources */, + 1C04488827C1CD8C00D22444 /* PersonalityPackable.swift in Sources */, 1C4FF3C427BEE07200BE8F34 /* PersistenceDELETE.swift in Sources */, 1C4FF3C827BEE09E00BE8F34 /* PersistenceADD.swift in Sources */, + 1C2162F527C16061004353D1 /* MoodImagable.swift in Sources */, + 1C2162EC27C14FC5004353D1 /* Date+Extensions.swift in Sources */, 1C4FF3C127BEE06900BE8F34 /* PersistenceGET.swift in Sources */, 1C361F0D27C03BDF00E832FC /* OnboardingData.swift in Sources */, 1CD90B52278C7E7A001C4FEA /* FeelsWidget.intentdefinition in Sources */, 1CD90B4D278C7E7A001C4FEA /* FeelsWidget.swift in Sources */, + 1C04489627C2DB0100D22444 /* Theme.swift in Sources */, 1C361F0F27C03C0E00E832FC /* LocalNotification.swift in Sources */, 1C10E24E27A1AB110047948B /* UserDefaultsStore.swift in Sources */, ); diff --git a/FeelsWidget/FeelsWidget.swift b/FeelsWidget/FeelsWidget.swift index dc95f68..64e4011 100644 --- a/FeelsWidget/FeelsWidget.swift +++ b/FeelsWidget/FeelsWidget.swift @@ -16,12 +16,14 @@ class WatchTimelineView: Identifiable { let graphic: Image let date: Date let color: Color + let secondaryColor: Color - init(image: Image, date: Date, color: Color, graphic: Image) { + init(image: Image, graphic: Image, date: Date, color: Color, secondaryColor: Color) { self.image = image self.date = date self.color = color self.graphic = graphic + self.secondaryColor = secondaryColor } } @@ -39,17 +41,21 @@ struct TimeLineCreator { let dayStart = Calendar.current.startOfDay(for: day) let dayEnd = Calendar.current.date(bySettingHour: 23, minute: 59, second: 59, of: dayStart)! + let moodTint: MoodTintable.Type = UserDefaultsStore.moodTintable() + let moodImages: MoodImagable.Type = UserDefaultsStore.moodMoodImagable() if let todayEntry = PersistenceController.shared.getData(startDate: dayStart, endDate: dayEnd, includedDays: []).first { - timeLineView.append(WatchTimelineView(image: todayEntry.mood.icon, - date: dayStart, - color: todayEntry.mood.color, - graphic: todayEntry.mood.graphic)) + timeLineView.append(WatchTimelineView(image: moodImages.icon(forMood: todayEntry.mood), + graphic: moodImages.icon(forMood: todayEntry.mood), + date: dayStart, + color: moodTint.color(forMood: todayEntry.mood), + secondaryColor: moodTint.secondary(forMood: todayEntry.mood))) } else { - timeLineView.append(WatchTimelineView(image: Mood.missing.icon, - date: dayStart, - color: Mood.missing.color, - graphic: Mood.missing.graphic)) + timeLineView.append(WatchTimelineView(image: moodImages.icon(forMood: .missing), + graphic: moodImages.icon(forMood: .missing), + date: Date(), + color: moodTint.color(forMood: .missing), + secondaryColor: moodTint.secondary(forMood: .missing))) } } timeLineView = timeLineView.sorted(by: { $0.date > $1.date }) @@ -72,7 +78,7 @@ struct Provider: IntentTimelineProvider { func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) { let entry = SimpleEntry(date: Date(), - configuration: ConfigurationIntent(), + configuration: ConfigurationIntent(), timeLineViews: Array(TimeLineCreator.createViews(daysBack: 11).prefix(10))) completion(entry) } @@ -83,8 +89,8 @@ struct Provider: IntentTimelineProvider { timeLineViews: nil) let midNightEntry = SimpleEntry(date: Calendar.current.date(bySettingHour: 23, minute: 59, second: 59, of: Date())!, - configuration: ConfigurationIntent(), - timeLineViews: nil) + configuration: ConfigurationIntent(), + timeLineViews: nil) let date = Calendar.current.date(byAdding: .second, value: 10, to: Date())! let timeline = Timeline(entries: [entry, midNightEntry], policy: .after(date)) @@ -164,7 +170,7 @@ struct SmallWidgetView: View { struct MediumWidgetView: View { var entry: Provider.Entry var timeLineView = [WatchTimelineView]() - + init(entry: Provider.Entry) { self.entry = entry timeLineView = Array(TimeLineCreator.createViews(daysBack: 6).prefix(5)) @@ -191,7 +197,7 @@ struct MediumWidgetView: View { struct LargeWidgetView: View { var entry: Provider.Entry var timeLineView = [WatchTimelineView]() - + init(entry: Provider.Entry) { self.entry = entry timeLineView = Array(TimeLineCreator.createViews(daysBack: 11).prefix(10)) @@ -241,26 +247,31 @@ struct FeelsGraphicWidgetEntryView : View { var body: some View { SmallGraphicWidgetView(entry: entry) .onReceive(NotificationCenter.default.publisher(for: .NSPersistentStoreRemoteChange)) { _ in - // make sure you don't call this too often - WidgetCenter.shared.reloadAllTimelines() - } + // make sure you don't call this too often + WidgetCenter.shared.reloadAllTimelines() + } } } struct SmallGraphicWidgetView: View { var entry: Provider.Entry - var timeLineView = [WatchTimelineView]() + var timeLineView: [WatchTimelineView] init(entry: Provider.Entry) { self.entry = entry - timeLineView = [TimeLineCreator.createViews(daysBack: 2).first!] + timeLineView = TimeLineCreator.createViews(daysBack: 2) } var body: some View { - GeometryReader { geo in - timeLineView.first!.graphic - .resizable() - .scaledToFit() + if let first = timeLineView.first { + IconView(iconViewModel: IconViewModel(backgroundImage: first.graphic, + bgColor: first.color, + bgOverlayColor: first.secondaryColor, + centerImage: first.graphic), + isPreview: true) + } else { + IconView(iconViewModel: IconViewModel.great, + isPreview: false) } } } @@ -407,23 +418,49 @@ struct FeelsGraphicWidget: Widget { struct FeelsWidget_Previews: PreviewProvider { static var previews: some View { Group { -// FeelsWidgetEntryView(entry: SimpleEntry(date: Date(), -// configuration: ConfigurationIntent(), -// timeLineViews: FeelsWidget_Previews.data)) -// .previewContext(WidgetPreviewContext(family: .systemSmall)) -// .environment(\.sizeCategory, .small) -// -// FeelsWidgetEntryView(entry: SimpleEntry(date: Date(), -// configuration: ConfigurationIntent(), -// timeLineViews: FeelsWidget_Previews.data)) -// .previewContext(WidgetPreviewContext(family: .systemMedium)) -// .environment(\.sizeCategory, .medium) -// -// FeelsWidgetEntryView(entry: SimpleEntry(date: Date(), -// configuration: ConfigurationIntent(), -// timeLineViews: FeelsWidget_Previews.data)) -// .previewContext(WidgetPreviewContext(family: .systemLarge)) -// .environment(\.sizeCategory, .large) + FeelsGraphicWidgetEntryView(entry: SimpleEntry(date: Date(), + configuration: ConfigurationIntent(), + timeLineViews: [WatchTimelineView(image: HandEmojiMoodImages.icon(forMood: .great), + graphic: HandEmojiMoodImages.icon(forMood: .great), + date: Date(), + color: MoodTints.Neon.color(forMood: .great), + + secondaryColor: .white), + WatchTimelineView(image: HandEmojiMoodImages.icon(forMood: .great), + graphic: HandEmojiMoodImages.icon(forMood: .great), + date: Date(), + color: MoodTints.Neon.color(forMood: .great), + + secondaryColor: .white)])) + .previewContext(WidgetPreviewContext(family: .systemSmall)) + + FeelsGraphicWidgetEntryView(entry: SimpleEntry(date: Date(), + configuration: ConfigurationIntent(), + timeLineViews: [WatchTimelineView(image: HandEmojiMoodImages.icon(forMood: .horrible), + graphic: HandEmojiMoodImages.icon(forMood: .horrible), + date: Date(), + color: MoodTints.Neon.color(forMood: .horrible), + + secondaryColor: .white), + WatchTimelineView(image: HandEmojiMoodImages.icon(forMood: .horrible), + graphic: HandEmojiMoodImages.icon(forMood: .horrible), + date: Date(), + color: MoodTints.Neon.color(forMood: .horrible), + + secondaryColor: .white)])) + .previewContext(WidgetPreviewContext(family: .systemSmall)) + + // FeelsWidgetEntryView(entry: SimpleEntry(date: Date(), + // configuration: ConfigurationIntent(), + // timeLineViews: FeelsWidget_Previews.data)) + // .previewContext(WidgetPreviewContext(family: .systemMedium)) + // .environment(\.sizeCategory, .medium) + // + // FeelsWidgetEntryView(entry: SimpleEntry(date: Date(), + // configuration: ConfigurationIntent(), + // timeLineViews: FeelsWidget_Previews.data)) + // .previewContext(WidgetPreviewContext(family: .systemLarge)) + // .environment(\.sizeCategory, .large) } } } diff --git a/Shared/AppDelegate.swift b/Shared/AppDelegate.swift index f9fa15d..096ebb9 100644 --- a/Shared/AppDelegate.swift +++ b/Shared/AppDelegate.swift @@ -18,11 +18,20 @@ class AppDelegate: NSObject, UIApplicationDelegate { // PersistenceController.shared.clearDB() PersistenceController.shared.fillInMissingDates() UNUserNotificationCenter.current().delegate = self + + let theme = UserDefaultsStore.theme() + UIPageControl.appearance().currentPageIndicatorTintColor = UIColor.label + UIPageControl.appearance().pageIndicatorTintColor = UIColor.systemGray + UITabBar.appearance().backgroundColor = UIColor(cgColor: theme.currentTheme.secondaryBGColor.cgColor ?? UIColor.secondarySystemBackground.cgColor) + return true } func applicationWillEnterForeground(_ application: UIApplication) { PersistenceController.shared.fillInMissingDates() + + // reschedule notifications so there's a new title next notification + LocalNotification.rescheduleNotifiations() } } diff --git a/Shared/Date+Extensions.swift b/Shared/Date+Extensions.swift new file mode 100644 index 0000000..984a24b --- /dev/null +++ b/Shared/Date+Extensions.swift @@ -0,0 +1,87 @@ +// +// Date+Extensions.swift +// Feels (iOS) +// +// Created by Trey Tartt on 2/19/22. +// + +import Foundation + +extension Date: RawRepresentable { + public var rawValue: String { + self.timeIntervalSinceReferenceDate.description + } + + public init?(rawValue: String) { + self = Date(timeIntervalSinceReferenceDate: Double(rawValue) ?? 0.0) + } + + var startOfDay: Date { + return Calendar.current.startOfDay(for: self) + } + + var startOfMonth: Date { + + let calendar = Calendar(identifier: .gregorian) + let components = calendar.dateComponents([.year, .month], from: self) + + return calendar.date(from: components)! + } + + var endOfDay: Date { + var components = DateComponents() + components.day = 1 + components.second = -1 + return Calendar.current.date(byAdding: components, to: startOfDay)! + } + + var endOfMonth: Date { + var components = DateComponents() + components.month = 1 + components.second = -1 + return Calendar(identifier: .gregorian).date(byAdding: components, to: startOfMonth)! + } + + func toLocalTime() -> Date { + let timezone = TimeZone.current + let seconds = TimeInterval(timezone.secondsFromGMT(for: self)) + return Date(timeInterval: seconds, since: self) + } + + var weekday: Int { + Calendar.current.component(.weekday, from: self) + } + + var firstDayOfTheMonth: Date { + Calendar.current.dateComponents([.calendar, .year,.month], from: self).date! + } + + static func dates(from fromDate: Date, to toDate: Date) -> [Date] { + var dates: [Date] = [] + var date = fromDate + + while date <= toDate { + dates.append(date) + guard let newDate = Calendar.current.date(byAdding: .day, value: 1, to: date) else { break } + date = newDate + } + return dates + } + + static func dateRange(monthInt: Int, yearInt: Int) -> (startDate: Date, endDate: Date) { + var dateComponents = DateComponents() + dateComponents.year = yearInt + dateComponents.month = monthInt + dateComponents.day = 3 + dateComponents.hour = 12 + var userCalendar = Calendar(identifier: .gregorian) + userCalendar.timeZone = TimeZone(secondsFromGMT: 0)! + let someDateTime = userCalendar.date(from: dateComponents)! + + let startDate = someDateTime.startOfMonth + var endDate = someDateTime.endOfMonth + endDate = Calendar.current.date(byAdding: .hour, value: -8, to: endDate)! + + return (startDate, endDate) + } +} diff --git a/Shared/LocalNotification.swift b/Shared/LocalNotification.swift index da7e152..86a6536 100644 --- a/Shared/LocalNotification.swift +++ b/Shared/LocalNotification.swift @@ -28,6 +28,14 @@ class LocalNotification { } } + public class func rescheduleNotifiations() { + if let data = GroupUserDefaults.groupDefaults.object(forKey: UserDefaultsStore.Keys.savedOnboardingData.rawValue) as? Data, + let model = try? JSONDecoder().decode(OnboardingData.self, from: data) { + LocalNotification.scheduleReminder(atTime: model.date, + withTitle: model.title) + } + } + public class func scheduleReminder(atTime time: Date, withTitle title: String) { self.removeNotificaiton() @@ -37,7 +45,7 @@ class LocalNotification { let _ = LocalNotification.createNotificationCategory() let notificationContent = UNMutableNotificationContent() - notificationContent.title = title + notificationContent.title = UserDefaultsStore.personalityPackable().randomPushNotificationTitle() notificationContent.badge = NSNumber(value: 1) notificationContent.sound = .default diff --git a/Shared/Models/CenterTiledImage.swift b/Shared/Models/CenterTiledImage.swift deleted file mode 100644 index d486d40..0000000 --- a/Shared/Models/CenterTiledImage.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// CenterTiledImage.swift -// Feels (iOS) -// -// Created by Trey Tartt on 2/13/22. -// - -import SwiftUI - -struct CenterTiledImage: View { - let imageName: String - let imageSize: CGSize - - var body: some View { - GeometryReader { geoReader in - let horizontalTilesNeeded = ceil(geoReader.size.width / imageSize.width / 2) * 2 + 1 - let verticalTilesNeeded = ceil(geoReader.size.height / imageSize.height / 2) * 2 + 1 - - Image(imageName) - .resizable(resizingMode: .tile) - .frame( - width: horizontalTilesNeeded * imageSize.width, - height: verticalTilesNeeded * imageSize.height - ) - .position(x: geoReader.size.width * 0.5, y: geoReader.size.height * 0.5) - } - } - - init?(imageName: String) { - guard let imageSize = UIImage(named: imageName)?.size else { - return nil - } - - self.imageName = imageName - self.imageSize = imageSize - } -} diff --git a/Shared/Models/Mood.swift b/Shared/Models/Mood.swift index 253b13b..7e90fc5 100644 --- a/Shared/Models/Mood.swift +++ b/Shared/Models/Mood.swift @@ -37,22 +37,8 @@ enum Mood: Int { } var color: Color { - switch self { - case .horrible: - return .red - case .bad: - return .orange - case .average: - return .blue - case .good: - return .yellow - case .great: - return .green - case .missing: - return Color(uiColor: UIColor.systemGray2) - case .placeholder: - return .clear - } + let moodTint: MoodTintable.Type = UserDefaultsStore.moodTintable() + return moodTint.color(forMood: self) } static var allValues: [Mood] { @@ -60,23 +46,8 @@ enum Mood: Int { } var icon: Image { - switch self { - - case .horrible: - return Image("horrible", bundle: .main) - case .bad: - return Image("bad", bundle: .main) - case .average: - return Image("average", bundle: .main) - case .good: - return Image("good", bundle: .main) - case .great: - return Image("great", bundle: .main) - case .missing: - return Image("missing", bundle: .main) - case .placeholder: - return Image("missing", bundle: .main) - } + let moodImages: MoodImagable.Type = UserDefaultsStore.moodMoodImagable() + return moodImages.icon(forMood: self) } var graphic: Image { diff --git a/Shared/Models/MoodImagable.swift b/Shared/Models/MoodImagable.swift new file mode 100644 index 0000000..97ecb90 --- /dev/null +++ b/Shared/Models/MoodImagable.swift @@ -0,0 +1,104 @@ +// +// MoodImagable.swift +// Feels (iOS) +// +// Created by Trey Tartt on 2/19/22. +// + +import SwiftUI + +protocol MoodImagable { + static func icon(forMood mood: Mood) -> Image +} + +enum MoodImages: Int, CaseIterable { + case FontAwesome + case Emoji + case HandEmjoi + + func icon(forMood mood: Mood) -> Image { + switch self { + + case .FontAwesome: + return FontAwesomeMoodImages.icon(forMood: mood) + case .Emoji: + return EmojiMoodImages.icon(forMood: mood) + case .HandEmjoi: + return HandEmojiMoodImages.icon(forMood: mood) + } + } + + var moodImages: MoodImagable.Type { + switch self { + case .FontAwesome: + return FontAwesomeMoodImages.self + case .Emoji: + return EmojiMoodImages.self + case .HandEmjoi: + return HandEmojiMoodImages.self + } + } +} + +final class FontAwesomeMoodImages: MoodImagable { + static func icon(forMood mood: Mood) -> Image { + switch mood { + case .horrible: + return Image("horrible", bundle: .main) + case .bad: + return Image("bad", bundle: .main) + case .average: + return Image("average", bundle: .main) + case .good: + return Image("good", bundle: .main) + case .great: + return Image("great", bundle: .main) + case .missing: + return Image("missing", bundle: .main) + case .placeholder: + return Image("missing", bundle: .main) + } + } +} + +final class EmojiMoodImages: MoodImagable { + static func icon(forMood mood: Mood) -> Image { + switch mood { + case .horrible: + return Image(uiImage: "💩".textToImage()!) + case .bad: + return Image(uiImage: "😕".textToImage()!) + case .average: + return Image(uiImage: "😑".textToImage()!) + case .good: + return Image(uiImage: "🙂".textToImage()!) + case .great: + return Image(uiImage: "😀".textToImage()!) + case .missing: + return Image(uiImage: "x".textToImage()!) + case .placeholder: + return Image(uiImage: "x".textToImage()!) + } + } +} + +final class HandEmojiMoodImages: MoodImagable { + static func icon(forMood mood: Mood) -> Image { + switch mood { + case .horrible: + return Image(uiImage: "🖕".textToImage()!) + case .bad: + return Image(uiImage: "👎".textToImage()!) + case .average: + return Image(uiImage: "🖖".textToImage()!) + case .good: + return Image(uiImage: "👍".textToImage()!) + case .great: + return Image(uiImage: "🙏".textToImage()!) + case .missing: + return Image(uiImage: "x".textToImage()!) + case .placeholder: + return Image(uiImage: "x".textToImage()!) + } + } +} diff --git a/Shared/Models/MoodTintable.swift b/Shared/Models/MoodTintable.swift new file mode 100644 index 0000000..a237c49 --- /dev/null +++ b/Shared/Models/MoodTintable.swift @@ -0,0 +1,266 @@ +// +// MoodTintable.swift +// Feels (iOS) +// +// Created by Trey Tartt on 2/19/22. +// + +import SwiftUI + +protocol MoodTintable { + static func color(forMood mood: Mood) -> Color + static func secondary(forMood mood: Mood) -> Color +} + +enum MoodTints: Int, CaseIterable { + case Default + case AllRed + case Neon + case MonoChrome + case Pastel + + func color(forMood mood: Mood) -> Color { + switch self { + case .Default: + return DefaultMoodTint.color(forMood: mood) + case .AllRed: + return AllRedMoodTint.color(forMood: mood) + case .Neon: + return NeonMoodTint.color(forMood: mood) + case .MonoChrome: + return MonoChromeTint.color(forMood: mood) + case .Pastel: + return PastelTint.color(forMood: mood) + } + } + + func secondary(forMood mood: Mood) -> Color { + switch self { + case .Default: + return DefaultMoodTint.secondary(forMood: mood) + case .AllRed: + return AllRedMoodTint.secondary(forMood: mood) + case .Neon: + return NeonMoodTint.secondary(forMood: mood) + case .MonoChrome: + return MonoChromeTint.secondary(forMood: mood) + case .Pastel: + return PastelTint.secondary(forMood: mood) + } + } + + var moodTints: MoodTintable.Type { + switch self { + case .Default: + return DefaultMoodTint.self + case .AllRed: + return AllRedMoodTint.self + case .Neon: + return NeonMoodTint.self + case .MonoChrome: + return MonoChromeTint.self + case .Pastel: + return PastelTint.self + } + } +} + +final class DefaultMoodTint: MoodTintable { + static func color(forMood mood: Mood) -> Color { + switch mood { + case .horrible: + return Color(hex: "ff453a") + case .bad: + return Color(hex: "ff9e0b") + case .average: + return Color(hex: "0b84ff") + case .good: + return Color(hex: "ffd709") + case .great: + return Color(hex: "31d158") + case .missing: + return Color(uiColor: UIColor.systemGray4) + case .placeholder: + return Color(uiColor: UIColor.systemGray4) + } + } + + static func secondary(forMood mood: Mood) -> Color { + switch mood { + case .horrible: + return Color(hex: "a92b26") + case .bad: + return Color(hex: "a06407") + case .average: + return Color(hex: "074f9a") + case .good: + return Color(hex: "9d8405") + case .great: + return Color(hex: "208939") + case .missing: + return Color(uiColor: UIColor.label) + case .placeholder: + return Color(uiColor: UIColor.label) + } + } +} + +final class AllRedMoodTint: MoodTintable { + static func color(forMood mood: Mood) -> Color { + switch mood { + case .horrible: + return .red + case .bad: + return .red + case .average: + return .red + case .good: + return .red + case .great: + return .red + case .missing: + return Color(uiColor: UIColor.systemGray2) + case .placeholder: + return Color(uiColor: UIColor.systemGray2) + } + } + + static func secondary(forMood mood: Mood) -> Color { + switch mood { + case .horrible: + return .red + case .bad: + return .red + case .average: + return .red + case .good: + return .red + case .great: + return .red + case .missing: + return Color(uiColor: UIColor.label) + case .placeholder: + return Color(uiColor: UIColor.label) + } + } +} + +final class NeonMoodTint: MoodTintable { + static func color(forMood mood: Mood) -> Color { + switch mood { + case .horrible: + return Color(hex: "#ff1818") + case .bad: + return Color(hex: "#FF5F1F") + case .average: + return Color(hex: "#1F51FF") + case .good: + return Color(hex: "#FFF01F") + case .great: + return Color(hex: "#39FF14") + case .missing: + return Color(uiColor: UIColor.systemGray2) + case .placeholder: + return Color(uiColor: UIColor.systemGray2) + } + } + + static func secondary(forMood mood: Mood) -> Color { + switch mood { + case .horrible: + return Color(hex: "#8b1113") + case .bad: + return Color(hex: "#893315") + case .average: + return Color(hex: "#0f2a85") + case .good: + return Color(hex: "#807a18") + case .great: + return Color(hex: "#218116") + case .missing: + return Color(uiColor: UIColor.label) + case .placeholder: + return Color(uiColor: UIColor.label) + } + } +} + +final class MonoChromeTint: MoodTintable { + static func color(forMood mood: Mood) -> Color { + switch mood { + case .horrible: + return .black + case .bad: + return Color(uiColor: UIColor.systemGray) + case .average: + return Color(uiColor: UIColor.systemGray) + case .good: + return Color(uiColor: UIColor.systemGray2) + case .great: + return Color(uiColor: UIColor.systemGray3) + case .missing: + return Color(uiColor: UIColor.systemGray2) + case .placeholder: + return Color(uiColor: UIColor.systemGray4) + } + } + + static func secondary(forMood mood: Mood) -> Color { + switch mood { + case .horrible: + return .black + case .bad: + return Color(uiColor: UIColor.systemGray) + case .average: + return Color(uiColor: UIColor.systemGray) + case .good: + return Color(uiColor: UIColor.systemGray2) + case .great: + return Color(uiColor: UIColor.systemGray3) + case .missing: + return Color(uiColor: UIColor.systemGray2) + case .placeholder: + return Color(uiColor: UIColor.systemGray4) + } + } +} + +final class PastelTint: MoodTintable { + static func color(forMood mood: Mood) -> Color { + switch mood { + case .horrible: + return Color(hex: "#FF6961") + case .bad: + return Color(hex: "#ffb347") + case .average: + return Color(hex: "#A7C7E7") + case .good: + return Color(hex: "#fdfd96") + case .great: + return Color(hex: "#C1E1C1") + case .missing: + return Color(uiColor: UIColor.systemGray2) + case .placeholder: + return Color(uiColor: UIColor.systemGray4) + } + } + + static func secondary(forMood mood: Mood) -> Color { + switch mood { + case .horrible: + return Color(hex: "#893734") + case .bad: + return Color(hex: "#855d28") + case .average: + return Color(hex: "#5d6e83") + case .good: + return Color(hex: "#7f804f") + case .great: + return Color(hex: "#6b7e6d") + case .missing: + return Color(uiColor: UIColor.systemGray2) + case .placeholder: + return Color(uiColor: UIColor.systemGray4) + } + } +} diff --git a/Shared/Models/OnboardingDataDataManager.swift b/Shared/Models/OnboardingDataDataManager.swift index b3ba180..e304da2 100644 --- a/Shared/Models/OnboardingDataDataManager.swift +++ b/Shared/Models/OnboardingDataDataManager.swift @@ -7,13 +7,14 @@ import Foundation -final class OnboardingDataDataManager { +final class OnboardingDataDataManager: ObservableObject { static let shared = OnboardingDataDataManager() @Published public private(set) var savedOnboardingData = UserDefaultsStore.getOnboarding() - + public func updateOnboardingData(onboardingData: OnboardingData) { let onboardingData = UserDefaultsStore.saveOnboarding(onboardingData: onboardingData) + savedOnboardingData = onboardingData LocalNotification.scheduleReminder(atTime: onboardingData.date, withTitle: onboardingData.title) } } diff --git a/Shared/Models/PersonalityPackable.swift b/Shared/Models/PersonalityPackable.swift new file mode 100644 index 0000000..193b9c1 --- /dev/null +++ b/Shared/Models/PersonalityPackable.swift @@ -0,0 +1,91 @@ +// +// NotificationTitles.swift +// Feels (iOS) +// +// Created by Trey Tartt on 2/19/22. +// + +import Foundation + +protocol PersonalityPackable { + static var notificationTitlesToday: [String] { get } + static var notificationTitlesYesterday: [String] { get } + static var notificationTitlesTwoDaysAgo: [String] { get } + + static var title: String { get } +} + +enum PersonalityPack: Int, CaseIterable { + case Default + case Rude + + func randomPushNotificationTitle() -> String { + switch self { + case .Default: + return DefaultTitles.notificationTitlesToday.randomElement()! + case .Rude: + return RudeTitles.notificationTitlesToday.randomElement()! + } + } + + func title() -> String { + switch self { + case .Default: + return DefaultTitles.title + case .Rude: + return RudeTitles.title + } + } +} + +final class DefaultTitles: PersonalityPackable { + static var title = "Nice" + + static var notificationTitlesToday: [String] { + [ + "How was your day", + "Don't forget to rate your day", + "Please rate your day" + ] + } + + static var notificationTitlesYesterday: [String] { + [ + "How was your day", + "Don't forget to rate your day" + ] + } + + static var notificationTitlesTwoDaysAgo: [String] { + [ + "How was your day", + "Don't forget to rate your day" + ] + } +} + +final class RudeTitles: PersonalityPackable { + static var title = "Rude" + + static var notificationTitlesToday: [String] { + [ + "How the fuck was your day", + "Hey asshat, tell me how your day was", + "Hey, lazy dickbag, rate your day" + ] + } + + static var notificationTitlesYesterday: [String] { + [ + "How was your day", + "Don't forget to rate your day" + ] + } + + static var notificationTitlesTwoDaysAgo: [String] { + [ + "How was your day", + "Don't forget to rate your day" + ] + } +} diff --git a/Shared/Models/Theme.swift b/Shared/Models/Theme.swift index 71203b4..4d65da6 100644 --- a/Shared/Models/Theme.swift +++ b/Shared/Models/Theme.swift @@ -14,6 +14,8 @@ struct ThemeConstants { enum Theme: Int, CaseIterable { case system case iFeel + case dark + case light var title: String { switch self { @@ -21,6 +23,10 @@ enum Theme: Int, CaseIterable { return SystemTheme.title case .iFeel: return IFeelTheme.title + case .dark: + return AlwaysDark.title + case .light: + return AlwaysLight.title } } @@ -31,24 +37,33 @@ enum Theme: Int, CaseIterable { return SystemTheme() case .iFeel: return IFeelTheme() + case .dark: + return AlwaysDark() + case .light: + return AlwaysLight() } } } protocol Themeable { static var title: String { get } - var secondaryBGColor: UIColor { get } + var secondaryBGColor: Color { get } var bg: AnyView { get } var preview: AnyView { get } + var labelColor: Color { get } } struct IFeelTheme: Themeable { - static var title: String { - return "iFeel Theme" + var labelColor: Color { + return Color(uiColor: UIColor.label) } - var secondaryBGColor: UIColor { - return UIColor.systemBackground + static var title: String { + return "iFeel" + } + + var secondaryBGColor: Color { + return Color(uiColor: UIColor.systemBackground) } var bg: AnyView { @@ -69,12 +84,16 @@ struct IFeelTheme: Themeable { } struct SystemTheme: Themeable { + var labelColor: Color { + return Color(uiColor: UIColor.label) + } + static var title: String { return "System" } - var secondaryBGColor: UIColor { - return UIColor.secondarySystemBackground + var secondaryBGColor: Color { + return Color(uiColor: UIColor.secondarySystemBackground) } var bg: AnyView { @@ -97,3 +116,71 @@ struct SystemTheme: Themeable { ) } } + +struct AlwaysDark: Themeable { + var labelColor: Color { + return .white + } + + static var title: String { + return "Dark" + } + + var secondaryBGColor: Color { + return Color(uiColor: UIColor.secondarySystemBackground.resolvedColor(with: .init(userInterfaceStyle: .dark))) + } + + var bg: AnyView { + return AnyView( + ZStack { + Rectangle() + .fill(Color(UIColor.systemBackground.resolvedColor(with: .init(userInterfaceStyle: .dark)))) + } + ) + } + + var preview: AnyView { + return AnyView( + ZStack { + Rectangle() + .fill(Color(UIColor.secondarySystemBackground.resolvedColor(with: .init(userInterfaceStyle: .dark)))) + .frame(width: ThemeConstants.iconSize, height: ThemeConstants.iconSize) + .clipShape(RoundedRectangle(cornerRadius: 25, style: .continuous)) + } + ) + } +} + +struct AlwaysLight: Themeable { + var labelColor: Color { + return .black + } + + static var title: String { + return "Light" + } + + var secondaryBGColor: Color { + return Color(uiColor: UIColor.secondarySystemBackground.resolvedColor(with: .init(userInterfaceStyle: .light))) + } + + var bg: AnyView { + return AnyView( + ZStack { + Rectangle() + .fill(Color(UIColor.systemBackground.resolvedColor(with: .init(userInterfaceStyle: .light)))) + } + ) + } + + var preview: AnyView { + return AnyView( + ZStack { + Rectangle() + .fill(Color(UIColor.secondarySystemBackground.resolvedColor(with: .init(userInterfaceStyle: .light)))) + .frame(width: ThemeConstants.iconSize, height: ThemeConstants.iconSize) + .clipShape(RoundedRectangle(cornerRadius: 25, style: .continuous)) + } + ) + } +} diff --git a/Shared/Models/UserDefaultsStore.swift b/Shared/Models/UserDefaultsStore.swift index b74d191..641c9c3 100644 --- a/Shared/Models/UserDefaultsStore.swift +++ b/Shared/Models/UserDefaultsStore.swift @@ -15,7 +15,9 @@ class UserDefaultsStore { case deleteEnable case mainViewTopHeaderIndex case theme - + case moodImages + case moodTint + case personalityPack case customIcon case contentViewCurrentSelectedHeaderViewBackDays @@ -43,4 +45,41 @@ class UserDefaultsStore { fatalError("error saving") } } + + static func moodMoodImagable() -> MoodImagable.Type { + if let data = GroupUserDefaults.groupDefaults.object(forKey: UserDefaultsStore.Keys.moodImages.rawValue) as? Int, + let model = MoodImages.init(rawValue: data) { + return model.moodImages + } else { + return MoodImages.FontAwesome.moodImages + } + } + + static func moodTintable() -> MoodTintable.Type { + if let data = GroupUserDefaults.groupDefaults.object(forKey: UserDefaultsStore.Keys.moodTint.rawValue) as? Int, + let model = MoodTints.init(rawValue: data) { + return model.moodTints + } else { + return MoodTints.Default.moodTints + } + } + + static func personalityPackable() -> PersonalityPack { + if let data = GroupUserDefaults.groupDefaults.object(forKey: UserDefaultsStore.Keys.personalityPack.rawValue) as? Int, + let model = PersonalityPack.init(rawValue: data) { + return model + } else { + return PersonalityPack.Default + } + } + + static func theme() -> Theme { + if let data = GroupUserDefaults.groupDefaults.object(forKey: UserDefaultsStore.Keys.theme.rawValue) as? Int, + let model = Theme.init(rawValue: data) { + return model + } else { + return Theme.system + } + } } + diff --git a/Shared/MoodEntryFunctions.swift b/Shared/MoodEntryFunctions.swift new file mode 100644 index 0000000..c01ef92 --- /dev/null +++ b/Shared/MoodEntryFunctions.swift @@ -0,0 +1,66 @@ +// +// MoodEntryFunctions.swift +// Feels (iOS) +// +// Created by Trey Tartt on 2/19/22. +// + +import Foundation + +class MoodEntryFunctions { + static func padMoodEntriesForCalendar(entries grouped: [Int: [Int: [MoodEntry]]]) -> [Int: [Int: [MoodEntry]]] { + var newGrouped = [Int: [Int: [MoodEntry]]]() + + let allYears = grouped.keys.sorted(by: > ) + for year in allYears { + var newMonth = [Int: [MoodEntry]]() + + let oldMonths = grouped[year]! + let monthKeys = oldMonths.keys.sorted(by: > ) + for key in monthKeys { + if let entries = oldMonths[key] { + newMonth[key] = MoodEntryFunctions.padMoodEntriesMonth(monthEntries: entries) + } + newGrouped[year] = newMonth + } + } + return newGrouped + } + + static func padMoodEntriesMonth(monthEntries entries: [MoodEntry]) -> [MoodEntry] { + let sortedEntries = entries.sorted(by: { $0.forDate! < $1.forDate! }) + var mutableEntries = sortedEntries + + if let firstDate = sortedEntries.first { + let date = firstDate.forDate! + + // if the first entry for a month is in the middle of the month we + // need to add in the missing entries, as placeholders, to the beignning to get + // the entries on the right day. think user downloads in the middle of the month + // and entry is on the 13th ... this needs to show on the 13th entry spot + var startOfMonth = date.startOfMonth + startOfMonth = Calendar.current.date(byAdding: .hour, value: 9, to: startOfMonth)! + let lastMissingDate = mutableEntries.first?.forDate ?? date.endOfMonth + var missingDates = Date.dates(from: startOfMonth, to: lastMissingDate) + missingDates = missingDates.dropLast() + + for date in missingDates { + mutableEntries.insert(PersistenceController.shared.generateObjectNotInArray(forDate: date, withMood: .placeholder), at: 0) + } + + mutableEntries = mutableEntries.sorted(by: { + $0.forDate! < $1.forDate! + }) + + // fill in calendar day offset .. if month starts on wed we need to + // pad the beginning sun, mon, tues + if let firstDate = mutableEntries.first?.forDate { + let weekday = Int16(Calendar.current.component(.weekday, from: firstDate)) + for _ in 1..(entityName: "MoodEntry") fetchRequest.sortDescriptors = [NSSortDescriptor(key: "forDate", ascending: false)] diff --git a/Shared/Persisence/PersistenceGET.swift b/Shared/Persisence/PersistenceGET.swift index 7422283..425945d 100644 --- a/Shared/Persisence/PersistenceGET.swift +++ b/Shared/Persisence/PersistenceGET.swift @@ -73,13 +73,12 @@ extension PersistenceController { var components = DateComponents() components.month = month components.year = year + components.day = 1 let startDateOfMonth = Calendar.current.date(from: components)! - let items = data.filter({ entry in - let components = calendar.dateComponents([.month, .year], from: startDateOfMonth) - let entryComponents = calendar.dateComponents([.month, .year], from: entry.forDate!) - return (components.month == entryComponents.month && components.year == entryComponents.year) - }) + let items = PersistenceController.shared.getData(startDate: startDateOfMonth, + endDate: startDateOfMonth.endOfMonth, + includedDays: [1,2,3,4,5,6,7]) if !items.isEmpty { allMonths[month] = items } diff --git a/Shared/Persisence/PersistenceHelper.swift b/Shared/Persisence/PersistenceHelper.swift index 4f570d0..fd48b83 100644 --- a/Shared/Persisence/PersistenceHelper.swift +++ b/Shared/Persisence/PersistenceHelper.swift @@ -51,11 +51,11 @@ extension PersistenceController { } } - func generateObjectNotInArray() -> MoodEntry { + func generateObjectNotInArray(forDate date: Date = Date(), withMood mood: Mood = .placeholder) -> MoodEntry { let newItem = MoodEntry(context: childContext) newItem.timestamp = Date() - newItem.moodValue = Int16(Mood.placeholder.rawValue) - newItem.forDate = Date() + newItem.moodValue = Int16(mood.rawValue) + newItem.forDate = date newItem.weekDay = Int16(Calendar.current.component(.weekday, from: Date())) newItem.canEdit = false newItem.canDelete = false diff --git a/Shared/Protocols/ChartDataBuildable.swift b/Shared/Protocols/ChartDataBuildable.swift index eb7c39c..78a4c2f 100644 --- a/Shared/Protocols/ChartDataBuildable.swift +++ b/Shared/Protocols/ChartDataBuildable.swift @@ -77,7 +77,9 @@ extension ChartDataBuildable { let date = components.day return day == date }).first { - let view = ChartType(color: item.mood.color, + let moodTint: MoodTintable.Type = UserDefaultsStore.moodTintable() + + let view = ChartType(color: moodTint.color(forMood: item.mood), weekDay: Int(item.weekDay), viewType: .square) filledOutArray.append(view) diff --git a/Shared/Random.swift b/Shared/Random.swift index 20bb70d..574c662 100644 --- a/Shared/Random.swift +++ b/Shared/Random.swift @@ -18,8 +18,6 @@ struct GroupUserDefaults { } } -typealias MoodGroupingMetrics = (mood: Mood, total: Int, percent: Float) - class Random { static var tomorrowMidnightThirty: Date { let components = DateComponents(hour: 0, minute: 30, second: 0) @@ -52,61 +50,29 @@ class Random { return formatter.string(from: NSNumber(integerLiteral: day)) ?? "" } - static func createTotalPerc(fromEntries entries: [MoodEntry]) -> [MoodGroupingMetrics] { - var returnData = [MoodGroupingMetrics]() + static func createTotalPerc(fromEntries entries: [MoodEntry]) -> [MoodMetrics] { + let filteredEntries = entries.filter({ + return ![.missing, .placeholder].contains($0.mood) + }) + var returnData = [MoodMetrics]() for (_, mood) in Mood.allValues.enumerated() { - let moodEntries = entries.filter({ + let moodEntries = filteredEntries.filter({ Int($0.moodValue) == mood.rawValue }) let total = moodEntries.count - let perc = (Float(total) / Float(entries.count)) * 100 - returnData.append((mood, total, perc)) + let perc = (Float(total) / Float(filteredEntries.count)) * 100 + returnData.append(MoodMetrics(mood: mood, total: total, percent: perc)) } returnData = returnData.sorted(by: { - $0.0.rawValue > $1.0.rawValue + $0.mood.rawValue > $1.mood.rawValue }) return returnData } } -extension Date: RawRepresentable { - public var rawValue: String { - self.timeIntervalSinceReferenceDate.description - } - - public init?(rawValue: String) { - self = Date(timeIntervalSinceReferenceDate: Double(rawValue) ?? 0.0) - } - - func startOfMonth() -> Date { - let interval = Calendar.current.dateInterval(of: .month, for: self) - return (interval?.start.toLocalTime())! // Without toLocalTime it give last months last date - } - - func endOfMonth() -> Date { - let interval = Calendar.current.dateInterval(of: .month, for: self) - return interval!.end - } - - func toLocalTime() -> Date { - let timezone = TimeZone.current - let seconds = TimeInterval(timezone.secondsFromGMT(for: self)) - return Date(timeInterval: seconds, since: self) - } - - var weekday: Int { - Calendar.current.component(.weekday, from: self) - } - - var firstDayOfTheMonth: Date { - Calendar.current.dateComponents([.calendar, .year,.month], from: self).date! - } -} - - struct RoundedCorner: Shape { var radius: CGFloat = .infinity @@ -153,20 +119,6 @@ extension UIView { } } -extension Date { - static func dates(from fromDate: Date, to toDate: Date) -> [Date] { - var dates: [Date] = [] - var date = fromDate - - while date <= toDate { - dates.append(date) - guard let newDate = Calendar.current.date(byAdding: .day, value: 1, to: date) else { break } - date = newDate - } - return dates - } -} - extension Color { static func random() -> Self { Self( @@ -175,4 +127,47 @@ extension Color { blue: .random(in: 0...1) ) } + + init(hex: String) { + let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted) + var int: UInt64 = 0 + Scanner(string: hex).scanHexInt64(&int) + let a, r, g, b: UInt64 + switch hex.count { + case 3: // RGB (12-bit) + (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17) + case 6: // RGB (24-bit) + (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF) + case 8: // ARGB (32-bit) + (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF) + default: + (a, r, g, b) = (1, 1, 1, 0) + } + + self.init( + .sRGB, + red: Double(r) / 255, + green: Double(g) / 255, + blue: Double(b) / 255, + opacity: Double(a) / 255 + ) + } +} + +extension String { + func textToImage() -> UIImage? { + let nsString = (self as NSString) + let font = UIFont.systemFont(ofSize: 100) // you can change your font size here + let stringAttributes = [NSAttributedString.Key.font: font] + let imageSize = nsString.size(withAttributes: stringAttributes) + + UIGraphicsBeginImageContextWithOptions(imageSize, false, 0) // begin image context + UIColor.clear.set() // clear background + UIRectFill(CGRect(origin: CGPoint(), size: imageSize)) // set rect size + nsString.draw(at: CGPoint.zero, withAttributes: stringAttributes) // draw text within rect + let image = UIGraphicsGetImageFromCurrentImageContext() // create image from context + UIGraphicsEndImageContext() // end image context + + return image ?? UIImage() + } } diff --git a/Shared/ShowBasedOnVoteLogics.swift b/Shared/ShowBasedOnVoteLogics.swift index 89fad94..0051ef0 100644 --- a/Shared/ShowBasedOnVoteLogics.swift +++ b/Shared/ShowBasedOnVoteLogics.swift @@ -18,17 +18,13 @@ import SwiftUI // today at 11 am -> How as 2 days ago // today at 1 pm -> How was yesterday class ShowBasedOnVoteLogics { - private static var currentVoting: (passTimeToVote: Bool, dayOptions: DayOptions) { - let passedTimeToVote = UserDefaultsStore.getOnboarding().ableToVoteBasedOnCurentTime() - let inputDay = UserDefaultsStore.getOnboarding().inputDay + static func isMissingCurrentVote(onboardingData: OnboardingData) -> Bool { + let passedTimeToVote = onboardingData.ableToVoteBasedOnCurentTime() + let inputDay = onboardingData.inputDay - return (passedTimeToVote, inputDay) - } - - static func isMissingCurrentVote() -> Bool { var startDate: Date? - - switch (currentVoting.passTimeToVote, currentVoting.dayOptions) { + + switch (passedTimeToVote, inputDay) { case (true, .Previous): // if we're passed time to vote and the voting type is previous - last vote should be -1 startDate = Calendar.current.date(byAdding: .day, value: -1, to: Date())! @@ -57,8 +53,11 @@ class ShowBasedOnVoteLogics { return entries < 1 } - static func getVotingTitle() -> String { - switch (currentVoting.passTimeToVote, currentVoting.dayOptions) { + static func getVotingTitle(onboardingData: OnboardingData) -> String { + let passedTimeToVote = onboardingData.ableToVoteBasedOnCurentTime() + let inputDay = onboardingData.inputDay + + switch (passedTimeToVote, inputDay) { case (true, .Previous): // if we're passed time to vote and the voting type is previous - last vote should be -1 return "how was yesterday" @@ -67,7 +66,7 @@ class ShowBasedOnVoteLogics { return "how is today" case (false, .Previous): // if we're passed time to vote and the voting type is previous - last vote should be -2 - let lastDayVoteShouldExist = ShowBasedOnVoteLogics.getLastDateVoteShouldExist() + let lastDayVoteShouldExist = ShowBasedOnVoteLogics.getLastDateVoteShouldExist(onboardingData: onboardingData) return "how was \(Random.weekdayName(fromDate: lastDayVoteShouldExist))" case (false, .Today): // if we're passed time to vote and the voting type is previous - last vote should be -1 @@ -75,10 +74,13 @@ class ShowBasedOnVoteLogics { } } - static func dateForHeaderVote() -> Date? { + static func dateForHeaderVote(onboardingData: OnboardingData) -> Date? { + let passedTimeToVote = onboardingData.ableToVoteBasedOnCurentTime() + let inputDay = onboardingData.inputDay + var date: Date? - switch (currentVoting.passTimeToVote, currentVoting.dayOptions) { + switch (passedTimeToVote, inputDay) { case (true, .Previous): // if we're passed time to vote and the voting type is previous - last vote should be -1 date = Calendar.current.date(byAdding: .day, value: -1, to: Date()) @@ -100,10 +102,13 @@ class ShowBasedOnVoteLogics { return nil } - static func getLastDateVoteShouldExist() -> Date { + static func getLastDateVoteShouldExist(onboardingData: OnboardingData) -> Date { + let passedTimeToVote = onboardingData.ableToVoteBasedOnCurentTime() + let inputDay = onboardingData.inputDay + var endDate: Date? - switch (currentVoting.passTimeToVote, currentVoting.dayOptions) { + switch (passedTimeToVote, inputDay) { case (true, .Previous): // if we're passed time to vote and the voting type is previous - last vote should -1 endDate = Calendar.current.date(byAdding: .day, value: -1, to: Date())! diff --git a/Shared/views/AddMoodHeaderView.swift b/Shared/views/AddMoodHeaderView.swift index fd421aa..eaa2fe0 100644 --- a/Shared/views/AddMoodHeaderView.swift +++ b/Shared/views/AddMoodHeaderView.swift @@ -11,8 +11,10 @@ import SwiftUI import CoreData struct AddMoodHeaderView: View { - private let savedOnboardingData = UserDefaultsStore.getOnboarding() @AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system + @AppStorage(UserDefaultsStore.Keys.moodTint.rawValue, store: GroupUserDefaults.groupDefaults) private var moodTint: MoodTints = .Default + + @State var onboardingData = OnboardingDataDataManager.shared.savedOnboardingData let addItemHeaderClosure: ((Mood, Date) -> Void) @@ -22,12 +24,12 @@ struct AddMoodHeaderView: View { var body: some View { ZStack { - Color(theme.currentTheme.secondaryBGColor) + theme.currentTheme.secondaryBGColor VStack { - Text(ShowBasedOnVoteLogics.getVotingTitle()) + Text(ShowBasedOnVoteLogics.getVotingTitle(onboardingData: onboardingData)) .font(.title) - .foregroundColor(Color(UIColor.label)) + .foregroundColor(theme.currentTheme.labelColor) .padding() HStack{ ForEach(Mood.allValues) { mood in @@ -38,7 +40,7 @@ struct AddMoodHeaderView: View { mood.icon .resizable() .frame(width: CGFloat(50), height: CGFloat(50), alignment: .center) - .foregroundColor(mood.color) + .foregroundColor(moodTint.color(forMood: mood)) }) //Text(mood.strValue) @@ -49,7 +51,7 @@ struct AddMoodHeaderView: View { .padding([.leading, .trailing, .bottom]) } .background( - Color(theme.currentTheme.secondaryBGColor) + theme.currentTheme.secondaryBGColor ) .cornerRadius(10, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) .frame(minHeight: 88, maxHeight: 150) @@ -57,7 +59,7 @@ struct AddMoodHeaderView: View { } private func addItem(withMood mood: Mood) { - if let date = ShowBasedOnVoteLogics.dateForHeaderVote() { + if let date = ShowBasedOnVoteLogics.dateForHeaderVote(onboardingData: onboardingData) { addItemHeaderClosure(mood, date) } } diff --git a/Shared/views/BGView.swift b/Shared/views/BGView.swift index bade627..6babd7d 100644 --- a/Shared/views/BGView.swift +++ b/Shared/views/BGView.swift @@ -8,14 +8,16 @@ import SwiftUI struct BGViewItem: View { + @AppStorage(UserDefaultsStore.Keys.moodTint.rawValue, store: GroupUserDefaults.groupDefaults) private var moodTint: MoodTints = .Default + let mood: Mood let size: CGSize var color: Color let animate: Bool let yRowPosition: Float - init(mood: Mood, size: CGSize, animate: Bool, yRowPosition: Float) { - color = mood.color + init(mood: Mood, size: CGSize, animate: Bool, yRowPosition: Float, color: Color) { + self.color = color self.mood = mood self.size = size self.yRowPosition = yRowPosition @@ -23,16 +25,18 @@ struct BGViewItem: View { } var body: some View { - Mood.allValues.randomElement()?.icon + FontAwesomeMoodImages.icon(forMood: mood) .resizable() .frame(width: size.width, height: size.height) - .foregroundColor(color) -// .blur(radius: 3) + .foregroundColor(DefaultMoodTint.color(forMood: mood)) + // .blur(radius: 3) .opacity(0.1) } } struct BGView: View, Equatable { + @AppStorage(UserDefaultsStore.Keys.moodTint.rawValue, store: GroupUserDefaults.groupDefaults) private var moodTint: MoodTints = .Default + var numAcross: Int var numDown: Int let iconSize = 35 @@ -45,15 +49,22 @@ struct BGView: View, Equatable { numDown = Int(screenHeight)/iconSize } + var randomMood: Mood? { + return Mood.allValues.randomElement() + } + var body: some View { VStack { ForEach(0...numDown, id: \.self) { row in HStack { ForEach(0...numAcross, id: \.self) { _ in - BGViewItem(mood: Mood.allValues.randomElement()!, - size: .init(width: iconSize,height: iconSize), - animate: false, - yRowPosition: Float(row)/Float(numDown)) + if let randomMood = randomMood { + BGViewItem(mood: randomMood, + size: .init(width: iconSize,height: iconSize), + animate: false, + yRowPosition: Float(row)/Float(numDown), + color: moodTint.color(forMood:randomMood)) + } }.frame(minWidth: 0, maxWidth: .infinity) .padding([.trailing, .leading], 13.5) } diff --git a/Shared/views/CreateIconView.swift b/Shared/views/CustomIcon/CreateWidgetView.swift similarity index 71% rename from Shared/views/CreateIconView.swift rename to Shared/views/CustomIcon/CreateWidgetView.swift index 038f06b..8a8ce1c 100644 --- a/Shared/views/CreateIconView.swift +++ b/Shared/views/CustomIcon/CreateWidgetView.swift @@ -7,47 +7,47 @@ import SwiftUI -struct CreateIconView: View { +struct CreateWidgetView: View { @AppStorage(UserDefaultsStore.Keys.customIcon.rawValue, store: GroupUserDefaults.groupDefaults) private var savedCustomIcon = Data() @AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system - static var iconViewBGs: [(BackGroundOptions, UUID)] = { - var blah = [(BackGroundOptions, UUID)]() + static var iconViewBGs: [(CustomWidgetBackGroundOptions, UUID)] = { + var blah = [(CustomWidgetBackGroundOptions, UUID)]() for _ in 0...99 { - blah.append((BackGroundOptions.selectable.randomElement()!, UUID())) + blah.append((CustomWidgetBackGroundOptions.selectable.randomElement()!, UUID())) } return blah }() - @State private var mouth: MouthOptions = MouthOptions.defaultOption - @StateObject private var customIcon = CustomIcon(leftEye: EyeOptions.defaultOption, - rightEye: EyeOptions.defaultOption, - mouth: MouthOptions.defaultOption, - background: CreateIconView.iconViewBGs, - bgColor: .red, - innerColor: .green, - bgOverlayColor: .black, - rightEyeColor: .orange, - leftEyeColor: .yellow, - mouthColor: .purple, - circleStrokeColor: .pink) + @State private var mouth: CustomWidgetMouthOptions = CustomWidgetMouthOptions.defaultOption + @StateObject private var customIcon = CustomWidgetModel(leftEye: CustomWidgetEyeOptions.defaultOption, + rightEye: CustomWidgetEyeOptions.defaultOption, + mouth: CustomWidgetMouthOptions.defaultOption, + background: CreateWidgetView.iconViewBGs, + bgColor: .red, + innerColor: .green, + bgOverlayColor: .black, + rightEyeColor: .orange, + leftEyeColor: .yellow, + mouthColor: .purple, + circleStrokeColor: .pink) private var randomElements: [AnyView] = [ - AnyView(Image(BackGroundOptions.selectable.randomElement()!.rawValue) + AnyView(Image(CustomWidgetBackGroundOptions.selectable.randomElement()!.rawValue) .resizable() .frame(width: 20, height: 20)), - AnyView(Image(BackGroundOptions.selectable.randomElement()!.rawValue) + AnyView(Image(CustomWidgetBackGroundOptions.selectable.randomElement()!.rawValue) .resizable() .frame(width: 20, height: 20)), - AnyView(Image(BackGroundOptions.selectable.randomElement()!.rawValue) + AnyView(Image(CustomWidgetBackGroundOptions.selectable.randomElement()!.rawValue) .resizable() .frame(width: 20, height: 20)), - AnyView(Image(BackGroundOptions.selectable.randomElement()!.rawValue) + AnyView(Image(CustomWidgetBackGroundOptions.selectable.randomElement()!.rawValue) .resizable() .frame(width: 20, height: 20)) ] - func update(eye: Eyes, eyeOption: EyeOptions) { + func update(eye: CustomWidgetEyes, eyeOption: CustomWidgetEyeOptions) { switch eye { case .left: customIcon.leftEye = eyeOption @@ -65,27 +65,27 @@ struct CreateIconView: View { customIcon.rightEyeColor = Color.random() customIcon.mouthColor = Color.random() - update(eye: .left, eyeOption: EyeOptions.allCases.randomElement()!) - update(eye: .right, eyeOption: EyeOptions.allCases.randomElement()!) - update(mouthOption: MouthOptions.allCases.randomElement()!) + update(eye: .left, eyeOption: CustomWidgetEyeOptions.allCases.randomElement()!) + update(eye: .right, eyeOption: CustomWidgetEyeOptions.allCases.randomElement()!) + update(mouthOption: CustomWidgetMouthOptions.allCases.randomElement()!) - update(background: BackGroundOptions.allCases.randomElement()!) + update(background: CustomWidgetBackGroundOptions.allCases.randomElement()!) } - func update(mouthOption: MouthOptions) { + func update(mouthOption: CustomWidgetMouthOptions) { customIcon.mouth = mouthOption } - func update(background: BackGroundOptions) { + func update(background: CustomWidgetBackGroundOptions) { customIcon.background.removeAll() if background == .random { - for _ in 0...CustomIcon.numberOfBGItems { - customIcon.background.append((BackGroundOptions.selectable.randomElement()!, UUID())) + for _ in 0...CustomWidgetModel.numberOfBGItems { + customIcon.background.append((CustomWidgetBackGroundOptions.selectable.randomElement()!, UUID())) } return } - for _ in 0...CustomIcon.numberOfBGItems { + for _ in 0...CustomWidgetModel.numberOfBGItems { customIcon.background.append((background, UUID())) } } @@ -103,16 +103,16 @@ struct CreateIconView: View { } } - var iconView: some View { - IconView(customIcon: customIcon, isPreview: true) + var widgetView: some View { + CustomWidgetView(customWidgetModel: customIcon) } var body: some View { VStack(spacing: 0) { - iconView - .frame(width: 256, height: 256) + widgetView +// .frame(width: 256, height: 256) .cornerRadius(10) - .padding(.top) + .padding() Spacer() @@ -123,7 +123,7 @@ struct CreateIconView: View { Spacer() VStack(alignment: .center) { Menu("Left Eye") { - ForEach(EyeOptions.allCases, id: \.self) { option in + ForEach(CustomWidgetEyeOptions.allCases, id: \.self) { option in Button(action: { update(eye: .left, eyeOption: option) }, label: { @@ -131,12 +131,12 @@ struct CreateIconView: View { }) } } - .foregroundColor(Color(UIColor.label)) + .foregroundColor(theme.currentTheme.labelColor) } Spacer() VStack(alignment: .center) { Menu("Right Eye") { - ForEach(EyeOptions.allCases, id: \.self) { option in + ForEach(CustomWidgetEyeOptions.allCases, id: \.self) { option in Button(action: { update(eye: .right, eyeOption: option) }, label: { @@ -144,12 +144,12 @@ struct CreateIconView: View { }) } } - .foregroundColor(Color(UIColor.label)) + .foregroundColor(theme.currentTheme.labelColor) } Spacer() VStack(alignment: .center) { Menu("Mouth") { - ForEach(MouthOptions.allCases, id: \.self) { option in + ForEach(CustomWidgetMouthOptions.allCases, id: \.self) { option in Button(action: { update(mouthOption: option) }, label: { @@ -157,13 +157,13 @@ struct CreateIconView: View { }) } } - .foregroundColor(Color(UIColor.label)) + .foregroundColor(theme.currentTheme.labelColor) } Spacer() } .padding() .background( - Color(uiColor: theme.currentTheme.secondaryBGColor) + theme.currentTheme.secondaryBGColor ) } @@ -171,7 +171,7 @@ struct CreateIconView: View { Group { HStack { - ForEach(BackGroundOptions.selectable, id: \.self) { bg in + ForEach(CustomWidgetBackGroundOptions.selectable, id: \.self) { bg in Image(bg.rawValue, bundle: .main) .resizable() .aspectRatio(contentMode: .fill) @@ -191,7 +191,7 @@ struct CreateIconView: View { } .padding() .background( - Color(uiColor: theme.currentTheme.secondaryBGColor) + theme.currentTheme.secondaryBGColor ) } @@ -248,7 +248,7 @@ struct CreateIconView: View { } .padding() .background( - Color(uiColor: theme.currentTheme.secondaryBGColor) + theme.currentTheme.secondaryBGColor ) } @@ -269,7 +269,7 @@ struct CreateIconView: View { .background(.blue) Button(action: { - let bigIconView = IconView(customIcon: customIcon, isPreview: false) + let bigIconView = CustomWidgetView(customWidgetModel: customIcon) .frame(width: 512, height: 512, alignment: .center) .aspectRatio(contentMode: .fill) let icon = bigIconView.snapshot() @@ -297,9 +297,9 @@ struct CreateIconView: View { struct CreateIconView_Previews: PreviewProvider { static var previews: some View { Group { - CreateIconView() + CreateWidgetView() - CreateIconView() + CreateWidgetView() .preferredColorScheme(.dark) } } diff --git a/Shared/views/CustomIcon/IconView.swift b/Shared/views/CustomIcon/IconView.swift new file mode 100644 index 0000000..98509d9 --- /dev/null +++ b/Shared/views/CustomIcon/IconView.swift @@ -0,0 +1,124 @@ +// +// IconView.swift +// Feels (iOS) +// +// Created by Trey Tartt on 2/20/22. +// + +import SwiftUI + +struct IconView: View { + @State public var iconViewModel: IconViewModel + + private let facePercSize = 0.6 + public let isPreview: Bool + + private var gridXOffset: CGFloat { + if isPreview { + return CGFloat(0) + } + return CGFloat(6) + } + + private var gridYOffset: CGFloat { + if isPreview { + return CGFloat(0) + } + return CGFloat(-8) + } + + private var entireFuckingViewOffset: CGFloat { + if isPreview { + return CGFloat(0) + } + return CGFloat(25) + } + + let columns = [ + GridItem(.flexible(minimum: 1, maximum: 100), spacing: 1), + GridItem(.flexible(minimum: 1, maximum: 100), spacing: 1), + GridItem(.flexible(minimum: 1, maximum: 100), spacing: 1), + GridItem(.flexible(minimum: 1, maximum: 100), spacing: 1), + GridItem(.flexible(minimum: 1, maximum: 100), spacing: 1), + GridItem(.flexible(minimum: 1, maximum: 100), spacing: 1), + GridItem(.flexible(minimum: 1, maximum: 100), spacing: 1), + GridItem(.flexible(minimum: 1, maximum: 100), spacing: 1), + GridItem(.flexible(minimum: 1, maximum: 100), spacing: 1), + GridItem(.flexible(minimum: 1, maximum: 100), spacing: 1) + ] + + var body: some View { + GeometryReader { geo in + ZStack { + Rectangle() + .fill( + iconViewModel.bgColor + ) + .frame(maxWidth: .infinity, maxHeight: .infinity) + + LazyVGrid(columns: columns, alignment: .leading, spacing: 0) { + ForEach(iconViewModel.background, id: \.self.1) { (bgOption, uuid) in + bgOption + .resizable() + .aspectRatio(1, contentMode: .fill) + .foregroundColor(iconViewModel.bgOverlayColor) + } + } + .scaleEffect(1.1) + .clipped() + .background( + .clear + ) + + Circle() + .strokeBorder(iconViewModel.bgColor, lineWidth: geo.size.width * 0.045) + .background(Circle().fill(iconViewModel.bgColor)) + .frame(width: geo.size.width*facePercSize, + height: geo.size.height*facePercSize, + alignment: .center) + .position(x: geo.size.width/2, y: geo.size.height/2) + + iconViewModel.centerImage + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: geo.size.width*facePercSize, + height: geo.size.height*facePercSize, + alignment: .center) + .foregroundColor(iconViewModel.bgOverlayColor) + .position(x: geo.size.width/2, y: geo.size.height/2) + + } + .position(x: geo.size.width/2, + y: geo.size.height/2 - entireFuckingViewOffset) + } + } +} + +struct IconView_Previews: PreviewProvider { + static var previews: some View { + Group { + IconView(iconViewModel: IconViewModel.great, isPreview: false) + .frame(width: 256, height: 256, alignment: .center) + +// IconView(iconViewModel: IconViewModel.good, isPreview: true) +// .frame(width: 256, height: 256, alignment: .center) +// +// IconView(iconViewModel: IconViewModel.average, isPreview: true) +// .frame(width: 256, height: 256, alignment: .center) +// +// IconView(iconViewModel: IconViewModel.bad, isPreview: true) +// .frame(width: 256, height: 256, alignment: .center) +// +// IconView(iconViewModel: IconViewModel.horrible, isPreview: true) +// .frame(width: 256, height: 256, alignment: .center) +// +// IconView(iconViewModel: IconViewModel(backgroundImage: EmojiMoodImages.icon(forMood: .horrible), +// bgColor: MoodTints.Neon.color(forMood: .horrible), +// bgOverlayColor: MoodTints.Neon.color(forMood: .horrible), +// centerImage: EmojiMoodImages.icon(forMood: .horrible)), +// isPreview: true) +// .frame(width: 256, height: 256, alignment: .center) + } + + } +} diff --git a/Shared/views/CustomIcon/IconViewModel.swift b/Shared/views/CustomIcon/IconViewModel.swift new file mode 100644 index 0000000..dc07bbe --- /dev/null +++ b/Shared/views/CustomIcon/IconViewModel.swift @@ -0,0 +1,80 @@ +// +// CustomIcon.swift +// Feels (iOS) +// +// Created by Trey Tartt on 2/20/22. +// + +import SwiftUI + +class IconViewModel: ObservableObject { + static let numberOfBGItems = 109 + + static let great = IconViewModel(backgroundImage: MoodImages.FontAwesome.icon(forMood: .great), + bgColor: Color(hex: "31d158"), + bgOverlayColor: Color(hex: "208939"), + centerImage: MoodImages.FontAwesome.icon(forMood: .great)) + + static let good = IconViewModel(backgroundImage: MoodImages.FontAwesome.icon(forMood: .good), + bgColor: Color(hex: "ffd709"), + bgOverlayColor: Color(hex: "9d8405"), + centerImage: MoodImages.FontAwesome.icon(forMood: .good)) + + static let average = IconViewModel(backgroundImage: MoodImages.FontAwesome.icon(forMood: .average), + bgColor: Color(hex: "0b84ff"), + bgOverlayColor: Color(hex: "074f9a"), + centerImage: MoodImages.FontAwesome.icon(forMood: .average)) + + static let bad = IconViewModel(backgroundImage: MoodImages.FontAwesome.icon(forMood: .bad), + bgColor: Color(hex: "ff9f0b"), + bgOverlayColor: Color(hex: "a06407"), + centerImage: MoodImages.FontAwesome.icon(forMood: .bad)) + + static let horrible = IconViewModel(backgroundImage: MoodImages.FontAwesome.icon(forMood: .horrible), + bgColor: Color(hex: "fe5257"), + bgOverlayColor: Color(hex: "a92b26"), + centerImage: MoodImages.FontAwesome.icon(forMood: .horrible)) + + init(backgroundImage: Image, + bgColor: Color, + bgOverlayColor: Color, + centerImage: Image + ) { + + var blah = [(Image, UUID)]() + for _ in 0...IconViewModel.numberOfBGItems { + blah.append((backgroundImage, UUID())) + } + + self.background = blah + self.bgColor = bgColor + self.bgOverlayColor = bgOverlayColor + self.centerImage = centerImage + } + + @Published var background: [(Image, UUID)] + @Published var bgColor: Color + @Published var bgOverlayColor: Color + @Published var centerImage: Image +} + +enum CustomIconBackGroundOptions: String, CaseIterable, Codable { + case horrible + case bad + case average + case good + case great + case random + + static var selectable: [CustomIconBackGroundOptions] { + return [.great, .good, .average, .bad, .horrible] + } + + static public var defaultOption: CustomIconBackGroundOptions { + CustomIconBackGroundOptions.random + } + + public var image: Image { + return Image(self.rawValue, bundle: .main) + } +} diff --git a/Shared/Models/CustomIcon.swift b/Shared/views/CustomWidget/CustomWidgetModel.swift similarity index 54% rename from Shared/Models/CustomIcon.swift rename to Shared/views/CustomWidget/CustomWidgetModel.swift index 6aac96f..9ff8f0e 100644 --- a/Shared/Models/CustomIcon.swift +++ b/Shared/views/CustomWidget/CustomWidgetModel.swift @@ -7,26 +7,26 @@ import SwiftUI -class CustomIcon: ObservableObject { - static let numberOfBGItems = 99 +class CustomWidgetModel: ObservableObject { + static let numberOfBGItems = 109 - static let defaultCustomIcon = CustomIcon(leftEye: EyeOptions.defaultOption, - rightEye: EyeOptions.defaultOption, - mouth: MouthOptions.defaultOption, - background: IconView_Previews.backgrounds, - bgColor: .red, - innerColor: .green, - bgOverlayColor: .orange, - rightEyeColor: .orange, - leftEyeColor: .yellow, - mouthColor: .green, - circleStrokeColor: .pink) + static let defaultCustomIcon = CustomWidgetModel(leftEye: CustomWidgetEyeOptions.defaultOption, + rightEye: CustomWidgetEyeOptions.defaultOption, + mouth: CustomWidgetMouthOptions.defaultOption, + background: WidgetView_Previews.backgrounds, + bgColor: .red, + innerColor: .green, + bgOverlayColor: .orange, + rightEyeColor: .orange, + leftEyeColor: .yellow, + mouthColor: .green, + circleStrokeColor: .pink) - init(leftEye: EyeOptions, - rightEye: EyeOptions, - mouth: MouthOptions, - background: [(BackGroundOptions, UUID)], + init(leftEye: CustomWidgetEyeOptions, + rightEye: CustomWidgetEyeOptions, + mouth: CustomWidgetMouthOptions, + background: [(CustomWidgetBackGroundOptions, UUID)], bgColor: Color, innerColor: Color, bgOverlayColor: Color, @@ -48,11 +48,11 @@ class CustomIcon: ObservableObject { self.circleStrokeColor = circleStrokeColor } - @Published var leftEye: EyeOptions - @Published var rightEye: EyeOptions - @Published var mouth: MouthOptions + @Published var leftEye: CustomWidgetEyeOptions + @Published var rightEye: CustomWidgetEyeOptions + @Published var mouth: CustomWidgetMouthOptions - @Published var background: [(BackGroundOptions, UUID)] + @Published var background: [(CustomWidgetBackGroundOptions, UUID)] @Published var bgColor: Color @Published var innerColor: Color @Published var bgOverlayColor: Color @@ -64,7 +64,7 @@ class CustomIcon: ObservableObject { @Published var circleStrokeColor: Color } -enum BackGroundOptions: String, CaseIterable, Codable { +enum CustomWidgetBackGroundOptions: String, CaseIterable, Codable { case horrible case bad case average @@ -72,12 +72,12 @@ enum BackGroundOptions: String, CaseIterable, Codable { case great case random - static var selectable: [BackGroundOptions] { + static var selectable: [CustomWidgetBackGroundOptions] { return [.great, .good, .average, .bad, .horrible] } - static public var defaultOption: BackGroundOptions { - BackGroundOptions.random + static public var defaultOption: CustomWidgetBackGroundOptions { + CustomWidgetBackGroundOptions.random } public var image: Image { @@ -85,12 +85,12 @@ enum BackGroundOptions: String, CaseIterable, Codable { } } -enum Eyes: String, Codable { +enum CustomWidgetEyes: String, Codable { case left case right } -enum EyeOptions: String, CaseIterable, Codable { +enum CustomWidgetEyeOptions: String, CaseIterable, Codable { case fire = "fire" case bolt = "bolt2" case dollar = "dollar" @@ -109,8 +109,8 @@ enum EyeOptions: String, CaseIterable, Codable { case skull2 = "skull2" case poo = "poo" - static public var defaultOption: EyeOptions { - EyeOptions.fire + static public var defaultOption: CustomWidgetEyeOptions { + CustomWidgetEyeOptions.fire } public var image: Image { @@ -118,7 +118,7 @@ enum EyeOptions: String, CaseIterable, Codable { } } -enum MouthOptions: String, CaseIterable, Codable { +enum CustomWidgetMouthOptions: String, CaseIterable, Codable { case fire = "fire" case bolt = "bolt2" case dollar = "dollar" @@ -137,8 +137,8 @@ enum MouthOptions: String, CaseIterable, Codable { case skull2 = "skull2" case poo = "poo" - static public var defaultOption: MouthOptions { - MouthOptions.bomb + static public var defaultOption: CustomWidgetMouthOptions { + CustomWidgetMouthOptions.bomb } public var image: Image { diff --git a/Shared/views/IconView.swift b/Shared/views/CustomWidget/CustomWidgetView.swift similarity index 62% rename from Shared/views/IconView.swift rename to Shared/views/CustomWidget/CustomWidgetView.swift index 25fa761..0575309 100644 --- a/Shared/views/IconView.swift +++ b/Shared/views/CustomWidget/CustomWidgetView.swift @@ -7,32 +7,31 @@ import SwiftUI -struct IconView: View { - @State public var customIcon: CustomIcon +struct CustomWidgetView: View { + @State public var customWidgetModel: CustomWidgetModel private let facePercSize = 0.6 - public let isPreview: Bool - private var gridXOffset: CGFloat { - if isPreview { - return CGFloat(0) - } - return CGFloat(6) - } - - private var gridYOffset: CGFloat { - if isPreview { - return CGFloat(0) - } - return CGFloat(-8) - } - - private var entireFuckingViewOffset: CGFloat { - if isPreview { - return CGFloat(0) - } - return CGFloat(25) - } +// private var gridXOffset: CGFloat { +// if isPreview { +// return CGFloat(0) +// } +// return CGFloat(6) +// } +// +// private var gridYOffset: CGFloat { +// if isPreview { +// return CGFloat(0) +// } +// return CGFloat(-8) +// } +// +// private var entireFuckingViewOffset: CGFloat { +// if isPreview { +// return CGFloat(0) +// } +// return CGFloat(25) +// } let columns = [ GridItem(.flexible(minimum: 1, maximum: 100), spacing: 1), @@ -52,32 +51,33 @@ struct IconView: View { ZStack { Rectangle() .fill( - customIcon.bgColor + customWidgetModel.bgColor ) .frame(maxWidth: .infinity, maxHeight: .infinity) LazyVGrid(columns: columns, alignment: .leading, spacing: 0) { - ForEach(customIcon.background, id: \.self.1) { (bgOption, uuid) in + ForEach(customWidgetModel.background, id: \.self.1) { (bgOption, uuid) in bgOption.image .resizable() .aspectRatio(1, contentMode: .fill) - .foregroundColor(customIcon.bgOverlayColor) + .foregroundColor(customWidgetModel.bgOverlayColor) } } - .frame(maxWidth: .infinity, maxHeight: .infinity) + .scaleEffect(1.1) + .clipped() .background( .clear ) Circle() - .strokeBorder(customIcon.circleStrokeColor, lineWidth: geo.size.width * 0.045) - .background(Circle().fill(customIcon.innerColor)) + .strokeBorder(customWidgetModel.circleStrokeColor, lineWidth: geo.size.width * 0.045) + .background(Circle().fill(customWidgetModel.innerColor)) .frame(width: geo.size.width*facePercSize, height: geo.size.height*facePercSize, alignment: .center) .position(x: geo.size.width/2, y: geo.size.height/2) - customIcon.leftEye.image + customWidgetModel.leftEye.image .resizable() .aspectRatio(contentMode: .fit) .frame(width: geo.size.width*0.12, @@ -85,9 +85,9 @@ struct IconView: View { alignment: .center) .position(x: geo.size.width*0.4, y: geo.size.height*0.4) - .foregroundColor(customIcon.leftEyeColor) + .foregroundColor(customWidgetModel.leftEyeColor) - customIcon.rightEye.image + customWidgetModel.rightEye.image .resizable() .aspectRatio(contentMode: .fit) .frame(width: geo.size.width*0.12, @@ -95,9 +95,9 @@ struct IconView: View { alignment: .center) .position(x: geo.size.width*0.6, y: geo.size.height*0.4) - .foregroundColor(customIcon.rightEyeColor) + .foregroundColor(customWidgetModel.rightEyeColor) - customIcon.mouth.image + customWidgetModel.mouth.image .resizable() .aspectRatio(contentMode: .fit) .frame(width: geo.size.width*0.12, @@ -105,26 +105,25 @@ struct IconView: View { alignment: .center) .position(x: geo.size.width*0.5, y: geo.size.height*0.59) - .foregroundColor(customIcon.mouthColor) + .foregroundColor(customWidgetModel.mouthColor) } .position(x: geo.size.width/2, - y: geo.size.height/2 - entireFuckingViewOffset) + y: geo.size.height/2) } } } -struct IconView_Previews: PreviewProvider { - static var backgrounds: [(BackGroundOptions, UUID)] = { - var blah = [(BackGroundOptions, UUID)]() - for _ in 0...CustomIcon.numberOfBGItems { - blah.append((BackGroundOptions.selectable.randomElement()!, UUID())) +struct WidgetView_Previews: PreviewProvider { + static var backgrounds: [(CustomWidgetBackGroundOptions, UUID)] = { + var blah = [(CustomWidgetBackGroundOptions, UUID)]() + for _ in 0...CustomWidgetModel.numberOfBGItems { + blah.append((CustomWidgetBackGroundOptions.selectable.randomElement()!, UUID())) } return blah }() static var previews: some View { - IconView(customIcon: CustomIcon.defaultCustomIcon, - isPreview: true) + CustomWidgetView(customWidgetModel: CustomWidgetModel.defaultCustomIcon) .frame(width: 256, height: 256, alignment: .center) } diff --git a/Shared/views/CustomizeView/CustomizeView.swift b/Shared/views/CustomizeView/CustomizeView.swift new file mode 100644 index 0000000..73e7f58 --- /dev/null +++ b/Shared/views/CustomizeView/CustomizeView.swift @@ -0,0 +1,239 @@ +// +// CustomizeView.swift +// Feels (iOS) +// +// Created by Trey Tartt on 2/19/22. +// + +import SwiftUI + +struct CustomizeView: View { + @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.personalityPack.rawValue, store: GroupUserDefaults.groupDefaults) private var personalityPack: PersonalityPack = .Default + + @State private var showCreateCustomWidget = false + + let iconSets: [(String,String)] = [ + ("PurpleFeelsAppIcon", "PurpleAppIcon"), + ("RedFeelsAppIcon", "RedAppIcon") + ] + + var body: some View { + ScrollView { + VStack { + Text(String(localized: "customize_view_title")) + .font(.title) + .foregroundColor(theme.currentTheme.labelColor) + .padding([.trailing, .leading], 55) + .padding([.top], 15) + + createCustomWidget + changeIcon + themePicker + pickMoodImagePack + pickMoodTintPack + pickPeronsalityPack + } + } + .padding() + .sheet(isPresented: $showCreateCustomWidget) { + CreateWidgetView() + } + .background( + theme.currentTheme.bg + .edgesIgnoringSafeArea(.all) + ) + } + + private var changeIcon: some View { + ZStack { + theme.currentTheme.secondaryBGColor + VStack { + Text(String(localized: "settings_view_change_icon")) + HStack { + + Button(action: { + UIApplication.shared.setAlternateIconName(nil) + }, label: { + Image("FeelsAppIcon", bundle: .main) + .resizable() + .frame(width: 50, height:50) + .cornerRadius(10) + }) + .padding() + + ForEach(iconSets, id: \.self.0){ iconSet in + Button(action: { + UIApplication.shared.setAlternateIconName(iconSet.1) { (error) in + // FIXME: Handle error + } + }, label: { + Image(iconSet.0, bundle: .main) + .resizable() + .frame(width: 50, height:50) + .cornerRadius(10) + }) + .padding() + } + } + } + .padding() + } + .fixedSize(horizontal: false, vertical: true) + .cornerRadius(10, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) + } + + private var themePicker: some View { + ZStack { + theme.currentTheme.secondaryBGColor + VStack { + Text(String(localized: "settings_background_title")) + HStack { + Spacer() + ForEach(Theme.allCases, id:\.rawValue) { aTheme in + Button(action: { + theme = aTheme + }, label: { + VStack { + aTheme.currentTheme.preview + .overlay( + Circle() + .stroke(Color(UIColor.systemGray), style: StrokeStyle(lineWidth: 2)) + ) + Text(aTheme.title) + } + }) + Spacer() + } + } + .padding(.top) + } + .padding() + } + .fixedSize(horizontal: false, vertical: true) + .cornerRadius(10, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) + } + + private var createCustomWidget: some View { + ZStack { + theme.currentTheme.secondaryBGColor + Button(action: { + showCreateCustomWidget = true + }, label: { + Text("Create Custom Widget") + }) + .padding() + } + .fixedSize(horizontal: false, vertical: true) + .cornerRadius(10, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) + } + + private var pickMoodImagePack: some View { + ZStack { + theme.currentTheme.secondaryBGColor + VStack { + ForEach(MoodImages.allCases, id: \.rawValue) { images in + HStack { + ForEach(Mood.allValues, id: \.self) { mood in + images.icon(forMood: mood) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 35, height: 35) + .foregroundColor( + moodTint.color(forMood: mood) + ) + } + .frame(minWidth: 0, maxWidth: .infinity) + .onTapGesture { + let impactMed = UIImpactFeedbackGenerator(style: .heavy) + impactMed.impactOccurred() + imagePack = images + } + } + if images.rawValue != (MoodImages.allCases.sorted(by: { $0.rawValue > $1.rawValue }).first?.rawValue) ?? 0 { + Divider() + } + } + } + .padding() + } + .fixedSize(horizontal: false, vertical: true) + .cornerRadius(10, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) + } + + private var pickMoodTintPack: some View { + ZStack { + theme.currentTheme.secondaryBGColor + VStack { + ForEach(MoodTints.allCases, id: \.rawValue) { tint in + HStack { + ForEach(Mood.allValues, id: \.self) { mood in + Circle() + .frame(width: 35, height: 35) + .foregroundColor( + tint.color(forMood: mood) + ) + } + .frame(minWidth: 0, maxWidth: .infinity) + .onTapGesture { + let impactMed = UIImpactFeedbackGenerator(style: .heavy) + impactMed.impactOccurred() + moodTint = tint + } + } + if tint.rawValue != (MoodTints.allCases.sorted(by: { $0.rawValue > $1.rawValue }).first?.rawValue) ?? 0 { + Divider() + } + } + } + .padding() + } + .fixedSize(horizontal: false, vertical: true) + .cornerRadius(10, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) + } + + private var pickPeronsalityPack: some View { + ZStack { + theme.currentTheme.secondaryBGColor + VStack { + ForEach(PersonalityPack.allCases, id: \.self) { aPack in + VStack(spacing: 10) { + Text(String(aPack.title())) + .font(.title) + .fontWeight(.bold) + .foregroundColor(theme.currentTheme.labelColor) + + + Text(aPack.randomPushNotificationTitle()) + .font(.body) + .foregroundColor(Color(UIColor.systemGray)) + } + .frame(minWidth: 0, maxWidth: .infinity) + .padding() + .onTapGesture { + let impactMed = UIImpactFeedbackGenerator(style: .heavy) + impactMed.impactOccurred() + personalityPack = aPack + LocalNotification.rescheduleNotifiations() + + UITabBar.appearance().backgroundColor = UIColor(cgColor: theme.currentTheme.secondaryBGColor.cgColor ?? UIColor.secondarySystemBackground.cgColor) + } + if aPack.rawValue != (PersonalityPack.allCases.sorted(by: { $0.rawValue > $1.rawValue }).first?.rawValue) ?? 0 { + Divider() + } + } + + } + } + .fixedSize(horizontal: false, vertical: true) + .cornerRadius(10, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) + } +} + +struct CustomizeView_Previews: PreviewProvider { + static var previews: some View { + CustomizeView() + } +} diff --git a/Shared/Models/DayChartView.swift b/Shared/views/DayChartView.swift similarity index 100% rename from Shared/Models/DayChartView.swift rename to Shared/views/DayChartView.swift diff --git a/Shared/views/EmptyView.swift b/Shared/views/EmptyView.swift index 09ea525..eed7c93 100644 --- a/Shared/views/EmptyView.swift +++ b/Shared/views/EmptyView.swift @@ -10,21 +10,21 @@ import SwiftUI struct EmptyHomeView: View { @AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system - let viewModel: ContentModeViewModel + let viewModel: HomeViewViewModel var body: some View { ZStack { - Color(theme.currentTheme.secondaryBGColor) + theme.currentTheme.secondaryBGColor VStack { Text(String(localized: "content_view_empty_title")) .font(.title) - .foregroundColor(Color(UIColor.label)) + .foregroundColor(theme.currentTheme.labelColor) .padding() Text(String(localized: "content_view_empty_title")) .font(.body) - .foregroundColor(Color(UIColor.label)) + .foregroundColor(theme.currentTheme.labelColor) .padding() AddMoodHeaderView(addItemHeaderClosure: { (mood, date) in withAnimation { @@ -40,6 +40,6 @@ struct EmptyHomeView: View { struct EmptyHomeView_Previews: PreviewProvider { static var previews: some View { - EmptyHomeView(viewModel: ContentModeViewModel(addMonthStartWeekdayPadding: false)) + EmptyHomeView(viewModel: HomeViewViewModel(addMonthStartWeekdayPadding: false)) } } diff --git a/Shared/views/FilterView.swift b/Shared/views/FilterView/FilterView.swift similarity index 87% rename from Shared/views/FilterView.swift rename to Shared/views/FilterView/FilterView.swift index b648531..8237b9b 100644 --- a/Shared/views/FilterView.swift +++ b/Shared/views/FilterView/FilterView.swift @@ -21,7 +21,8 @@ struct FilterView: View { private var items: FetchedResults @AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system - + @AppStorage(UserDefaultsStore.Keys.moodTint.rawValue, store: GroupUserDefaults.groupDefaults) private var moodTint: MoodTints = .Default + @StateObject private var viewModel = FilterViewModel() //[ // 2001: [0: [], 1: [], 2: []], @@ -54,6 +55,7 @@ struct FilterView: View { Text(String(localized: "filter_view_total") + ": \(self.viewModel.numberOfRatings)") .font(.title2) + .foregroundColor(theme.currentTheme.labelColor) if showFilter { filterView @@ -80,13 +82,16 @@ struct FilterView: View { Text(showFilter ? String(localized: "filter_view_hide_filters") : String(localized: "filter_view_show_filters")) .frame(maxWidth: .infinity) .frame(height: 44) - .foregroundColor(Color(UIColor.label)) - .background(Color(theme.currentTheme.secondaryBGColor)) + .foregroundColor(theme.currentTheme.labelColor) + .background(theme.currentTheme.secondaryBGColor) .cornerRadius(10) }).frame(maxWidth: .infinity) } struct StatsSubView: View { + @AppStorage(UserDefaultsStore.Keys.moodTint.rawValue, store: GroupUserDefaults.groupDefaults) private var moodTint: MoodTints = .Default + @AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system + let data: [MoodEntry] let mood: Mood @@ -95,15 +100,16 @@ struct FilterView: View { Text(String(Stats.getCountFor(moodType: mood, inData: data))) .font(.title) + .foregroundColor(theme.currentTheme.labelColor) Text(mood.strValue) - .foregroundColor(mood.color) + .foregroundColor(moodTint.color(forMood: mood)) } } } private var statsView: some View { ZStack { - Color(theme.currentTheme.secondaryBGColor) + theme.currentTheme.secondaryBGColor HStack { Spacer() @@ -120,7 +126,7 @@ struct FilterView: View { VStack { VStack { ZStack { - Color(theme.currentTheme.secondaryBGColor) + theme.currentTheme.secondaryBGColor DatePicker( String(localized: "filter_view_begin_date"), selection: $viewModel.entryStartDate, @@ -133,9 +139,10 @@ struct FilterView: View { .frame(minWidth: 0, maxWidth: .infinity, minHeight: 44, maxHeight: 44) .cornerRadius(10) .padding([.leading, .trailing]) + .foregroundColor(theme.currentTheme.labelColor) ZStack { - Color(theme.currentTheme.secondaryBGColor) + theme.currentTheme.secondaryBGColor DatePicker( String(localized: "filter_view_end_date"), selection: $viewModel.entryEndDate, @@ -148,9 +155,10 @@ struct FilterView: View { .frame(minWidth: 0, maxWidth: .infinity, minHeight: 44, maxHeight: 44) .cornerRadius(10) .padding([.leading, .trailing]) + .foregroundColor(theme.currentTheme.labelColor) ZStack { - Color(theme.currentTheme.secondaryBGColor) + theme.currentTheme.secondaryBGColor HStack { Spacer() ForEach(weekdays.indices, id: \.self) { dayIdx in @@ -186,6 +194,7 @@ struct FilterView: View { ForEach(months, id: \.self.0) { item in Text(item.1) .textCase(.uppercase) + .foregroundColor(theme.currentTheme.labelColor) } }.padding([.leading, .trailing, .top]) } @@ -202,9 +211,10 @@ struct FilterView: View { let yearData = self.viewModel.data[yearKey]! Text(String(yearKey)) .font(.title) + .foregroundColor(theme.currentTheme.labelColor) yearGridView(yearData: yearData, columns: columns) .background( - Color(theme.currentTheme.secondaryBGColor) + theme.currentTheme.secondaryBGColor ) .cornerRadius(10) } diff --git a/Shared/Models/FilterViewModel.swift b/Shared/views/FilterView/FilterViewModel.swift similarity index 100% rename from Shared/Models/FilterViewModel.swift rename to Shared/views/FilterView/FilterViewModel.swift diff --git a/Shared/views/HeaderPercView.swift b/Shared/views/HeaderPercView.swift index e5f4493..16847ed 100644 --- a/Shared/views/HeaderPercView.swift +++ b/Shared/views/HeaderPercView.swift @@ -13,8 +13,9 @@ enum PercViewType { } struct HeaderPercView: View { - typealias model = (mood: Mood, total: Int, percent: Float) - var entries = [model]() + @AppStorage(UserDefaultsStore.Keys.moodTint.rawValue, store: GroupUserDefaults.groupDefaults) private var moodTint: MoodTints = .Default + + var entries = [MoodMetrics]() let backDays: Int let type: PercViewType @@ -32,44 +33,34 @@ struct HeaderPercView: View { moodEntries = PersistenceController.shared.getData(startDate: daysAgo, endDate: Date(), includedDays: [1,2,3,4,5,6,7]) } - let totalEntryCount = moodEntries?.count ?? 0 - if let moodEntries = moodEntries { - for (_, mood) in Mood.allValues.enumerated() { - - let moodEntries = moodEntries.filter({ - Int($0.moodValue) == mood.rawValue - }) - let total = moodEntries.count - let perc = (Float(total) / Float(totalEntryCount)) * 100 - entries.append((mood, total, perc)) - } + entries = Random.createTotalPerc(fromEntries: moodEntries) + + entries = entries.sorted(by: { + $0.mood.rawValue > $1.mood.rawValue + }) } - - entries = entries.sorted(by: { - $0.0.rawValue > $1.0.rawValue - }) } private var textViews: some View { VStack { Spacer() HStack { - ForEach(entries.prefix(3), id: \.0) { model in + ForEach(entries.prefix(3), id: \.id) { model in Text("\(model.percent, specifier: "%.0f")%") .font(.title) .fontWeight(.bold) - .foregroundColor(model.mood.color) + .foregroundColor(moodTint.color(forMood: model.mood)) .frame(maxWidth: .infinity) } } Spacer() HStack { - ForEach(entries.suffix(2), id: \.0) { model in + ForEach(entries.suffix(2), id: \.id) { model in Text("\(model.percent, specifier: "%.0f")%") .font(.title) .fontWeight(.bold) - .foregroundColor(model.mood.color) + .foregroundColor(moodTint.color(forMood: model.mood)) .frame(maxWidth: .infinity) } } @@ -81,27 +72,27 @@ struct HeaderPercView: View { VStack { Spacer() HStack { - ForEach(entries.prefix(3), id: \.0) { model in + ForEach(entries.prefix(3), id: \.id) { model in Text("\(model.percent, specifier: "%.0f")%") .font(.title2) .fontWeight(.bold) .multilineTextAlignment(.center) .padding() .frame(maxWidth: .infinity) - .background(Circle().fill(model.mood.color)) + .background(Circle().fill(moodTint.color(forMood: model.mood))) .foregroundColor(Color(UIColor.white)) } } Spacer() HStack { - ForEach(entries.suffix(2), id: \.0) { model in + ForEach(entries.suffix(2), id: \.id) { model in Text("\(model.percent, specifier: "%.0f")%") .font(.title2) .fontWeight(.bold) .multilineTextAlignment(.center) .padding() .frame(maxWidth: .infinity) - .background(Circle().fill(model.mood.color)) + .background(Circle().fill(moodTint.color(forMood: model.mood))) .foregroundColor(Color(UIColor.white)) } } diff --git a/Shared/views/HeaderStatsView.swift b/Shared/views/HeaderStatsView.swift index ca296b1..841ec0a 100644 --- a/Shared/views/HeaderStatsView.swift +++ b/Shared/views/HeaderStatsView.swift @@ -11,8 +11,10 @@ import Charts struct HeaderStatsView : UIViewRepresentable { //Bar chart accepts data as array of BarChartDataEntry objects var entries : [BarChartDataEntry] + var moodTint: MoodTints - init(fakeData: Bool, backDays: Int) { + init(fakeData: Bool, backDays: Int, moodTint: MoodTints) { + self.moodTint = moodTint entries = [BarChartDataEntry]() var moodEntries: [MoodEntry]? @@ -96,7 +98,7 @@ struct HeaderStatsView : UIViewRepresentable { let dataSet = BarChartDataSet(entries: entries) // change bars color to green - dataSet.colors = Mood.allValues.map({ NSUIColor( $0.color ) }) + dataSet.colors = Mood.allValues.map({ NSUIColor( moodTint.color(forMood: $0) ) }) dataSet.secondaryTextColor = UIColor.systemGray dataSet.valueColors = [.white] dataSet.highlightAlpha = 0.0 @@ -120,6 +122,6 @@ struct HeaderStatsView : UIViewRepresentable { struct HeaderStatsView_Previews: PreviewProvider { static var previews: some View { - HeaderStatsView(fakeData: true, backDays: 30).frame(minHeight: 85, maxHeight: 90) + HeaderStatsView(fakeData: true, backDays: 30, moodTint: .Default).frame(minHeight: 85, maxHeight: 90) } } diff --git a/Shared/views/HomeView.swift b/Shared/views/HomeView/HomeView.swift similarity index 91% rename from Shared/views/HomeView.swift rename to Shared/views/HomeView/HomeView.swift index 8413af9..bf6798d 100644 --- a/Shared/views/HomeView.swift +++ b/Shared/views/HomeView/HomeView.swift @@ -20,7 +20,10 @@ struct HomeView: View { @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 + // MARK: top header storage @AppStorage(UserDefaultsStore.Keys.contentViewCurrentSelectedHeaderViewBackDays.rawValue, store: GroupUserDefaults.groupDefaults) private var currentSelectedHeaderViewBackDays: Int = 30 @AppStorage(UserDefaultsStore.Keys.contentViewHeaderTagViewOneViewType.rawValue, store: GroupUserDefaults.groupDefaults) private var firstSwichableHeaderViewType: MainSwitchableViewType = .total @@ -37,6 +40,7 @@ struct HomeView: View { // MARK: ?? properties @State private var showTodayInput = true @State private var showUpdateEntryAlert = false + @StateObject private var onboardingData = OnboardingDataDataManager.shared // MARK: header properties @State private var headerHeight: CGFloat = HomeViewConstants.maxHeaderHeight @@ -44,17 +48,11 @@ struct HomeView: View { @State private var headerOpacity: Double = 1.0 // - @ObservedObject var viewModel = ContentModeViewModel(addMonthStartWeekdayPadding: false) - - init(){ - UIPageControl.appearance().currentPageIndicatorTintColor = UIColor.label - UIPageControl.appearance().pageIndicatorTintColor = UIColor.systemGray - UITabBar.appearance().backgroundColor = UIColor.secondarySystemBackground - } - + @ObservedObject var viewModel = HomeViewViewModel(addMonthStartWeekdayPadding: false) + var body: some View { mainView - .alert(ContentModeViewModel.updateTitleHeader(forEntry: selectedEntry), + .alert(HomeViewViewModel.updateTitleHeader(forEntry: selectedEntry), isPresented: $showUpdateEntryAlert) { ForEach(Mood.allValues) { mood in Button(mood.strValue, action: { @@ -130,11 +128,12 @@ struct HomeView: View { VStack { SmallRollUpHeaderView(entries: getBackEntries(), viewType: $currentSelectedHeaderViewViewType) + .frame(height: HomeViewConstants.minHeaderHeight) + .padding([.trailing, .leading]) .background( - Color(theme.currentTheme.secondaryBGColor) + theme.currentTheme.secondaryBGColor ) .cornerRadius(10, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) - .padding([.top, .bottom], 5) Spacer() } @@ -173,8 +172,6 @@ struct HomeView: View { withAnimation{ viewModel.updateData() } - }, updateBoardingDataClosure: { onboardingData in - OnboardingDataDataManager.shared.updateOnboardingData(onboardingData: onboardingData) }) }.padding(.trailing) } @@ -182,7 +179,7 @@ struct HomeView: View { private var headerView: some View { VStack { - if ShowBasedOnVoteLogics.isMissingCurrentVote() { + if ShowBasedOnVoteLogics.isMissingCurrentVote(onboardingData: onboardingData.savedOnboardingData) { AddMoodHeaderView(addItemHeaderClosure: { (mood, date) in withAnimation { viewModel.add(mood: mood, forDate: date, entryType: .header) @@ -255,7 +252,7 @@ struct HomeView: View { ) } .background( - Color(theme.currentTheme.secondaryBGColor) + theme.currentTheme.secondaryBGColor ) .coordinateSpace(name: "scroll") .onPreferenceChange(ViewOffsetKey.self) { value in @@ -281,11 +278,11 @@ extension HomeView { private func SectionHeaderView(month: Int, year: Int) -> some View { Text("\(Random.monthName(fromMonthInt: month)) \(String(year))") .font(.title) - .foregroundColor(Color(UIColor.label)) + .foregroundColor(theme.currentTheme.labelColor) .frame(maxWidth: .infinity, alignment: .leading) .padding() .background( - Color(theme.currentTheme.secondaryBGColor) + theme.currentTheme.secondaryBGColor ) } @@ -307,23 +304,23 @@ extension HomeView { private func entryListView(entry: MoodEntry) -> some View { HStack { - entry.mood.icon + imagePack.icon(forMood: entry.mood) .resizable() .aspectRatio(contentMode: .fit) .frame(width: 40, height: 40, alignment: .center) - .foregroundColor(entry.mood.color) + .foregroundColor(moodTint.color(forMood: entry.mood)) .padding(.leading, 5) VStack { HStack { Text(Random.weekdayName(fromDate:entry.forDate!)) .font(.title3) - .foregroundColor(Color(UIColor.label)) + .foregroundColor(theme.currentTheme.labelColor) Text(" - ") .padding([.leading, .trailing], -10) Text(Random.dayFormat(fromDate:entry.forDate!)) .font(.title3) - .foregroundColor(Color(UIColor.label)) + .foregroundColor(theme.currentTheme.labelColor) Spacer() } .multilineTextAlignment(.leading) diff --git a/Shared/views/HomeView/HomeViewTwo/HomeViewTwo.swift b/Shared/views/HomeView/HomeViewTwo/HomeViewTwo.swift new file mode 100644 index 0000000..03fb7bd --- /dev/null +++ b/Shared/views/HomeView/HomeViewTwo/HomeViewTwo.swift @@ -0,0 +1,170 @@ +// +// HomeViewTwo.swift +// Feels (iOS) +// +// Created by Trey Tartt on 2/18/22. +// + +import SwiftUI + +struct HomeViewTwo: View { + @AppStorage(UserDefaultsStore.Keys.needsOnboarding.rawValue, store: GroupUserDefaults.groupDefaults) private var needsOnboarding = true + + @AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system + @AppStorage(UserDefaultsStore.Keys.moodTint.rawValue, store: GroupUserDefaults.groupDefaults) private var moodTint: MoodTints = .Default + + @ObservedObject var viewModel = HomeViewViewModel(addMonthStartWeekdayPadding: true) + + @StateObject private var selectedDetail = StupidAssDetailViewObservableObject() + @State private var showingSheet = false + @StateObject private var onboardingData = OnboardingDataDataManager.shared + + class StupidAssDetailViewObservableObject: ObservableObject { + @Published var fuckingWrapped: MonthDetailView? = nil + @Published var showFuckingSheet = false + } + + let columns = [ + GridItem(.flexible(minimum: 5, maximum: 400)), + GridItem(.flexible(minimum: 5, maximum: 400)), + GridItem(.flexible(minimum: 5, maximum: 400)), + GridItem(.flexible(minimum: 5, maximum: 400)), + GridItem(.flexible(minimum: 5, maximum: 400)), + GridItem(.flexible(minimum: 5, maximum: 400)), + GridItem(.flexible(minimum: 5, maximum: 400)) + ] + + var body: some View { + ZStack { + if viewModel.hasNoData { + settingsButtonView + VStack { + Spacer() + EmptyHomeView(viewModel: viewModel) + .padding() + Spacer() + } + } else { + ScrollView { + ZStack { + topView + .frame(maxWidth: .infinity, alignment: .leading) + .frame(height: 250) + .background( + .blue + ) + settingsButtonView + } + 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() { + homeViewTwoMonthListView(month: month, year: year, entries: entries) + } + } + .padding(.bottom) + } + .padding() + .background( + RoundedRectangle(cornerRadius: 10) + .foregroundColor( + theme.currentTheme.secondaryBGColor + ) + ) + } + .padding([.leading, .trailing]) + }.sheet(isPresented: $selectedDetail.showFuckingSheet, + onDismiss: didDismiss) { + selectedDetail.fuckingWrapped + } + .edgesIgnoringSafeArea(.top) + } + } + .background( + theme.currentTheme.bg + .edgesIgnoringSafeArea(.all) + ) + } + + + func didDismiss() { + selectedDetail.showFuckingSheet = false + selectedDetail.fuckingWrapped = nil + } +} + +extension HomeViewTwo { + private var topView: some View { + VStack { + if ShowBasedOnVoteLogics.isMissingCurrentVote(onboardingData: onboardingData.savedOnboardingData) { + Text("Vote") + } + Text("dis top") + } + } + + private var settingsButtonView: some View { + HStack { + Spacer() + VStack { + Button(action: { + showingSheet.toggle() + }, label: { + Image(systemName: "gear") + .foregroundColor(Color(UIColor.darkGray)) + .font(.system(size: 20)) + }).sheet(isPresented: $showingSheet) { + SettingsView(editedDataClosure: { + withAnimation{ + viewModel.updateData() + } + }) + } + .padding(.top, 60) + .padding(.trailing) + Spacer() + } + } + } +} + +// view that make up the list body +extension HomeViewTwo { + private func homeViewTwoSectionHeaderView(month: Int, year: Int) -> some View { + Text("\(Random.monthName(fromMonthInt: month)) \(String(year))") + .font(.body) + .foregroundColor(theme.currentTheme.labelColor) + .frame(maxWidth: .infinity, alignment: .leading) + } + + private func homeViewTwoMonthListView(month: Int, year: Int, entries: [MoodEntry]) -> some View { + VStack { + homeViewTwoSectionHeaderView(month: month, year: year) + Divider() + LazyVGrid(columns: columns, spacing: 15) { + ForEach(entries, id: \.self) { entry in + Circle() + .foregroundColor(moodTint.color(forMood: entry.mood)) + .frame(minHeight: 5, idealHeight: 20, maxHeight: 50, alignment: .center) + } + } + } + .onTapGesture{ + let deailView = MonthDetailView(monthInt: month, + yearInt: year, + entries: entries, + parentViewModel: viewModel) + + selectedDetail.fuckingWrapped = deailView + selectedDetail.showFuckingSheet = true + } + } +} + +struct HomeViewTwo_Previews: PreviewProvider { + static var previews: some View { + HomeViewTwo() + } +} diff --git a/Shared/views/HomeView/HomeViewTwo/MonthDetailView.swift b/Shared/views/HomeView/HomeViewTwo/MonthDetailView.swift new file mode 100644 index 0000000..90eba9f --- /dev/null +++ b/Shared/views/HomeView/HomeViewTwo/MonthDetailView.swift @@ -0,0 +1,171 @@ +// +// MonthDetailView.swift +// Feels (iOS) +// +// Created by Trey Tartt on 2/18/22. +// + +import SwiftUI + +struct MonthDetailView: View { + @AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system + @AppStorage(UserDefaultsStore.Keys.deleteEnable.rawValue, store: GroupUserDefaults.groupDefaults) private var deleteEnabled = true + @AppStorage(UserDefaultsStore.Keys.moodTint.rawValue, store: GroupUserDefaults.groupDefaults) private var moodTint: MoodTints = .Default + + @State private var showingSheet = false + @State private var selectedEntry: MoodEntry? + @State private var showingUpdateEntryAlert = false + @State private var showUpdateEntryAlert = false + + let monthInt: Int + let yearInt: Int + @State var entries: [MoodEntry] + var parentViewModel: HomeViewViewModel + + let columns = [ + GridItem(.flexible(minimum: 5, maximum: 50)), + GridItem(.flexible(minimum: 5, maximum: 50)), + GridItem(.flexible(minimum: 5, maximum: 50)), + GridItem(.flexible(minimum: 5, maximum: 50)), + GridItem(.flexible(minimum: 5, maximum: 50)), + GridItem(.flexible(minimum: 5, maximum: 50)), + GridItem(.flexible(minimum: 5, maximum: 50)) + ] + + var body: some View { + VStack { + Text("\(Random.monthName(fromMonthInt: monthInt)) \(String(yearInt))") + .font(.title) + .foregroundColor(theme.currentTheme.labelColor) + .frame(maxWidth: .infinity, alignment: .leading) + .padding() + .background( + theme.currentTheme.secondaryBGColor + ) + + createListView() + .padding([.leading, .trailing]) + .background( + theme.currentTheme.bg + ) + + monthDetails + .frame(maxWidth: .infinity, alignment: .leading) + .background( + theme.currentTheme.secondaryBGColor + ) + } + .alert(HomeViewViewModel.updateTitleHeader(forEntry: selectedEntry), + isPresented: $showUpdateEntryAlert) { + ForEach(Mood.allValues) { mood in + Button(mood.strValue, action: { + if let selectedEntry = selectedEntry { + PersistenceController.shared.update(entryDate: selectedEntry.forDate!, withModd: mood) + } + updateEntries() + showUpdateEntryAlert = false + selectedEntry = nil + }) + } + + if let selectedEntry = selectedEntry, + deleteEnabled, + selectedEntry.mood != .missing { + Button(String(localized: "content_view_delete_entry"), action: { + updateEntries() + PersistenceController.shared.update(entryDate: selectedEntry.forDate!, withModd: .missing) + showUpdateEntryAlert = false + }) + } + + Button(String(localized: "content_view_fill_in_missing_entry_cancel"), role: .cancel, action: { + updateEntries() + selectedEntry = nil + showUpdateEntryAlert = false + }) + } + .background( + theme.currentTheme.bg + ) + } + + private func createListView() -> some View { + ScrollView { + LazyVGrid(columns: columns, spacing: 25) { + ForEach(entries, id: \.self) { entry in + listViewEntry(forEntry: entry) + .onTapGesture(perform: { + if entry.canEdit { + selectedEntry = entry + showUpdateEntryAlert = true + } + }) + } + } + } + } + + private func listViewEntry(forEntry entry: MoodEntry) -> some View { + VStack { + if entry.mood == .placeholder { + Text(" ") + .font(.title3) + .foregroundColor(Mood.placeholder.color) + } else { + Text(entry.forDate!, + format: Date.FormatStyle().day()) + .font(.title3) + .foregroundColor(theme.currentTheme.labelColor) + } + + if entry.mood == .placeholder { + Circle() + .frame(minWidth: 5, + maxWidth: 50, + minHeight: 5, + maxHeight: 50, + alignment: .center) + .foregroundColor(moodTint.color(forMood: entry.mood)) + } else { + entry.mood.icon + .resizable() + .aspectRatio(contentMode: .fit) + .frame(minWidth: 5, + maxWidth: 50, + minHeight: 5, + maxHeight: 50, + alignment: .center) + .foregroundColor(moodTint.color(forMood: entry.mood)) + } + } + } + + private func updateEntries() { + parentViewModel.updateData() + let (startDate, endDate) = Date.dateRange(monthInt: monthInt, yearInt: yearInt) + let updatedEntries = PersistenceController.shared.getData(startDate: startDate, endDate: endDate, includedDays: [1,2,3,4,5,6,7]) + let padded = MoodEntryFunctions.padMoodEntriesMonth(monthEntries: updatedEntries) + entries = padded + } + + private var monthDetails: some View { + VStack { + SmallRollUpHeaderView(entries: entries, + viewType: .constant(.total)) + + SmallRollUpHeaderView(entries: entries, + viewType: .constant(.percentageCircle)) + .padding(.top, -20) + } + .padding() + } +} + +struct MonthDetailView_Previews: PreviewProvider { + static var previews: some View { + MonthDetailView(monthInt: 5, yearInt: 2022, entries: + PersistenceController.shared.randomEntries(count: 30).sorted(by: { + $0.forDate! < $1.forDate! + }), parentViewModel: HomeViewViewModel(addMonthStartWeekdayPadding: true)) + } +} diff --git a/Shared/Models/ContentModeViewModel.swift b/Shared/views/HomeView/HomeViewViewModel.swift similarity index 76% rename from Shared/Models/ContentModeViewModel.swift rename to Shared/views/HomeView/HomeViewViewModel.swift index 0bfb77c..f29fc64 100644 --- a/Shared/Models/ContentModeViewModel.swift +++ b/Shared/views/HomeView/HomeViewViewModel.swift @@ -8,7 +8,7 @@ import SwiftUI import CoreData -class ContentModeViewModel: ObservableObject { +class HomeViewViewModel: ObservableObject { @Published var grouped = [Int: [Int: [MoodEntry]]]() @Published var numberOfItems = 0 @@ -50,33 +50,7 @@ class ContentModeViewModel: ObservableObject { grouped = PersistenceController.shared.splitIntoYearMonth() if addMonthStartWeekdayPadding { - var newGrouped = [Int: [Int: [MoodEntry]]]() - - let allYears = grouped.keys.sorted(by: > ) - for year in allYears { - var newMonth = [Int: [MoodEntry]]() - - let oldMonths = grouped[year]! - let monthKeys = oldMonths.keys.sorted(by: > ) - for key in monthKeys { - if let entries = oldMonths[key] { - let sortedEntries = entries.sorted(by: { $0.forDate! < $1.forDate! }) - var mutableEntries = sortedEntries - - if let firstDate = sortedEntries.first { - let date = firstDate.forDate! - let weekday = Int16(Calendar.current.component(.weekday, from: date)) - for _ in 1.. $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: homeViewTwoSectionHeaderView(month: month, year: year)) { - homeViewTwoMonthListView(month: month, year: year, entries: entries) - } - .onTapGesture{ - let deailView = MonthDetailView(monthInt: month, - yearInt: year, - entries: entries) - - selectedDetail.fuckingWrapped = deailView - selectedDetail.showFuckingSheet = true - } - } - .padding(.bottom) - } - } - .padding([.leading, .trailing]) - }.sheet(isPresented: $selectedDetail.showFuckingSheet, - onDismiss: didDismiss) { - selectedDetail.fuckingWrapped - } - } - - func didDismiss() { - selectedDetail.showFuckingSheet = false - selectedDetail.fuckingWrapped = nil - } -} - -extension HomeViewTwo { - private var topView: some View { - HStack { - Text("dis top") - } - } -} - -// view that make up the list body -extension HomeViewTwo { - private func homeViewTwoSectionHeaderView(month: Int, year: Int) -> some View { - Text("\(Random.monthName(fromMonthInt: month)) \(String(year))") - .font(.body) - .foregroundColor(Color(UIColor.label)) - .frame(maxWidth: .infinity, alignment: .leading) - .background( - Color(theme.currentTheme.secondaryBGColor) - ) - } - - private func homeViewTwoMonthListView(month: Int, year: Int, entries: [MoodEntry]) -> some View { - LazyVGrid(columns: columns, spacing: 15) { - ForEach(entries, id: \.self) { entry in - Circle() - .foregroundColor(entry.mood.color) - .frame(minHeight: 5, idealHeight: 20, maxHeight: 50, alignment: .center) - } - } - } -} - -struct HomeViewTwo_Previews: PreviewProvider { - static var previews: some View { - HomeViewTwo() - } -} diff --git a/Shared/views/HomeViewTwo/MonthDetailView.swift b/Shared/views/HomeViewTwo/MonthDetailView.swift deleted file mode 100644 index 656e173..0000000 --- a/Shared/views/HomeViewTwo/MonthDetailView.swift +++ /dev/null @@ -1,122 +0,0 @@ -// -// MonthDetailView.swift -// Feels (iOS) -// -// Created by Trey Tartt on 2/18/22. -// - -import SwiftUI - -struct MonthDetailView: View { - @AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system - @AppStorage(UserDefaultsStore.Keys.deleteEnable.rawValue, store: GroupUserDefaults.groupDefaults) private var deleteEnabled = true - - @State private var showingUpdateEntryAlert = false - - let monthInt: Int - let yearInt: Int - let entries: [MoodEntry] - - lazy var dateFormatter: DateFormatter = { - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "dd" - return dateFormatter - }() - - let columns = [ - GridItem(.flexible(minimum: 5, maximum: 50)), - GridItem(.flexible(minimum: 5, maximum: 50)), - GridItem(.flexible(minimum: 5, maximum: 50)), - GridItem(.flexible(minimum: 5, maximum: 50)), - GridItem(.flexible(minimum: 5, maximum: 50)), - GridItem(.flexible(minimum: 5, maximum: 50)), - GridItem(.flexible(minimum: 5, maximum: 50)) - ] - - var body: some View { - VStack { - Text("\(Random.monthName(fromMonthInt: monthInt)) \(String(yearInt))") - .font(.title) - .foregroundColor(Color(UIColor.label)) - .frame(maxWidth: .infinity, alignment: .leading) - .padding() - .background( - Color(theme.currentTheme.secondaryBGColor) - ) - - ScrollView { - LazyVGrid(columns: columns, spacing: 25) { - ForEach(entries, id: \.self) { entry in - VStack { - if entry.mood != .placeholder { - Text(entry.forDate!, - format: Date.FormatStyle().day()) - .font(.title3) - .foregroundColor(Color(UIColor.label)) - - entry.mood.icon - .resizable() - .frame(minWidth: 5, - maxWidth: 50, - minHeight: 5, - maxHeight: 50, - alignment: .center) - .aspectRatio(contentMode: .fit) - .foregroundColor(entry.mood.color) - } - } - .alert(ContentModeViewModel.updateTitleHeader(forEntry: entry), - isPresented: $showingUpdateEntryAlert) { - ForEach(Mood.allValues) { mood in - Button(mood.strValue, action: { - PersistenceController.shared.update(entryDate: entry.forDate!, withModd: mood) -// viewModel.update(entry: selectedEntry, toMood: mood) - }) - } - - if deleteEnabled, - entry.mood != .missing { - Button(String(localized: "content_view_delete_entry"), action: { - PersistenceController.shared.update(entryDate: entry.forDate!, withModd: Mood.missing) -// viewModel.update(entry: selectedEntry, toMood: Mood.missing) - }) - } - - Button(String(localized: "content_view_fill_in_missing_entry_cancel"), role: .cancel, action: { - - }) - } - } - } - } - .padding([.leading, .trailing]) - - monthDetails - .frame(maxWidth: .infinity, alignment: .leading) - .background( - Color(theme.currentTheme.secondaryBGColor) - ) - } - } - - private var monthDetails: some View { - VStack { - SmallRollUpHeaderView(entries: entries, - viewType: .constant(.total)) - - SmallRollUpHeaderView(entries: entries, - viewType: .constant(.percentageCircle)) - .padding(.top, -20) - } - .padding() - } -} - -struct MonthDetailView_Previews: PreviewProvider { - static var previews: some View { - MonthDetailView(monthInt: 5, yearInt: 2022, entries: - PersistenceController.shared.randomEntries(count: 30).sorted(by: { - $0.forDate! < $1.forDate! - })) - } -} diff --git a/Shared/views/MainTabView.swift b/Shared/views/MainTabView.swift index 19fd8bc..f52dd92 100644 --- a/Shared/views/MainTabView.swift +++ b/Shared/views/MainTabView.swift @@ -16,7 +16,7 @@ struct MainTabView: View { TabView { HomeViewTwo() .tabItem { - Label(String(localized: "content_view_tab_main"), systemImage: "list.dash") + Label(String(localized: "content_view_tab_main"), systemImage: "calendar") } HomeView() @@ -26,13 +26,18 @@ struct MainTabView: View { FilterView() .tabItem { - Label(String(localized: "content_view_tab_filter"), systemImage: "calendar.circle") + Label(String(localized: "content_view_tab_filter"), systemImage: "line.3.horizontal.decrease.circle") } SharingListView() .tabItem { Label(String(localized: "content_view_tab_share"), systemImage: "square.and.arrow.up") } + + CustomizeView() + .tabItem { + Label(String(localized: "content_view_tab_customize"), systemImage: "pencil") + } }.sheet(isPresented: $needsOnboarding, onDismiss: { }, content: { diff --git a/Shared/views/SettingsView.swift b/Shared/views/SettingsView/SettingsView.swift similarity index 62% rename from Shared/views/SettingsView.swift rename to Shared/views/SettingsView/SettingsView.swift index 82cadd5..f875461 100644 --- a/Shared/views/SettingsView.swift +++ b/Shared/views/SettingsView/SettingsView.swift @@ -12,13 +12,11 @@ struct SettingsView: View { @Environment(\.dismiss) var dismiss let editedDataClosure: (() -> Void) - let updateBoardingDataClosure: ((OnboardingData) -> Void) @State private var showOnboarding = false @State private var showSpecialThanks = false @State private var showWhyBGMode = false - @State private var showCreateCustomWidget = false @ObservedObject var syncMonitor = SyncMonitor.shared @AppStorage(UserDefaultsStore.Keys.useCloudKit.rawValue, store: GroupUserDefaults.groupDefaults) private var useCloudKit = false @@ -34,9 +32,6 @@ struct SettingsView: View { cloudKitEnable canDelete - changeIcon - themePicker - createCustomWidget showOnboardingButton whyBackgroundMode specialThanksCell @@ -56,13 +51,10 @@ struct SettingsView: View { }.sheet(isPresented: $showOnboarding) { OnboardingMain(onboardingData: UserDefaultsStore.getOnboarding(), updateBoardingDataClosure: { onboardingData in - updateBoardingDataClosure(onboardingData) + OnboardingDataDataManager.shared.updateOnboardingData(onboardingData: onboardingData) showOnboarding = false }) } - .sheet(isPresented: $showCreateCustomWidget) { - CreateIconView() - } .background( theme.currentTheme.bg .edgesIgnoringSafeArea(.all) @@ -84,7 +76,7 @@ struct SettingsView: View { private var specialThanksCell: some View { ZStack { - Color(theme.currentTheme.secondaryBGColor) + theme.currentTheme.secondaryBGColor VStack { Button(action: { withAnimation{ @@ -107,7 +99,7 @@ struct SettingsView: View { private var addTestDataCell: some View { ZStack { - Color(theme.currentTheme.secondaryBGColor) + theme.currentTheme.secondaryBGColor Button(action: { PersistenceController.shared.populateTestData() editedDataClosure() @@ -122,7 +114,7 @@ struct SettingsView: View { private var clearDB: some View { ZStack { - Color(theme.currentTheme.secondaryBGColor) + theme.currentTheme.secondaryBGColor Button(action: { PersistenceController.shared.clearDB() editedDataClosure() @@ -137,7 +129,7 @@ struct SettingsView: View { private var whyBackgroundMode: some View { ZStack { - Color(theme.currentTheme.secondaryBGColor) + theme.currentTheme.secondaryBGColor VStack { Button(action: { withAnimation{ @@ -157,51 +149,13 @@ struct SettingsView: View { .cornerRadius(10, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) } - let iconSets: [(String,String)] = [ - ("PurpleFeelsAppIcon", "PurpleAppIcon"), - ("RedFeelsAppIcon", "RedAppIcon") - ] + + - private var changeIcon: some View { - ZStack { - Color(theme.currentTheme.secondaryBGColor) - VStack { - Text(String(localized: "settings_view_change_icon")) - HStack { - - Button(action: { - UIApplication.shared.setAlternateIconName(nil) - }, label: { - Image("FeelsAppIcon", bundle: .main) - .resizable() - .frame(width: 50, height:50) - .cornerRadius(10) - }) - .padding() - - ForEach(iconSets, id: \.self.0){ iconSet in - Button(action: { - UIApplication.shared.setAlternateIconName(iconSet.1) { (error) in - // FIXME: Handle error - } - }, label: { - Image(iconSet.0, bundle: .main) - .resizable() - .frame(width: 50, height:50) - .cornerRadius(10) - }) - .padding() - } - } - } - } - .fixedSize(horizontal: false, vertical: true) - .cornerRadius(10, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) - } private var showOnboardingButton: some View { ZStack { - Color(theme.currentTheme.secondaryBGColor) + theme.currentTheme.secondaryBGColor Button(action: { showOnboarding.toggle() }, label: { @@ -215,7 +169,7 @@ struct SettingsView: View { private var cloudKitEnable: some View { ZStack { - Color(theme.currentTheme.secondaryBGColor) + theme.currentTheme.secondaryBGColor VStack { Toggle(String(localized: "settings_use_cloudkit_title"), isOn: $useCloudKit) @@ -233,7 +187,7 @@ struct SettingsView: View { private var cloudKitStatus: some View { ZStack { - Color(theme.currentTheme.secondaryBGColor) + theme.currentTheme.secondaryBGColor VStack { Image(systemName: syncMonitor.syncStateSummary.symbolName) .foregroundColor(syncMonitor.syncStateSummary.symbolColor) @@ -247,7 +201,7 @@ struct SettingsView: View { private var canDelete: some View { ZStack { - Color(theme.currentTheme.secondaryBGColor) + theme.currentTheme.secondaryBGColor VStack { Toggle(String(localized: "settings_use_delete_enable"), isOn: $deleteEnabled) @@ -257,65 +211,16 @@ struct SettingsView: View { .fixedSize(horizontal: false, vertical: true) .cornerRadius(10, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) } - - private var themePicker: some View { - ZStack { - Color(theme.currentTheme.secondaryBGColor) - VStack { - Text(String(localized: "settings_background_title")) - HStack { - Spacer() - ForEach(Theme.allCases, id:\.rawValue) { aTheme in - Button(action: { - theme = aTheme - }, label: { - VStack { - aTheme.currentTheme.preview - .overlay( - Circle() - .stroke(Color(UIColor.systemGray), style: StrokeStyle(lineWidth: 2)) - ) - Text(aTheme.title) - } - }) - Spacer() - } - } - .padding(.top) - } - .padding() - } - .fixedSize(horizontal: false, vertical: true) - .cornerRadius(10, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) - } - - private var createCustomWidget: some View { - ZStack { - Color(theme.currentTheme.secondaryBGColor) - Button(action: { - showCreateCustomWidget = true - }, label: { - Text("Create Custom Widget") - }) - .padding() - } - .fixedSize(horizontal: false, vertical: true) - .cornerRadius(10, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) - } } struct SettingsView_Previews: PreviewProvider { static var previews: some View { SettingsView(editedDataClosure: { - }, updateBoardingDataClosure: { _ in - }) SettingsView(editedDataClosure: { - }, updateBoardingDataClosure: { _ in - }) .preferredColorScheme(.dark) } diff --git a/Shared/views/SharingListView.swift b/Shared/views/Sharing/SharingListView.swift similarity index 93% rename from Shared/views/SharingListView.swift rename to Shared/views/Sharing/SharingListView.swift index 6363886..d58b37b 100644 --- a/Shared/views/SharingListView.swift +++ b/Shared/views/Sharing/SharingListView.swift @@ -71,13 +71,13 @@ struct SharingListView: View { ////////////////////////////////////////////////////////// WrappedSharable(preview: AnyView( MonthTotalTemplate(isPreview: true, - startDate: Date().startOfMonth(), - endDate: Date().endOfMonth(), + startDate: Date().startOfMonth, + endDate: Date().endOfMonth, fakeData: false) ), destination: AnyView( MonthTotalTemplate(isPreview: false, - startDate: Date().startOfMonth(), - endDate: Date().endOfMonth(), + startDate: Date().startOfMonth, + endDate: Date().endOfMonth, fakeData: false) ), description: MonthTotalTemplate.description), ////////////////////////////////////////////////////////// @@ -93,7 +93,7 @@ struct SharingListView: View { Text(String(format: String(localized: "Share your shit"))) .font(.title) .fontWeight(.bold) - .foregroundColor(Color(UIColor.label)) + .foregroundColor(theme.currentTheme.labelColor) .padding([.top, .leading]) .frame(maxWidth: .infinity, alignment: .leading) @@ -104,7 +104,7 @@ struct SharingListView: View { selectedShare.showFuckingSheet = true }, label: { ZStack { - Color(theme.currentTheme.secondaryBGColor) + theme.currentTheme.secondaryBGColor item.preview .frame(height: 88) @@ -113,7 +113,7 @@ struct SharingListView: View { Spacer() Text(item.description) .font(.title) - .foregroundColor(Color(UIColor.label)) + .foregroundColor(theme.currentTheme.labelColor) .fontWeight(.bold) .frame(minWidth: 0, maxWidth: .infinity) .frame(height: 44) diff --git a/Shared/views/SharingTemplates/AllMoodsTotalTemplate.swift b/Shared/views/SharingTemplates/AllMoodsTotalTemplate.swift index 9322f64..0336100 100644 --- a/Shared/views/SharingTemplates/AllMoodsTotalTemplate.swift +++ b/Shared/views/SharingTemplates/AllMoodsTotalTemplate.swift @@ -19,6 +19,8 @@ struct AllMoodsTotalTemplate: View, SharingTemplate { @State var showSharingTemplate = false @Environment(\.presentationMode) var presentationMode + @AppStorage(UserDefaultsStore.Keys.moodTint.rawValue, store: GroupUserDefaults.groupDefaults) private var moodTint: MoodTints = .Default + @AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system private var entries = [MoodMetrics]() @@ -41,16 +43,7 @@ struct AllMoodsTotalTemplate: View, SharingTemplate { totalEntryCount = moodEntries?.count ?? 0 if let moodEntries = moodEntries { - for (_, mood) in Mood.allValues.enumerated() { - - let moodEntries = moodEntries.filter({ - Int($0.moodValue) == mood.rawValue - }) - let total = moodEntries.count - let perc = (Float(total) / Float(totalEntryCount)) * 100 - entries.append(MoodMetrics(mood: mood, total: total, percent: perc)) - } - + entries = Random.createTotalPerc(fromEntries: moodEntries) entries = entries.sorted(by: { $0.percent > $1.percent }) @@ -78,7 +71,7 @@ struct AllMoodsTotalTemplate: View, SharingTemplate { mood.icon .resizable() .aspectRatio(contentMode: .fit) - .foregroundColor(mood.color) + .foregroundColor(moodTint.color(forMood: mood)) } } @@ -86,7 +79,7 @@ struct AllMoodsTotalTemplate: View, SharingTemplate { HStack { ForEach(entries.prefix(2), id: \.mood) { model in ZStack { - Circle().fill(model.mood.color) + Circle().fill(moodTint.color(forMood: model.mood)) Text("\(model.percent, specifier: "%.0f")%") .font(.title) @@ -104,7 +97,7 @@ struct AllMoodsTotalTemplate: View, SharingTemplate { HStack { ForEach(entries.suffix(3), id: \.mood) { model in ZStack { - Circle().fill(model.mood.color) + Circle().fill(moodTint.color(forMood: model.mood)) Text("\(model.percent, specifier: "%.0f")%") .font(.title) @@ -124,7 +117,7 @@ struct AllMoodsTotalTemplate: View, SharingTemplate { HStack { ForEach(entries, id: \.mood) { model in ZStack { - Circle().fill(model.mood.color) + Circle().fill(moodTint.color(forMood: model.mood)) Text("\(model.total)") .font(.title) @@ -144,7 +137,7 @@ struct AllMoodsTotalTemplate: View, SharingTemplate { VStack { Text(String(format: String(localized: "share_view_all_moods_total_template_title"), totalEntryCount)) .font(.title) - .foregroundColor(Color(UIColor.label)) + .foregroundColor(theme.currentTheme.labelColor) .frame(maxWidth: .infinity, alignment: .center) .padding() @@ -203,7 +196,7 @@ struct AllMoodsTotalTemplate: View, SharingTemplate { .padding([.leading, .trailing], -20) } else { mainView - .padding([.leading, .trailing]) + .padding([.leading, .trailing, .top]) } } } diff --git a/Shared/views/SharingTemplates/CurrentStreakTemplate.swift b/Shared/views/SharingTemplates/CurrentStreakTemplate.swift index 9f702a8..bed4e52 100644 --- a/Shared/views/SharingTemplates/CurrentStreakTemplate.swift +++ b/Shared/views/SharingTemplates/CurrentStreakTemplate.swift @@ -23,7 +23,8 @@ struct CurrentStreakTemplate: View, SharingTemplate { @State var showSharingTemplate = false @Environment(\.presentationMode) var presentationMode - + @AppStorage(UserDefaultsStore.Keys.moodTint.rawValue, store: GroupUserDefaults.groupDefaults) private var moodTint: MoodTints = .Default + @AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system let columns = [ GridItem(.flexible(minimum: 5, maximum: .infinity), alignment: .center), @@ -60,7 +61,7 @@ struct CurrentStreakTemplate: View, SharingTemplate { entry.mood.icon .resizable() .aspectRatio(contentMode: .fit) - .foregroundColor(entry.mood.color) + .foregroundColor(moodTint.color(forMood: entry.mood)) } } } @@ -72,7 +73,7 @@ struct CurrentStreakTemplate: View, SharingTemplate { VStack { Text(String(format: String(localized: "share_view_current_streak_template_title"))) .font(.title) - .foregroundColor(Color(UIColor.label)) + .foregroundColor(theme.currentTheme.labelColor) .frame(maxWidth: .infinity, alignment: .center) .padding(.top) @@ -83,7 +84,7 @@ struct CurrentStreakTemplate: View, SharingTemplate { entry.mood.icon .resizable() .aspectRatio(contentMode: .fit) - .foregroundColor(entry.mood.color) + .foregroundColor(moodTint.color(forMood: entry.mood)) } } } @@ -141,7 +142,8 @@ struct CurrentStreakTemplate: View, SharingTemplate { .padding([.leading, .trailing], -20) } else { mainView - .padding([.leading, .trailing]) + .padding([.leading, .trailing, .top]) + } } } diff --git a/Shared/views/SharingTemplates/LongestStreakTemplate.swift b/Shared/views/SharingTemplates/LongestStreakTemplate.swift index 4ea8a47..73870be 100644 --- a/Shared/views/SharingTemplates/LongestStreakTemplate.swift +++ b/Shared/views/SharingTemplates/LongestStreakTemplate.swift @@ -33,7 +33,8 @@ struct LongestStreakTemplate: View, SharingTemplate { @State var showSharingTemplate = false @Environment(\.presentationMode) var presentationMode - + @AppStorage(UserDefaultsStore.Keys.moodTint.rawValue, store: GroupUserDefaults.groupDefaults) private var moodTint: MoodTints = .Default + @AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system let columns = [ GridItem(.flexible(minimum: 5, maximum: .infinity), alignment: .center), @@ -96,7 +97,7 @@ struct LongestStreakTemplate: View, SharingTemplate { entry.mood.icon .resizable() .aspectRatio(contentMode: .fit) - .foregroundColor(entry.mood.color) + .foregroundColor(moodTint.color(forMood: entry.mood)) } } } @@ -112,26 +113,26 @@ struct LongestStreakTemplate: View, SharingTemplate { VStack { Text(String(format: String(localized: "share_view_longest_streak_template_title"), self.selectedMood.strValue)) .font(.title) - .foregroundColor(Color(UIColor.label)) + .foregroundColor(theme.currentTheme.labelColor) .frame(maxWidth: .infinity, alignment: .center) .padding() HStack { Text(self.moodEntries.first?.forDate ?? Date(), formatter: itemFormatter) .font(.title) - .foregroundColor(Color(UIColor.label)) + .foregroundColor(theme.currentTheme.labelColor) .frame(maxWidth: .infinity, alignment: .center) .padding(.top, 1) Text("-") .font(.title) - .foregroundColor(Color(UIColor.label)) + .foregroundColor(theme.currentTheme.labelColor) .frame(maxWidth: .infinity, alignment: .center) .padding(.top, 1) Text(self.moodEntries.last?.forDate ?? Date(), formatter: itemFormatter) .font(.title) - .foregroundColor(Color(UIColor.label)) + .foregroundColor(theme.currentTheme.labelColor) .frame(maxWidth: .infinity, alignment: .center) .padding(.top, 1) } @@ -144,7 +145,7 @@ struct LongestStreakTemplate: View, SharingTemplate { entry.mood.icon .resizable() .aspectRatio(contentMode: .fit) - .foregroundColor(entry.mood.color) + .foregroundColor(moodTint.color(forMood: entry.mood)) } } } @@ -224,7 +225,8 @@ struct LongestStreakTemplate: View, SharingTemplate { .padding([.leading, .trailing], -20) } else { mainView - .padding([.leading, .trailing]) + .padding([.leading, .trailing, .top]) + } } } diff --git a/Shared/views/SharingTemplates/MonthTotalTemplate.swift b/Shared/views/SharingTemplates/MonthTotalTemplate.swift index fb08d0f..c10b921 100644 --- a/Shared/views/SharingTemplates/MonthTotalTemplate.swift +++ b/Shared/views/SharingTemplates/MonthTotalTemplate.swift @@ -21,6 +21,8 @@ struct MonthTotalTemplate: View, SharingTemplate { @State var showSharingTemplate = false @Environment(\.presentationMode) var presentationMode + @AppStorage(UserDefaultsStore.Keys.moodTint.rawValue, store: GroupUserDefaults.groupDefaults) private var moodTint: MoodTints = .Default + @AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system private var moodMetrics = [MoodMetrics]() private var moodEntries = [MoodEntry]() @@ -59,16 +61,7 @@ struct MonthTotalTemplate: View, SharingTemplate { moodEntries = _moodEntries ?? [MoodEntry]() totalEntryCount = moodEntries.count - - for (_, mood) in Mood.allValues.enumerated() { - let moodEntries = moodEntries.filter({ - Int($0.moodValue) == mood.rawValue - }) - let total = moodEntries.count - let perc = (Float(total) / Float(totalEntryCount)) * 100 - moodMetrics.append(MoodMetrics(mood: mood, total: total, percent: perc)) - } - + moodMetrics = Random.createTotalPerc(fromEntries: moodEntries) moodMetrics = moodMetrics.sorted(by: { $0.mood.rawValue > $1.mood.rawValue }) @@ -89,7 +82,7 @@ struct MonthTotalTemplate: View, SharingTemplate { HStack { ForEach(moodMetrics, id: \.mood) { model in ZStack { - Circle().fill(model.mood.color) + Circle().fill(moodTint.color(forMood: model.mood)) Text("\(model.percent, specifier: "%.0f")%") .font(.title) @@ -109,7 +102,7 @@ struct MonthTotalTemplate: View, SharingTemplate { VStack { Text(String(format: String(localized: "share_view_month_moods_total_template_title"), Random.monthName(fromMonthInt: month), moodEntries.count)) .font(.title) - .foregroundColor(Color(UIColor.label)) + .foregroundColor(theme.currentTheme.labelColor) .frame(maxWidth: .infinity, alignment: .center) .padding() @@ -119,7 +112,7 @@ struct MonthTotalTemplate: View, SharingTemplate { entry.mood.icon .resizable() .aspectRatio(contentMode: .fit) - .foregroundColor(entry.mood.color) + .foregroundColor(moodTint.color(forMood: entry.mood)) } } } @@ -179,15 +172,15 @@ struct MonthTotalTemplate: View, SharingTemplate { .padding([.leading, .trailing], -20) } else { mainView - .padding([.leading, .trailing]) + .padding([.leading, .trailing, .top]) } } } struct MonthTotalTemplate_Previews: PreviewProvider { static var previews: some View { - MonthTotalTemplate(isPreview: true, startDate: Date().startOfMonth(), endDate: Date().endOfMonth(), fakeData: true) + MonthTotalTemplate(isPreview: true, startDate: Date().startOfMonth, endDate: Date().endOfMonth, fakeData: true) - MonthTotalTemplate(isPreview: false, startDate: Date().startOfMonth(), endDate: Date().endOfMonth(), fakeData: true) + MonthTotalTemplate(isPreview: false, startDate: Date().startOfMonth, endDate: Date().endOfMonth, fakeData: true) } } diff --git a/Shared/views/SmallRollUpHeaderView.swift b/Shared/views/SmallRollUpHeaderView.swift index ef73a04..aaf0198 100644 --- a/Shared/views/SmallRollUpHeaderView.swift +++ b/Shared/views/SmallRollUpHeaderView.swift @@ -9,9 +9,10 @@ import SwiftUI struct SmallRollUpHeaderView: View { @Binding var viewType: MainSwitchableViewType - + @AppStorage(UserDefaultsStore.Keys.moodTint.rawValue, store: GroupUserDefaults.groupDefaults) private var moodTint: MoodTints = .Default + let entries: [MoodEntry] - private var moodMetrics = [MoodGroupingMetrics]() + private var moodMetrics = [MoodMetrics]() init(entries: [MoodEntry], viewType: Binding) { self.entries = entries @@ -20,7 +21,7 @@ struct SmallRollUpHeaderView: View { moodMetrics = Random.createTotalPerc(fromEntries: entries) } - private func textView(forModel model: MoodGroupingMetrics) -> Text { + private func textView(forModel model: MoodMetrics) -> Text { switch viewType { case .total: return Text(String(model.total)) @@ -32,24 +33,25 @@ struct SmallRollUpHeaderView: View { } private var textViews: some View { - HStack { - ForEach(moodMetrics, id: \.0) { model in + HStack() { + ForEach(moodMetrics, id: \.id) { model in textView(forModel: model) .font(.title2) .fontWeight(.bold) .lineLimit(1) - .foregroundColor(model.mood.color) - .frame(width: 70, height: 70) + .foregroundColor(moodTint.color(forMood: model.mood)) + .frame(maxWidth: .infinity, alignment: .center) } } + .frame(maxWidth: .infinity, alignment: .leading) .padding([.top, .bottom]) } private var circularViews: some View { HStack { - ForEach(moodMetrics, id: \.0) { model in + ForEach(moodMetrics, id: \.id) { model in ZStack { - Circle().fill(model.mood.color) + Circle().fill(moodTint.color(forMood: model.mood)) .frame(minWidth: 5, maxWidth: 70, minHeight: 5, @@ -60,9 +62,9 @@ struct SmallRollUpHeaderView: View { .font(.title3) .fontWeight(.bold) .lineLimit(1) - .clipShape(ContainerRelativeShape()).padding() + .clipShape(ContainerRelativeShape()) .foregroundColor(Color(UIColor.white)) - .minimumScaleFactor(0.1) + .minimumScaleFactor(0.7) ) } } @@ -94,8 +96,13 @@ struct SmallHeaderView_Previews: PreviewProvider { viewType: .constant(.total)) SmallRollUpHeaderView(entries: PersistenceController.shared.randomEntries(count: 10), - viewType: .constant(.total)) - .frame(height: 20) + viewType: .constant(.percentageCircle)) + .background(.gray) + + SmallRollUpHeaderView(entries: PersistenceController.shared.randomEntries(count: 10), + viewType: .constant(.percentage)) + + .background(.gray) } } } diff --git a/Shared/views/SwitchableView.swift b/Shared/views/SwitchableView.swift index 10b784d..897508d 100644 --- a/Shared/views/SwitchableView.swift +++ b/Shared/views/SwitchableView.swift @@ -28,7 +28,8 @@ struct SwitchableView: View { let daysBack: Int @AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system - + @AppStorage(UserDefaultsStore.Keys.moodTint.rawValue, store: GroupUserDefaults.groupDefaults) private var moodTint: MoodTints = .Default + init(daysBack: Int, viewType: Binding, headerTypeChanged: @escaping ((MainSwitchableViewType) -> Void)) { self.daysBack = daysBack self.headerTypeChanged = headerTypeChanged @@ -40,7 +41,7 @@ struct SwitchableView: View { ZStack { switch viewType { case .total: - HeaderStatsView(fakeData: false, backDays: daysBack) + HeaderStatsView(fakeData: false, backDays: daysBack, moodTint: moodTint) .padding([.leading, .trailing], -15) .padding([.top, .bottom], 8) .allowsHitTesting(false) @@ -75,7 +76,7 @@ struct SwitchableView: View { .padding(.top, -12) } .background( - Color(theme.currentTheme.secondaryBGColor) + theme.currentTheme.secondaryBGColor ) .contentShape(Rectangle()) .cornerRadius(10, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) diff --git a/en.lproj/Localizable.strings b/en.lproj/Localizable.strings index 73ba908..c519360 100644 --- a/en.lproj/Localizable.strings +++ b/en.lproj/Localizable.strings @@ -33,6 +33,7 @@ "content_view_tab_main" = "Main"; "content_view_tab_filter" = "Filter"; "content_view_tab_share" = "Share"; +"content_view_tab_customize" = "Customize"; "content_view_fill_in_missing_entry" = "Update %@"; "content_view_fill_in_missing_entry_cancel" = "Cancel"; "content_view_delete_entry" = "Delete this entry"; @@ -70,3 +71,5 @@ "share_view_month_moods_total_template_title" = "Total Entries for %@ - %d"; "share_view_current_streak_template_title" = "Last 10 Days"; "share_view_longest_streak_template_title" = "Longest consecutive days I was %@"; + +"customize_view_title" = "Make ifeel yours!!!";