Merge branch 'develop'
This commit is contained in:
@@ -30,14 +30,12 @@ struct FeelsApp: App {
|
||||
.environment(\.managedObjectContext, persistenceController.viewContext)
|
||||
}.onChange(of: scenePhase) { phase in
|
||||
if phase == .background {
|
||||
BGTask.scheduleBackgroundProcessing()
|
||||
// BGTask.scheduleBackgroundProcessing()
|
||||
WidgetCenter.shared.reloadAllTimelines()
|
||||
}
|
||||
|
||||
if phase == .active {
|
||||
UIApplication.shared.applicationIconBadgeNumber = 0
|
||||
print("UserDefaultsStore input day", UserDefaultsStore.getOnboarding().inputDay)
|
||||
print("UserDefaultsStore input date", UserDefaultsStore.getOnboarding().date)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,9 +75,9 @@ final class EmojiMoodImages: MoodImagable {
|
||||
case .great:
|
||||
return Image(uiImage: "😀".textToImage()!)
|
||||
case .missing:
|
||||
return Image(uiImage: "X".textToImage()!)
|
||||
return Image("missing", bundle: .main)
|
||||
case .placeholder:
|
||||
return Image(uiImage: "X".textToImage()!)
|
||||
return Image("missing", bundle: .main)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,9 +96,9 @@ final class HandEmojiMoodImages: MoodImagable {
|
||||
case .great:
|
||||
return Image(uiImage: "🙏".textToImage()!)
|
||||
case .missing:
|
||||
return Image(uiImage: "X".textToImage()!)
|
||||
return Image("missing", bundle: .main)
|
||||
case .placeholder:
|
||||
return Image(uiImage: "X".textToImage()!)
|
||||
return Image("missing", bundle: .main)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,21 +14,22 @@ protocol MoodTintable {
|
||||
|
||||
enum MoodTints: Int, CaseIterable {
|
||||
case Default
|
||||
case AllRed
|
||||
case Neon
|
||||
case MonoChrome
|
||||
case Pastel
|
||||
case Custom
|
||||
|
||||
static var defaultOptions: [MoodTints] {
|
||||
return [Default, Neon, Pastel]
|
||||
}
|
||||
|
||||
func color(forMood mood: Mood) -> Color {
|
||||
switch self {
|
||||
case .Default:
|
||||
return DefaultMoodTint.color(forMood: mood)
|
||||
case .AllRed:
|
||||
return AllRedMoodTint.color(forMood: mood)
|
||||
case .Custom:
|
||||
return CustomMoodTint.color(forMood: mood)
|
||||
case .Neon:
|
||||
return NeonMoodTint.color(forMood: mood)
|
||||
case .MonoChrome:
|
||||
return MonoChromeTint.color(forMood: mood)
|
||||
case .Pastel:
|
||||
return PastelTint.color(forMood: mood)
|
||||
}
|
||||
@@ -38,12 +39,10 @@ enum MoodTints: Int, CaseIterable {
|
||||
switch self {
|
||||
case .Default:
|
||||
return DefaultMoodTint.secondary(forMood: mood)
|
||||
case .AllRed:
|
||||
return AllRedMoodTint.secondary(forMood: mood)
|
||||
case .Custom:
|
||||
return CustomMoodTint.secondary(forMood: mood)
|
||||
case .Neon:
|
||||
return NeonMoodTint.secondary(forMood: mood)
|
||||
case .MonoChrome:
|
||||
return MonoChromeTint.secondary(forMood: mood)
|
||||
case .Pastel:
|
||||
return PastelTint.secondary(forMood: mood)
|
||||
}
|
||||
@@ -53,18 +52,94 @@ enum MoodTints: Int, CaseIterable {
|
||||
switch self {
|
||||
case .Default:
|
||||
return DefaultMoodTint.self
|
||||
case .AllRed:
|
||||
return AllRedMoodTint.self
|
||||
case .Custom:
|
||||
return CustomMoodTint.self
|
||||
case .Neon:
|
||||
return NeonMoodTint.self
|
||||
case .MonoChrome:
|
||||
return MonoChromeTint.self
|
||||
case .Pastel:
|
||||
return PastelTint.self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class SavedMoodTint: NSObject, ObservableObject, Codable {
|
||||
@Published var colorOne: Color
|
||||
@Published var colorTwo: Color
|
||||
@Published var colorThree: Color
|
||||
@Published var colorFour: Color
|
||||
@Published var colorFive: Color
|
||||
|
||||
override init() {
|
||||
colorOne = Color(hex: "a92b26")
|
||||
colorTwo = Color(hex: "a92b26")
|
||||
colorThree = Color(hex: "a92b26")
|
||||
colorFour = Color(hex: "a92b26")
|
||||
colorFive = Color(hex: "a92b26")
|
||||
}
|
||||
|
||||
enum CodingKeys: CodingKey {
|
||||
case colorOne, colorTwo, colorThree, colorFour, colorFive
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
colorOne = try container.decode(Color.self, forKey: .colorOne)
|
||||
colorTwo = try container.decode(Color.self, forKey: .colorTwo)
|
||||
colorThree = try container.decode(Color.self, forKey: .colorThree)
|
||||
colorFour = try container.decode(Color.self, forKey: .colorFour)
|
||||
colorFive = try container.decode(Color.self, forKey: .colorFive)
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(colorOne, forKey: .colorOne)
|
||||
try container.encode(colorTwo, forKey: .colorTwo)
|
||||
try container.encode(colorThree, forKey: .colorThree)
|
||||
try container.encode(colorFour, forKey: .colorFour)
|
||||
try container.encode(colorFive, forKey: .colorFive)
|
||||
}
|
||||
}
|
||||
|
||||
final class CustomMoodTint: MoodTintable {
|
||||
static func color(forMood mood: Mood) -> Color {
|
||||
switch mood {
|
||||
case .horrible:
|
||||
return UserDefaultsStore.getCustomMoodTint().colorFive
|
||||
case .bad:
|
||||
return UserDefaultsStore.getCustomMoodTint().colorFour
|
||||
case .average:
|
||||
return UserDefaultsStore.getCustomMoodTint().colorThree
|
||||
case .good:
|
||||
return UserDefaultsStore.getCustomMoodTint().colorTwo
|
||||
case .great:
|
||||
return UserDefaultsStore.getCustomMoodTint().colorOne
|
||||
case .missing:
|
||||
return Color(uiColor: UIColor.lightGray)
|
||||
case .placeholder:
|
||||
return Color(uiColor: UIColor.lightGray)
|
||||
}
|
||||
}
|
||||
|
||||
static func secondary(forMood mood: Mood) -> Color {
|
||||
switch mood {
|
||||
case .horrible:
|
||||
return UserDefaultsStore.getCustomMoodTint().colorFive.darker(by: 40)
|
||||
case .bad:
|
||||
return UserDefaultsStore.getCustomMoodTint().colorFour.darker(by: 40)
|
||||
case .average:
|
||||
return UserDefaultsStore.getCustomMoodTint().colorThree.darker(by: 40)
|
||||
case .good:
|
||||
return UserDefaultsStore.getCustomMoodTint().colorTwo.darker(by: 40)
|
||||
case .great:
|
||||
return UserDefaultsStore.getCustomMoodTint().colorOne.darker(by: 40)
|
||||
case .missing:
|
||||
return Color(uiColor: UIColor.lightGray).darker(by: 40)
|
||||
case .placeholder:
|
||||
return Color(uiColor: UIColor.lightGray).darker(by: 40)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class DefaultMoodTint: MoodTintable {
|
||||
static func color(forMood mood: Mood) -> Color {
|
||||
switch mood {
|
||||
|
||||
@@ -19,6 +19,8 @@ class UserDefaultsStore {
|
||||
case moodTint
|
||||
case personalityPack
|
||||
case customWidget
|
||||
case customMoodTint
|
||||
case customMoodTintUpdateNumber
|
||||
|
||||
case contentViewCurrentSelectedHeaderViewBackDays
|
||||
case contentViewHeaderTag
|
||||
@@ -173,5 +175,29 @@ class UserDefaultsStore {
|
||||
fatalError("error saving")
|
||||
}
|
||||
}
|
||||
|
||||
static func getCustomMoodTint() -> SavedMoodTint {
|
||||
if let data = GroupUserDefaults.groupDefaults.object(forKey: UserDefaultsStore.Keys.customMoodTint.rawValue) as? Data{
|
||||
do {
|
||||
let model = try JSONDecoder().decode(SavedMoodTint.self, from: data)
|
||||
return model
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
return SavedMoodTint()
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
static func saveCustomMoodTint(customTint: SavedMoodTint) -> SavedMoodTint {
|
||||
do {
|
||||
let data = try JSONEncoder().encode(customTint)
|
||||
GroupUserDefaults.groupDefaults.set(data, forKey: UserDefaultsStore.Keys.customMoodTint.rawValue)
|
||||
return UserDefaultsStore.getCustomMoodTint()
|
||||
} catch {
|
||||
print(error)
|
||||
fatalError("error saving")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,9 +12,7 @@ class PersistenceController {
|
||||
@AppStorage(UserDefaultsStore.Keys.useCloudKit.rawValue, store: GroupUserDefaults.groupDefaults) private var useCloudKit = false
|
||||
|
||||
static let shared = PersistenceController.persistenceController
|
||||
|
||||
public var listeners = [(() -> Void)]()
|
||||
|
||||
|
||||
private static var persistenceController: PersistenceController {
|
||||
return PersistenceController(inMemory: false)
|
||||
}
|
||||
@@ -23,8 +21,14 @@ class PersistenceController {
|
||||
return PersistenceController.shared.container.viewContext
|
||||
}
|
||||
|
||||
public lazy var childContext: NSManagedObjectContext = {
|
||||
NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
|
||||
}()
|
||||
|
||||
public var switchContainerListeners = [(() -> Void)]()
|
||||
|
||||
private var editedDataClosure = [() -> Void]()
|
||||
|
||||
public var earliestEntry: MoodEntry? {
|
||||
let fetchRequest = NSFetchRequest<MoodEntry>(entityName: "MoodEntry")
|
||||
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "forDate", ascending: true)]
|
||||
@@ -50,7 +54,23 @@ class PersistenceController {
|
||||
item()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func addNewDataListener(closure: @escaping (() -> Void)) {
|
||||
editedDataClosure.append(closure)
|
||||
}
|
||||
|
||||
public func saveAndRunDataListerners() {
|
||||
do {
|
||||
try viewContext.save()
|
||||
|
||||
for closure in editedDataClosure {
|
||||
closure()
|
||||
}
|
||||
} catch {
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
|
||||
private func setupContainer() -> NSPersistentContainer {
|
||||
if useCloudKit {
|
||||
container = NSPersistentCloudKitContainer(name: "Feels")
|
||||
@@ -79,12 +99,6 @@ class PersistenceController {
|
||||
init(inMemory: Bool = false) {
|
||||
container = setupContainer()
|
||||
}
|
||||
|
||||
func updateListeners() {
|
||||
for listener in listeners {
|
||||
listener()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension NSManagedObjectContext {
|
||||
|
||||
@@ -23,13 +23,7 @@ extension PersistenceController {
|
||||
newItem.canDelete = true
|
||||
newItem.entryType = Int16(entryType.rawValue)
|
||||
|
||||
do {
|
||||
try viewContext.save()
|
||||
updateListeners()
|
||||
} catch {
|
||||
let nsError = error as NSError
|
||||
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
|
||||
}
|
||||
saveAndRunDataListerners()
|
||||
}
|
||||
|
||||
func fillInMissingDates() {
|
||||
|
||||
@@ -14,18 +14,9 @@ extension PersistenceController {
|
||||
|
||||
do {
|
||||
try viewContext.executeAndMergeChanges(using: deleteRequest)
|
||||
try viewContext.save()
|
||||
updateListeners()
|
||||
saveAndRunDataListerners()
|
||||
} catch let error as NSError {
|
||||
fatalError("Unresolved error \(error), \(error.userInfo)")
|
||||
}
|
||||
}
|
||||
|
||||
func delete(forDate: Date) {
|
||||
if let entry = PersistenceController.shared.getEntry(byDate: forDate) {
|
||||
viewContext.delete(entry)
|
||||
try! viewContext.save()
|
||||
updateListeners()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ extension PersistenceController {
|
||||
|
||||
public func getData(startDate: Date, endDate: Date, includedDays: [Int]) -> [MoodEntry] {
|
||||
try! viewContext.setQueryGenerationFrom(.current)
|
||||
viewContext.refreshAllObjects()
|
||||
// viewContext.refreshAllObjects()
|
||||
|
||||
var includedDays16 = [Int16]()
|
||||
|
||||
@@ -79,6 +79,7 @@ extension PersistenceController {
|
||||
let items = PersistenceController.shared.getData(startDate: startDateOfMonth,
|
||||
endDate: startDateOfMonth.endOfMonth,
|
||||
includedDays: [1,2,3,4,5,6,7])
|
||||
|
||||
if !items.isEmpty {
|
||||
allMonths[month] = items
|
||||
}
|
||||
|
||||
@@ -8,10 +8,6 @@
|
||||
import CoreData
|
||||
|
||||
extension PersistenceController {
|
||||
private var childContext: NSManagedObjectContext {
|
||||
return NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
|
||||
}
|
||||
|
||||
public func randomEntries(count: Int) -> [MoodEntry] {
|
||||
var entries = [MoodEntry]()
|
||||
|
||||
@@ -63,19 +59,23 @@ extension PersistenceController {
|
||||
}
|
||||
|
||||
func populateTestData() {
|
||||
for idx in 1..<120 {
|
||||
let newItem = MoodEntry(context: viewContext)
|
||||
newItem.timestamp = Date()
|
||||
newItem.moodValue = Int16(Mood.allValues.randomElement()!.rawValue)
|
||||
newItem.canEdit = true
|
||||
newItem.canDelete = true
|
||||
|
||||
let date = Calendar.current.date(byAdding: .day, value: -idx, to: Date())!
|
||||
newItem.forDate = date
|
||||
newItem.weekDay = Int16(Calendar.current.component(.weekday, from: date))
|
||||
}
|
||||
do {
|
||||
self.clearDB()
|
||||
try viewContext.save()
|
||||
|
||||
for idx in 1..<120 {
|
||||
let newItem = MoodEntry(context: viewContext)
|
||||
newItem.timestamp = Date()
|
||||
newItem.moodValue = Int16(Mood.allValues.randomElement()!.rawValue)
|
||||
newItem.canEdit = true
|
||||
newItem.canDelete = true
|
||||
|
||||
let date = Calendar.current.date(byAdding: .day, value: -idx, to: Date())!
|
||||
newItem.forDate = date
|
||||
newItem.weekDay = Int16(Calendar.current.component(.weekday, from: date))
|
||||
}
|
||||
|
||||
saveAndRunDataListerners()
|
||||
} catch {
|
||||
// Replace this implementation with code to handle the error appropriately.
|
||||
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
|
||||
|
||||
@@ -10,34 +10,12 @@ import Foundation
|
||||
extension PersistenceController {
|
||||
@discardableResult
|
||||
public func update(entryDate: Date, withModd mood: Mood) -> Bool {
|
||||
do {
|
||||
if let entry = PersistenceController.shared.getEntry(byDate: entryDate) {
|
||||
viewContext.delete(entry)
|
||||
}
|
||||
|
||||
try viewContext.save()
|
||||
} catch {
|
||||
// Replace this implementation with code to handle the error appropriately.
|
||||
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
|
||||
let nsError = error as NSError
|
||||
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
|
||||
guard let existingEntry = getEntry(byDate: entryDate) else {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
|
||||
do {
|
||||
add(mood: mood, forDate: entryDate, entryType: .listView)
|
||||
try viewContext.save()
|
||||
} catch {
|
||||
// Replace this implementation with code to handle the error appropriately.
|
||||
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
|
||||
let nsError = error as NSError
|
||||
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
|
||||
return false
|
||||
}
|
||||
|
||||
updateListeners()
|
||||
existingEntry.setValue(mood.rawValue, forKey: "moodValue")
|
||||
saveAndRunDataListerners()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,6 +132,9 @@ extension Color {
|
||||
blue: .random(in: 0...1)
|
||||
)
|
||||
}
|
||||
|
||||
public func lighter(by amount: CGFloat = 0.2) -> Self { Self(UIColor(self).lighter(by: amount)) }
|
||||
public func darker(by amount: CGFloat = 0.2) -> Self { Self(UIColor(self).darker(by: amount)) }
|
||||
}
|
||||
|
||||
extension String {
|
||||
@@ -151,3 +154,38 @@ extension String {
|
||||
return image ?? UIImage()
|
||||
}
|
||||
}
|
||||
|
||||
extension UIColor {
|
||||
|
||||
func lighter(by percentage: CGFloat = 10.0) -> UIColor {
|
||||
return self.adjust(by: abs(percentage))
|
||||
}
|
||||
|
||||
func darker(by percentage: CGFloat = 10.0) -> UIColor {
|
||||
return self.adjust(by: -abs(percentage))
|
||||
}
|
||||
|
||||
func adjust(by percentage: CGFloat) -> UIColor {
|
||||
var alpha, hue, saturation, brightness, red, green, blue, white : CGFloat
|
||||
(alpha, hue, saturation, brightness, red, green, blue, white) = (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
|
||||
|
||||
let multiplier = percentage / 100.0
|
||||
|
||||
if self.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) {
|
||||
let newBrightness: CGFloat = max(min(brightness + multiplier*brightness, 1.0), 0.0)
|
||||
return UIColor(hue: hue, saturation: saturation, brightness: newBrightness, alpha: alpha)
|
||||
}
|
||||
else if self.getRed(&red, green: &green, blue: &blue, alpha: &alpha) {
|
||||
let newRed: CGFloat = min(max(red + multiplier*red, 0.0), 1.0)
|
||||
let newGreen: CGFloat = min(max(green + multiplier*green, 0.0), 1.0)
|
||||
let newBlue: CGFloat = min(max(blue + multiplier*blue, 0.0), 1.0)
|
||||
return UIColor(red: newRed, green: newGreen, blue: newBlue, alpha: alpha)
|
||||
}
|
||||
else if self.getWhite(&white, alpha: &alpha) {
|
||||
let newWhite: CGFloat = (white + multiplier*white)
|
||||
return UIColor(white: newWhite, alpha: alpha)
|
||||
}
|
||||
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,17 @@ 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
|
||||
|
||||
@AppStorage(UserDefaultsStore.Keys.customMoodTintUpdateNumber.rawValue, store: GroupUserDefaults.groupDefaults) private var customMoodTintUpdateNumber: Int = 0
|
||||
|
||||
@StateObject private var customMoodTint = UserDefaultsStore.getCustomMoodTint()
|
||||
|
||||
class StupidAssCustomWidgetObservableObject: ObservableObject {
|
||||
@Published var fuckingWrapped: CustomWidgetModel? = nil
|
||||
@Published var showFuckingSheet = false
|
||||
@@ -28,12 +35,6 @@ struct CustomizeView: View {
|
||||
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
|
||||
@@ -58,11 +59,6 @@ struct CustomizeView: View {
|
||||
ZStack {
|
||||
theme.currentTheme.secondaryBGColor
|
||||
VStack {
|
||||
Text(String(localized: "customize_view_view_change_icon"))
|
||||
.font(.body)
|
||||
.foregroundColor(theme.currentTheme.labelColor)
|
||||
.padding(.top)
|
||||
|
||||
ScrollView(.horizontal) {
|
||||
HStack {
|
||||
Button(action: {
|
||||
@@ -102,10 +98,6 @@ struct CustomizeView: View {
|
||||
ZStack {
|
||||
theme.currentTheme.secondaryBGColor
|
||||
VStack {
|
||||
Text(String(localized: "customize_view_background_title"))
|
||||
.font(.body)
|
||||
.foregroundColor(theme.currentTheme.labelColor)
|
||||
|
||||
HStack {
|
||||
Spacer()
|
||||
ForEach(Theme.allCases, id:\.rawValue) { aTheme in
|
||||
@@ -143,10 +135,6 @@ struct CustomizeView: View {
|
||||
ZStack {
|
||||
theme.currentTheme.secondaryBGColor
|
||||
VStack {
|
||||
Text(String(localized: "customize_view_custom_widget_title"))
|
||||
.font(.body)
|
||||
.foregroundColor(theme.currentTheme.labelColor)
|
||||
.padding(.top)
|
||||
ScrollView(.horizontal) {
|
||||
HStack {
|
||||
ForEach(UserDefaultsStore.getCustomWidgets(), id: \.uuid) { widget in
|
||||
@@ -173,6 +161,9 @@ struct CustomizeView: View {
|
||||
.background(RoundedRectangle(cornerRadius: 10).fill().foregroundColor(theme.currentTheme.bgColor))
|
||||
.padding()
|
||||
.cornerRadius(10)
|
||||
|
||||
Text("[How to add widget](https://support.apple.com/guide/iphone/add-widgets-iphb8f1bf206/ios)")
|
||||
.padding(.bottom)
|
||||
}
|
||||
}
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
@@ -227,7 +218,7 @@ struct CustomizeView: View {
|
||||
ZStack {
|
||||
theme.currentTheme.secondaryBGColor
|
||||
VStack {
|
||||
ForEach(MoodTints.allCases, id: \.rawValue) { tint in
|
||||
ForEach(MoodTints.defaultOptions, id: \.rawValue) { tint in
|
||||
HStack {
|
||||
ForEach(Mood.allValues, id: \.self) { mood in
|
||||
Circle()
|
||||
@@ -250,10 +241,68 @@ struct CustomizeView: View {
|
||||
impactMed.impactOccurred()
|
||||
moodTint = tint
|
||||
}
|
||||
if tint.rawValue != (MoodTints.allCases.sorted(by: { $0.rawValue > $1.rawValue }).first?.rawValue) ?? 0 {
|
||||
Divider()
|
||||
}
|
||||
Divider()
|
||||
}
|
||||
|
||||
ZStack {
|
||||
Color.clear
|
||||
|
||||
Rectangle()
|
||||
.frame(height: 35)
|
||||
.frame(minWidth: 0, maxWidth: .infinity)
|
||||
.foregroundColor(.clear)
|
||||
.contentShape(Rectangle())
|
||||
.onTapGesture {
|
||||
moodTint = .Custom
|
||||
let impactMed = UIImpactFeedbackGenerator(style: .heavy)
|
||||
impactMed.impactOccurred()
|
||||
}
|
||||
|
||||
HStack {
|
||||
ColorPicker("", selection: $customMoodTint.colorOne)
|
||||
.onChange(of: customMoodTint.colorOne, perform: { _ in
|
||||
saveCustomMoodTint()
|
||||
})
|
||||
.labelsHidden()
|
||||
.frame(minWidth: 0, maxWidth: .infinity)
|
||||
|
||||
ColorPicker("", selection: $customMoodTint.colorTwo)
|
||||
.labelsHidden()
|
||||
.frame(minWidth: 0, maxWidth: .infinity)
|
||||
.onChange(of: customMoodTint.colorTwo, perform: { _ in
|
||||
saveCustomMoodTint()
|
||||
})
|
||||
|
||||
ColorPicker("", selection: $customMoodTint.colorThree)
|
||||
.labelsHidden()
|
||||
.frame(minWidth: 0, maxWidth: .infinity)
|
||||
.onChange(of: customMoodTint.colorThree, perform: { _ in
|
||||
saveCustomMoodTint()
|
||||
})
|
||||
|
||||
ColorPicker("", selection: $customMoodTint.colorFour)
|
||||
.labelsHidden()
|
||||
.frame(minWidth: 0, maxWidth: .infinity)
|
||||
.onChange(of: customMoodTint.colorFour, perform: { _ in
|
||||
saveCustomMoodTint()
|
||||
})
|
||||
|
||||
ColorPicker("", selection: $customMoodTint.colorFive)
|
||||
.labelsHidden()
|
||||
.frame(minWidth: 0, maxWidth: .infinity)
|
||||
.onChange(of: customMoodTint.colorFive, perform: { _ in
|
||||
saveCustomMoodTint()
|
||||
})
|
||||
}
|
||||
.background(
|
||||
Color.clear
|
||||
)
|
||||
}
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 10, style: .continuous)
|
||||
.fill(moodTint == .Custom ? theme.currentTheme.bgColor : .clear)
|
||||
.padding([.top, .bottom], -3)
|
||||
)
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
@@ -261,6 +310,12 @@ struct CustomizeView: View {
|
||||
.cornerRadius(10, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
|
||||
}
|
||||
|
||||
private func saveCustomMoodTint() {
|
||||
UserDefaultsStore.saveCustomMoodTint(customTint: customMoodTint)
|
||||
moodTint = .Custom
|
||||
customMoodTintUpdateNumber += 1
|
||||
}
|
||||
|
||||
private var pickPeronsalityPack: some View {
|
||||
ZStack {
|
||||
theme.currentTheme.secondaryBGColor
|
||||
@@ -303,9 +358,3 @@ struct CustomizeView: View {
|
||||
.cornerRadius(10, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
|
||||
}
|
||||
}
|
||||
|
||||
struct CustomizeView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
CustomizeView()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,10 +11,16 @@ import Charts
|
||||
struct HeaderStatsView : UIViewRepresentable {
|
||||
//Bar chart accepts data as array of BarChartDataEntry objects
|
||||
var entries : [BarChartDataEntry]
|
||||
var moodTint: MoodTints
|
||||
var moodTints: [Color]
|
||||
|
||||
init(fakeData: Bool, backDays: Int, moodTint: MoodTints) {
|
||||
self.moodTint = moodTint
|
||||
var tmpHolderToMakeViewDiffefrent: Color
|
||||
|
||||
init(fakeData: Bool, backDays: Int, moodTint: [Color]) {
|
||||
self.moodTints = moodTint
|
||||
guard moodTints.count == 5 else {
|
||||
fatalError("mood tint count dont match")
|
||||
}
|
||||
self.tmpHolderToMakeViewDiffefrent = Color.random()
|
||||
entries = [BarChartDataEntry]()
|
||||
|
||||
var moodEntries: [MoodEntry]?
|
||||
@@ -98,7 +104,7 @@ struct HeaderStatsView : UIViewRepresentable {
|
||||
let dataSet = BarChartDataSet(entries: entries)
|
||||
|
||||
// change bars color to green
|
||||
dataSet.colors = Mood.allValues.map({ NSUIColor( moodTint.color(forMood: $0) ) })
|
||||
dataSet.colors = moodTints.map({ NSUIColor( $0 ) })
|
||||
dataSet.secondaryTextColor = UIColor.systemGray
|
||||
dataSet.valueColors = [.white]
|
||||
dataSet.highlightAlpha = 0.0
|
||||
@@ -122,6 +128,6 @@ struct HeaderStatsView : UIViewRepresentable {
|
||||
|
||||
struct HeaderStatsView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
HeaderStatsView(fakeData: true, backDays: 30, moodTint: .Default).frame(minHeight: 85, maxHeight: 90)
|
||||
HeaderStatsView(fakeData: true, backDays: 30, moodTint: [Color.green, Color.blue, Color.yellow, Color.red, Color.orange]).frame(minHeight: 85, maxHeight: 90)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,9 @@ struct HomeView: View {
|
||||
@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
|
||||
|
||||
// store a value that gets changed when user updates custom colors to update the view since the moodTint doesn't change
|
||||
@AppStorage(UserDefaultsStore.Keys.customMoodTintUpdateNumber.rawValue, store: GroupUserDefaults.groupDefaults) private var customMoodTintUpdateNumber: Int = 0
|
||||
|
||||
// MARK: 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
|
||||
@@ -119,12 +122,16 @@ struct HomeView: View {
|
||||
Spacer()
|
||||
} else {
|
||||
ZStack {
|
||||
|
||||
Text(String(customMoodTintUpdateNumber))
|
||||
.hidden()
|
||||
|
||||
VStack {
|
||||
headerView
|
||||
Spacer()
|
||||
}
|
||||
.opacity(headerOpacity)
|
||||
|
||||
|
||||
VStack {
|
||||
SmallRollUpHeaderView(entries: getBackEntries(),
|
||||
viewType: $currentSelectedHeaderViewViewType)
|
||||
@@ -134,14 +141,13 @@ struct HomeView: View {
|
||||
theme.currentTheme.secondaryBGColor
|
||||
)
|
||||
.cornerRadius(10, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
|
||||
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.opacity(1 - headerOpacity)
|
||||
}
|
||||
.frame(height: headerHeight + 20)
|
||||
|
||||
|
||||
listView
|
||||
.padding(.top, -25)
|
||||
}
|
||||
@@ -168,11 +174,7 @@ struct HomeView: View {
|
||||
.foregroundColor(Color(UIColor.darkGray))
|
||||
.font(.system(size: 20))
|
||||
}).sheet(isPresented: $showingSheet) {
|
||||
SettingsView(editedDataClosure: {
|
||||
withAnimation{
|
||||
viewModel.updateData()
|
||||
}
|
||||
})
|
||||
SettingsView()
|
||||
}.padding(.trailing)
|
||||
}
|
||||
}
|
||||
@@ -200,7 +202,7 @@ struct HomeView: View {
|
||||
.frame(height: headerHeight)
|
||||
.frame(minWidth: 0, maxWidth: .infinity)
|
||||
.contentShape(Rectangle())
|
||||
|
||||
|
||||
SwitchableView(daysBack: 7,
|
||||
viewType: $secondSwichableHeaderViewType,
|
||||
headerTypeChanged: { viewType in
|
||||
@@ -218,7 +220,7 @@ struct HomeView: View {
|
||||
currentSelectedHeaderViewBackDays = 30
|
||||
currentSelectedHeaderViewViewType = firstSwichableHeaderViewType
|
||||
}
|
||||
|
||||
|
||||
if value == 2 {
|
||||
currentSelectedHeaderViewBackDays = 7
|
||||
currentSelectedHeaderViewViewType = secondSwichableHeaderViewType
|
||||
|
||||
@@ -41,38 +41,36 @@ class HomeViewViewModel: ObservableObject {
|
||||
|
||||
PersistenceController.shared.switchContainerListeners.append {
|
||||
self.getGroupedData(addMonthStartWeekdayPadding: self.addMonthStartWeekdayPadding)
|
||||
|
||||
}
|
||||
PersistenceController.shared.listeners.append { [weak self] in
|
||||
self?.updateData()
|
||||
|
||||
PersistenceController.shared.addNewDataListener {
|
||||
withAnimation{
|
||||
self.updateData()
|
||||
}
|
||||
}
|
||||
updateData()
|
||||
}
|
||||
|
||||
private func getGroupedData(addMonthStartWeekdayPadding: Bool) {
|
||||
grouped = PersistenceController.shared.splitIntoYearMonth()
|
||||
|
||||
var newStuff = PersistenceController.shared.splitIntoYearMonth()
|
||||
if addMonthStartWeekdayPadding {
|
||||
grouped = MoodEntryFunctions.padMoodEntriesForCalendar(entries: grouped)
|
||||
newStuff = MoodEntryFunctions.padMoodEntriesForCalendar(entries: newStuff)
|
||||
}
|
||||
|
||||
grouped = newStuff
|
||||
numberOfItems = numberOfEntries
|
||||
}
|
||||
|
||||
|
||||
|
||||
public func updateData() {
|
||||
getGroupedData(addMonthStartWeekdayPadding: self.addMonthStartWeekdayPadding)
|
||||
}
|
||||
|
||||
public func add(mood: Mood, forDate date: Date, entryType: EntryType) {
|
||||
PersistenceController.shared.add(mood: mood, forDate: date, entryType: entryType)
|
||||
updateData()
|
||||
}
|
||||
|
||||
public func update(entry: MoodEntry, toMood mood: Mood) {
|
||||
if PersistenceController.shared.update(entryDate: entry.forDate!, withModd: mood) {
|
||||
updateData()
|
||||
if !PersistenceController.shared.update(entryDate: entry.forDate!, withModd: mood) {
|
||||
#warning("show error")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +94,6 @@ class HomeViewViewModel: ObservableObject {
|
||||
|
||||
do {
|
||||
try PersistenceController.shared.viewContext.save()
|
||||
updateData()
|
||||
} catch {
|
||||
// Replace this implementation with code to handle the error appropriately.
|
||||
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
|
||||
|
||||
@@ -66,7 +66,6 @@ struct MainTabView: View {
|
||||
}
|
||||
})
|
||||
.onChange(of: theme, perform: { value in
|
||||
print("changed to ", value)
|
||||
switch theme {
|
||||
case .system:
|
||||
UIApplication.shared.windows.first?.overrideUserInterfaceStyle = .unspecified
|
||||
|
||||
@@ -11,8 +11,12 @@ struct MonthView: 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
|
||||
|
||||
// store a value that gets changed when user updates custom colors to update the view since the moodTint doesn't change
|
||||
@AppStorage(UserDefaultsStore.Keys.customMoodTintUpdateNumber.rawValue, store: GroupUserDefaults.groupDefaults) private var customMoodTintUpdateNumber: Int = 0
|
||||
|
||||
@ObservedObject var viewModel = HomeViewViewModel(addMonthStartWeekdayPadding: true)
|
||||
|
||||
@StateObject private var selectedDetail = StupidAssDetailViewObservableObject()
|
||||
@@ -45,7 +49,7 @@ struct MonthView: View {
|
||||
}
|
||||
} else {
|
||||
ScrollView {
|
||||
LazyVStack(spacing: 5, pinnedViews: [.sectionHeaders]) {
|
||||
VStack(spacing: 5) {
|
||||
ForEach(viewModel.grouped.sorted(by: { $0.key < $1.key }), id: \.key) { year, months in
|
||||
|
||||
// for reach month
|
||||
@@ -69,10 +73,9 @@ struct MonthView: View {
|
||||
onDismiss: didDismiss) {
|
||||
selectedDetail.fuckingWrapped
|
||||
}
|
||||
.edgesIgnoringSafeArea(.top)
|
||||
}
|
||||
}
|
||||
.padding(.top)
|
||||
.padding([.top, .bottom])
|
||||
.background(
|
||||
theme.currentTheme.bg
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
@@ -108,11 +111,7 @@ extension MonthView {
|
||||
.foregroundColor(Color(UIColor.darkGray))
|
||||
.font(.system(size: 20))
|
||||
}).sheet(isPresented: $showingSheet) {
|
||||
SettingsView(editedDataClosure: {
|
||||
withAnimation{
|
||||
viewModel.updateData()
|
||||
}
|
||||
})
|
||||
SettingsView()
|
||||
}
|
||||
.padding(.top, 60)
|
||||
.padding(.trailing)
|
||||
@@ -125,10 +124,14 @@ extension MonthView {
|
||||
// view that make up the list body
|
||||
extension MonthView {
|
||||
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)
|
||||
ZStack {
|
||||
Text("\(Random.monthName(fromMonthInt: month)) \(String(year))")
|
||||
.font(.body)
|
||||
.foregroundColor(theme.currentTheme.labelColor)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
Text(String(customMoodTintUpdateNumber))
|
||||
.hidden()
|
||||
}
|
||||
}
|
||||
|
||||
private func homeViewTwoMonthListView(month: Int, year: Int, entries: [MoodEntry]) -> some View {
|
||||
|
||||
@@ -10,9 +10,7 @@ import CloudKitSyncMonitor
|
||||
|
||||
struct SettingsView: View {
|
||||
@Environment(\.dismiss) var dismiss
|
||||
|
||||
let editedDataClosure: (() -> Void)
|
||||
|
||||
|
||||
@State private var showOnboarding = false
|
||||
|
||||
@State private var showSpecialThanks = false
|
||||
@@ -104,7 +102,6 @@ struct SettingsView: View {
|
||||
theme.currentTheme.secondaryBGColor
|
||||
Button(action: {
|
||||
PersistenceController.shared.populateTestData()
|
||||
editedDataClosure()
|
||||
}, label: {
|
||||
Text("Add test data")
|
||||
.foregroundColor(theme.currentTheme.labelColor)
|
||||
@@ -120,7 +117,6 @@ struct SettingsView: View {
|
||||
theme.currentTheme.secondaryBGColor
|
||||
Button(action: {
|
||||
PersistenceController.shared.clearDB()
|
||||
editedDataClosure()
|
||||
}, label: {
|
||||
Text("Clear DB")
|
||||
.foregroundColor(theme.currentTheme.labelColor)
|
||||
@@ -224,13 +220,9 @@ struct SettingsView: View {
|
||||
|
||||
struct SettingsView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
SettingsView(editedDataClosure: {
|
||||
|
||||
})
|
||||
SettingsView()
|
||||
|
||||
SettingsView(editedDataClosure: {
|
||||
|
||||
})
|
||||
SettingsView()
|
||||
.preferredColorScheme(.dark)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +108,6 @@ struct CurrentStreakTemplate: View, SharingTemplate {
|
||||
HStack(alignment: .center) {
|
||||
Button(action: {
|
||||
let _image = self.image
|
||||
print(_image)
|
||||
self.shareImage.showFuckingSheet = true
|
||||
self.shareImage.fuckingWrappedShrable = _image
|
||||
}, label: {
|
||||
|
||||
@@ -182,7 +182,6 @@ struct LongestStreakTemplate: View, SharingTemplate {
|
||||
HStack(alignment: .center) {
|
||||
Button(action: {
|
||||
let _image = self.image
|
||||
print(_image)
|
||||
self.shareImage.showFuckingSheet = true
|
||||
self.shareImage.fuckingWrappedShrable = _image
|
||||
}, label: {
|
||||
|
||||
@@ -30,6 +30,9 @@ struct SwitchableView: View {
|
||||
@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
|
||||
|
||||
// store a value that gets changed when user updates custom colors to update the view since the moodTint doesn't change
|
||||
@AppStorage(UserDefaultsStore.Keys.customMoodTintUpdateNumber.rawValue, store: GroupUserDefaults.groupDefaults) private var customMoodTintUpdateNumber: Int = 0
|
||||
|
||||
init(daysBack: Int, viewType: Binding<MainSwitchableViewType>, headerTypeChanged: @escaping ((MainSwitchableViewType) -> Void)) {
|
||||
self.daysBack = daysBack
|
||||
self.headerTypeChanged = headerTypeChanged
|
||||
@@ -39,9 +42,17 @@ struct SwitchableView: View {
|
||||
var body: some View {
|
||||
VStack {
|
||||
ZStack {
|
||||
Text(String(customMoodTintUpdateNumber))
|
||||
.hidden()
|
||||
switch viewType {
|
||||
case .total:
|
||||
HeaderStatsView(fakeData: false, backDays: daysBack, moodTint: moodTint)
|
||||
HeaderStatsView(fakeData: false, backDays: daysBack, moodTint: [
|
||||
moodTint.color(forMood: .great),
|
||||
moodTint.color(forMood: .good),
|
||||
moodTint.color(forMood: .average),
|
||||
moodTint.color(forMood: .bad),
|
||||
moodTint.color(forMood: .horrible)
|
||||
])
|
||||
.padding([.leading, .trailing], -15)
|
||||
.padding([.top, .bottom], 8)
|
||||
.allowsHitTesting(false)
|
||||
|
||||
Reference in New Issue
Block a user