Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions FastPath/AppFeature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,58 @@

import SwiftUI
import ComposableArchitecture
import Foundation // Ensure Foundation is imported

struct UserDefaultsClient {
var integerForKey: @Sendable (String) -> Int? // Make @Sendable for actor safety
var setInteger: @Sendable (Int, String) async -> Void // Make async if it could involve async operations, though UserDefaults is sync
}

extension UserDefaultsClient: DependencyKey {
static let liveValue = Self(
integerForKey: { key in
// UserDefaults.integer(forKey:) returns 0 if key DNE or is not an Int.
// To correctly return nil if the key genuinely doesn't exist,
// we check object(forKey:) first.
if UserDefaults.standard.object(forKey: key) == nil {
return nil
}
return UserDefaults.standard.integer(forKey: key)
},
setInteger: { value, key in
UserDefaults.standard.set(value, forKey: key)
}
)

// Add a testValue if you plan to write tests for this
static let testValue = Self(
integerForKey: { _ in nil }, // Default test implementation
setInteger: { _, _ in }
)
}

extension DependencyValues {
var userDefaultsClient: UserDefaultsClient {
get { self[UserDefaultsClient.self] }
set { self[UserDefaultsClient.self] = newValue }
}
}

@Reducer
struct AppFeature {
@Dependency(\.userDefaultsClient) var userDefaultsClient

struct State: Equatable {
var fasting = FastingFeature.State()
var path = StackState<Path.State>()
var currentInspirationalMessage: String? = nil
}

enum Action: Equatable {
case fasting(FastingFeature.Action)
case path(StackAction<Path.State, Path.Action>)
case loadInspirationalMessage
case inspirationalMessageResponse(String)
}

@Reducer
Expand Down Expand Up @@ -47,6 +88,25 @@ struct AppFeature {
case .fasting(.showHistory):
state.path.append(.history(state.fasting))
return .none

case .loadInspirationalMessage:
let lastDisplayedMessageIndex = userDefaultsClient.integerForKey("lastDisplayedMessageIndex") ?? -1

guard !Quotes.allMessages.isEmpty else {
return .send(.inspirationalMessageResponse("No messages available."))
}

let nextIndex = (lastDisplayedMessageIndex + 1) % Quotes.allMessages.count
let message = Quotes.allMessages[nextIndex]

return .run { send in
await userDefaultsClient.setInteger(nextIndex, "lastDisplayedMessageIndex")
await send(.inspirationalMessageResponse(message))
}

case .inspirationalMessageResponse(let message):
state.currentInspirationalMessage = message
return .none

case .fasting:
return .none
Expand Down
87 changes: 87 additions & 0 deletions FastPath/Models/InspirationalMessages.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// InspirationalMessages.swift
import Foundation

struct Quotes {
static let allMessages: [String] = [
"Every fast is a step towards a healthier you.",
"Discipline today, results tomorrow.",
"Your body is capable of amazing things.",
"Stay focused on your goals.",
"Small changes lead to big results.",
"Believe in your strength to succeed.",
"Patience and persistence are key.",
"You are stronger than you think.",
"Embrace the journey, trust the process.",
"Today's effort is tomorrow's reward.",
"One day at a time, one fast at a time.",
"The best project you'll ever work on is you.",
"Nourish your body, cherish your progress.",
"Celebrate every small victory.",
"Consistency is what transforms average into excellence.",
"Focus on progress, not perfection.",
"You have the power to make a change.",
"Let your determination light your way.",
"Every moment is a fresh beginning.",
"Be proud of how far you've come.",
"The journey of a thousand miles begins with a single step.",
"Keep pushing, even when it's tough.",
"Your health is an investment, not an expense.",
"Unlock your potential, one fast at a time.",
"Make yourself a priority.",
"The only bad fast is the one not started.",
"Success is the sum of small efforts, repeated.",
"Challenge yourself, you might surprise yourself.",
"Listen to your body, it knows what it needs.",
"Transform your mind, transform your body.",
"It's not about being perfect, it's about effort.",
"Find strength in your discipline.",
"Every completed fast builds momentum.",
"Fuel your body, respect your temple.",
"Small steps every day add up to big changes.",
"The power of choice is your greatest tool.",
"Stay committed to your decisions.",
"Progress is progress, no matter how small.",
"You are creating a healthier future.",
"Believe in the process, trust your journey.",
"Let go of what was, embrace what will be.",
"Your dedication is inspiring.",
"Keep your eyes on the prize.",
"The discipline of today shapes the success of tomorrow.",
"You are resilient and capable.",
"Embrace the challenge, enjoy the results.",
"Every fast is a testament to your willpower.",
"Stay positive, stay strong.",
"Your journey is unique and valid.",
"Invest in yourself, you're worth it.",
"The path to health is paved with consistency.",
"Find joy in the process of becoming stronger.",
"Your commitment is your greatest asset.",
"Keep going, your future self will thank you.",
"Master your habits, master your life.",
"Each fast is a new opportunity for growth.",
"Strength doesn't come from what you can do, it comes from overcoming things you once thought you couldn't.",
"Be patient with yourself, great things take time.",
"The best view comes after the hardest climb.",
"Focus on your 'why' to stay motivated.",
"Turn 'I can't' into 'I can'.",
"Your body achieves what the mind believes.",
"Don't wait for opportunity, create it.",
"Every choice you make is a step towards your goal.",
"Be stronger than your excuses.",
"The discipline you build now will serve you for life.",
"You are in control of your choices.",
"Let your progress be your motivation.",
"Small, consistent efforts yield powerful results.",
"Trust in your ability to persevere.",
"Every sunrise brings a new chance to shine.",
"Dedication sees you through the tough times.",
"You are building a better version of yourself.",
"Celebrate your efforts, not just the outcomes.",
"The reward of discipline is a healthier, happier you.",
"Keep your spirit strong and your goals in sight.",
"Embrace the power of now.",
"Your journey, your rules, your success.",
"Stay true to your path and purpose.",
"You've got this!"
]
}
51 changes: 34 additions & 17 deletions FastPath/Views/AppView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,40 @@ struct AppView: View {
let store: StoreOf<AppFeature>

var body: some View {
NavigationStackStore(
store.scope(state: \.path, action: { .path($0) })
) {
FastingView(
store: store.scope(
state: \.fasting,
action: { .fasting($0) }
)
)
} destination: { state in
switch state {
case .history:
CaseLet(
/AppFeature.Path.State.history,
action: AppFeature.Path.Action.history,
then: HistoryView.init(store:)
)
WithViewStore(self.store, observe: { $0 }) { viewStore in // Observe the whole state or specific parts
VStack { // Wrap existing content in a VStack
// Display the inspirational message
if let message = viewStore.currentInspirationalMessage, !message.isEmpty {
Text(message)
.padding() // Add some padding for better appearance
.font(.caption) // Style as per TR-IM-004 (e.g., smaller font)
.foregroundColor(.gray) // Subtle color
.multilineTextAlignment(.center) // Center if it's long
}

// Existing NavigationStackStore
NavigationStackStore(
self.store.scope(state: \.path, action: { .path($0) })
) {
FastingView(
store: self.store.scope(
state: \.fasting,
action: { .fasting($0) }
)
)
} destination: { state in
switch state {
case .history:
CaseLet(
/AppFeature.Path.State.history,
action: AppFeature.Path.Action.history,
then: HistoryView.init(store:)
)
}
}
}
.onAppear {
viewStore.send(.loadInspirationalMessage)
}
}
}
Expand Down