diff --git a/FeelsWidget2/FeelsWidget.swift b/FeelsWidget2/FeelsWidget.swift index ffed556..4da17f1 100644 --- a/FeelsWidget2/FeelsWidget.swift +++ b/FeelsWidget2/FeelsWidget.swift @@ -337,6 +337,10 @@ struct SmallWidgetView: View { var entry: Provider.Entry var todayView: WatchTimelineView? + private var showVotingForToday: Bool { + entry.hasSubscription && !entry.hasVotedToday + } + private var dayFormatter: DateFormatter { let f = DateFormatter() f.dateFormat = "EEEE" @@ -360,7 +364,10 @@ struct SmallWidgetView: View { } var body: some View { - if let today = todayView { + if showVotingForToday { + // Show interactive voting buttons + VotingView(family: .systemSmall, promptText: entry.promptText) + } else if let today = todayView { VStack(spacing: 0) { Spacer() @@ -431,40 +438,45 @@ struct MediumWidgetView: View { } var body: some View { - GeometryReader { geo in - let cellHeight = geo.size.height - 36 + if showVotingForToday { + // Show interactive voting buttons + VotingView(family: .systemMedium, promptText: entry.promptText) + } else { + GeometryReader { geo in + let cellHeight = geo.size.height - 36 - VStack(spacing: 4) { - // Header - HStack { - Text("Last 5 Days") - .font(.subheadline.weight(.semibold)) - .foregroundStyle(.primary) - Text("·") - .foregroundStyle(.secondary) - Text(headerDateRange) - .font(.caption) - .foregroundStyle(.secondary) - Spacer() - } - .padding(.horizontal, 14) - .padding(.top, 10) - - // Single row of 5 days - HStack(spacing: 8) { - ForEach(Array(timeLineView.enumerated()), id: \.element.id) { index, item in - MediumDayCell( - dayLabel: dayFormatter.string(from: item.date), - dateLabel: dateFormatter.string(from: item.date), - image: item.image, - color: item.color, - isToday: index == 0, - height: cellHeight - ) + VStack(spacing: 4) { + // Header + HStack { + Text("Last 5 Days") + .font(.subheadline.weight(.semibold)) + .foregroundStyle(.primary) + Text("·") + .foregroundStyle(.secondary) + Text(headerDateRange) + .font(.caption) + .foregroundStyle(.secondary) + Spacer() } + .padding(.horizontal, 14) + .padding(.top, 10) + + // Single row of 5 days + HStack(spacing: 8) { + ForEach(Array(timeLineView.enumerated()), id: \.element.id) { index, item in + MediumDayCell( + dayLabel: dayFormatter.string(from: item.date), + dateLabel: dateFormatter.string(from: item.date), + image: item.image, + color: item.color, + isToday: index == 0, + height: cellHeight + ) + } + } + .padding(.horizontal, 10) + .padding(.bottom, 10) } - .padding(.horizontal, 10) - .padding(.bottom, 10) } } } @@ -538,57 +550,62 @@ struct LargeWidgetView: View { } var body: some View { - GeometryReader { geo in - let cellHeight = (geo.size.height - 70) / 2 // Subtract header height, divide by 2 rows + if showVotingForToday { + // Show interactive voting buttons for large widget + LargeVotingView(promptText: entry.promptText) + } else { + GeometryReader { geo in + let cellHeight = (geo.size.height - 70) / 2 // Subtract header height, divide by 2 rows - VStack(spacing: 6) { - // Header - HStack { - VStack(alignment: .leading, spacing: 2) { - Text("Last 10 Days") - .font(.subheadline.weight(.semibold)) - .foregroundStyle(.primary) - Text(headerDateRange) - .font(.caption2) - .foregroundStyle(.secondary) - } - Spacer() - } - .padding(.horizontal, 12) - .padding(.top, 8) - - // Calendar grid - 2 rows of 5 VStack(spacing: 6) { - // First row (most recent 5) - HStack(spacing: 6) { - ForEach(Array(timeLineView.prefix(5).enumerated()), id: \.element.id) { index, item in - DayCell( - dayLabel: dayFormatter.string(from: item.date), - dateLabel: dateFormatter.string(from: item.date), - image: item.image, - color: item.color, - isToday: index == 0, - height: cellHeight - ) + // Header + HStack { + VStack(alignment: .leading, spacing: 2) { + Text("Last 10 Days") + .font(.subheadline.weight(.semibold)) + .foregroundStyle(.primary) + Text(headerDateRange) + .font(.caption2) + .foregroundStyle(.secondary) } + Spacer() } + .padding(.horizontal, 12) + .padding(.top, 8) - // Second row (older 5) - HStack(spacing: 6) { - ForEach(Array(timeLineView.suffix(5).enumerated()), id: \.element.id) { _, item in - DayCell( - dayLabel: dayFormatter.string(from: item.date), - dateLabel: dateFormatter.string(from: item.date), - image: item.image, - color: item.color, - isToday: false, - height: cellHeight - ) + // Calendar grid - 2 rows of 5 + VStack(spacing: 6) { + // First row (most recent 5) + HStack(spacing: 6) { + ForEach(Array(timeLineView.prefix(5).enumerated()), id: \.element.id) { index, item in + DayCell( + dayLabel: dayFormatter.string(from: item.date), + dateLabel: dateFormatter.string(from: item.date), + image: item.image, + color: item.color, + isToday: index == 0, + height: cellHeight + ) + } + } + + // Second row (older 5) + HStack(spacing: 6) { + ForEach(Array(timeLineView.suffix(5).enumerated()), id: \.element.id) { _, item in + DayCell( + dayLabel: dayFormatter.string(from: item.date), + dateLabel: dateFormatter.string(from: item.date), + image: item.image, + color: item.color, + isToday: false, + height: cellHeight + ) + } } } + .padding(.horizontal, 10) + .padding(.bottom, 8) } - .padding(.horizontal, 10) - .padding(.bottom, 8) } } } @@ -646,6 +663,63 @@ struct DayCell: View { .frame(maxWidth: .infinity) } } + +// MARK: - Large Voting View + +struct LargeVotingView: View { + let promptText: String + + private var moodTint: MoodTintable.Type { + UserDefaultsStore.moodTintable() + } + + private var moodImages: MoodImagable.Type { + UserDefaultsStore.moodMoodImagable() + } + + var body: some View { + VStack(spacing: 24) { + Spacer() + + Text(promptText) + .font(.title2.weight(.semibold)) + .foregroundStyle(.primary) + .multilineTextAlignment(.center) + .lineLimit(2) + .minimumScaleFactor(0.8) + + // Large mood buttons in a row + HStack(spacing: 20) { + ForEach([Mood.great, .good, .average, .bad, .horrible], id: \.rawValue) { mood in + Button(intent: VoteMoodIntent(mood: mood)) { + VStack(spacing: 8) { + moodImages.icon(forMood: mood) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 56, height: 56) + .foregroundColor(moodTint.color(forMood: mood)) + + Text(mood.strValue) + .font(.caption.weight(.medium)) + .foregroundColor(moodTint.color(forMood: mood)) + } + .padding(.vertical, 12) + .padding(.horizontal, 8) + .background( + RoundedRectangle(cornerRadius: 16) + .fill(moodTint.color(forMood: mood).opacity(0.15)) + ) + } + .buttonStyle(.plain) + } + } + + Spacer() + } + .padding() + } +} + /**********************************************************/ struct FeelsGraphicWidgetEntryView : View { @Environment(\.sizeCategory) var sizeCategory