everything changed
This commit is contained in:
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
87
Shared/Date+Extensions.swift
Normal file
87
Shared/Date+Extensions.swift
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
104
Shared/Models/MoodImagable.swift
Normal file
104
Shared/Models/MoodImagable.swift
Normal file
@@ -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()!)
|
||||
}
|
||||
}
|
||||
}
|
||||
266
Shared/Models/MoodTintable.swift
Normal file
266
Shared/Models/MoodTintable.swift
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
91
Shared/Models/PersonalityPackable.swift
Normal file
91
Shared/Models/PersonalityPackable.swift
Normal file
@@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
66
Shared/MoodEntryFunctions.swift
Normal file
66
Shared/MoodEntryFunctions.swift
Normal file
@@ -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..<weekday {
|
||||
mutableEntries.insert(PersistenceController.shared.generateObjectNotInArray(withMood: .placeholder), at: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
return mutableEntries
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,8 @@ extension PersistenceController {
|
||||
}
|
||||
|
||||
func fillInMissingDates() {
|
||||
let endDate = ShowBasedOnVoteLogics.getLastDateVoteShouldExist()
|
||||
let currentOnboarding = UserDefaultsStore.getOnboarding()
|
||||
let endDate = ShowBasedOnVoteLogics.getLastDateVoteShouldExist(onboardingData: currentOnboarding)
|
||||
|
||||
let fetchRequest = NSFetchRequest<MoodEntry>(entityName: "MoodEntry")
|
||||
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "forDate", ascending: false)]
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())!
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
124
Shared/views/CustomIcon/IconView.swift
Normal file
124
Shared/views/CustomIcon/IconView.swift
Normal file
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
80
Shared/views/CustomIcon/IconViewModel.swift
Normal file
80
Shared/views/CustomIcon/IconViewModel.swift
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
@@ -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)
|
||||
|
||||
}
|
||||
239
Shared/views/CustomizeView/CustomizeView.swift
Normal file
239
Shared/views/CustomizeView/CustomizeView.swift
Normal file
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@ struct FilterView: View {
|
||||
private var items: FetchedResults<MoodEntry>
|
||||
|
||||
@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)
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
170
Shared/views/HomeView/HomeViewTwo/HomeViewTwo.swift
Normal file
170
Shared/views/HomeView/HomeViewTwo/HomeViewTwo.swift
Normal file
@@ -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()
|
||||
}
|
||||
}
|
||||
171
Shared/views/HomeView/HomeViewTwo/MonthDetailView.swift
Normal file
171
Shared/views/HomeView/HomeViewTwo/MonthDetailView.swift
Normal file
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -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..<weekday {
|
||||
mutableEntries.insert(PersistenceController.shared.generateObjectNotInArray(), at: 0)
|
||||
}
|
||||
}
|
||||
|
||||
newMonth[key] = mutableEntries
|
||||
}
|
||||
newGrouped[year] = newMonth
|
||||
}
|
||||
}
|
||||
grouped = newGrouped
|
||||
grouped = MoodEntryFunctions.padMoodEntriesForCalendar(entries: grouped)
|
||||
}
|
||||
|
||||
numberOfItems = numberOfEntries
|
||||
@@ -1,110 +0,0 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
@ObservedObject var viewModel = ContentModeViewModel(addMonthStartWeekdayPadding: true)
|
||||
|
||||
@StateObject private var selectedDetail = StupidAssDetailViewObservableObject()
|
||||
|
||||
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 {
|
||||
ScrollView {
|
||||
topView
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.frame(height: 200)
|
||||
.background(
|
||||
.blue
|
||||
)
|
||||
LazyVStack(spacing: 5, pinnedViews: [.sectionHeaders]) {
|
||||
ForEach(viewModel.grouped.sorted(by: { $0.key > $1.key }), id: \.key) { year, months in
|
||||
|
||||
// for reach month
|
||||
ForEach(months.sorted(by: { $0.key > $1.key }), id: \.key) { month, entries in
|
||||
Section(header: 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()
|
||||
}
|
||||
}
|
||||
@@ -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!
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -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: {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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])
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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])
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<MainSwitchableViewType>) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<MainSwitchableViewType>, 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])
|
||||
|
||||
Reference in New Issue
Block a user