Rebrand entire project from Feels to Reflect
Complete rename across all bundle IDs, App Groups, CloudKit containers, StoreKit product IDs, data store filenames, URL schemes, logger subsystems, Swift identifiers, user-facing strings (7 languages), file names, directory names, Xcode project, schemes, assets, and documentation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
232
Reflect Watch App/ReflectComplication.swift
Normal file
232
Reflect Watch App/ReflectComplication.swift
Normal file
@@ -0,0 +1,232 @@
|
||||
//
|
||||
// ReflectComplication.swift
|
||||
// Reflect Watch App
|
||||
//
|
||||
// WidgetKit complications for Apple Watch.
|
||||
//
|
||||
|
||||
import WidgetKit
|
||||
import SwiftUI
|
||||
|
||||
// MARK: - Timeline Provider
|
||||
|
||||
struct ReflectTimelineProvider: TimelineProvider {
|
||||
func placeholder(in context: Context) -> ReflectEntry {
|
||||
ReflectEntry(date: Date(), mood: nil, streak: 0)
|
||||
}
|
||||
|
||||
func getSnapshot(in context: Context, completion: @escaping (ReflectEntry) -> Void) {
|
||||
Task { @MainActor in
|
||||
let entry = createEntry()
|
||||
completion(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func getTimeline(in context: Context, completion: @escaping (Timeline<ReflectEntry>) -> Void) {
|
||||
Task { @MainActor in
|
||||
let entry = createEntry()
|
||||
|
||||
// Refresh at midnight for the next day
|
||||
let tomorrow = Calendar.current.startOfDay(
|
||||
for: Calendar.current.date(byAdding: .day, value: 1, to: Date())!
|
||||
)
|
||||
|
||||
let timeline = Timeline(entries: [entry], policy: .after(tomorrow))
|
||||
completion(timeline)
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private func createEntry() -> ReflectEntry {
|
||||
let todayEntry = ExtensionDataProvider.shared.getTodayEntry()
|
||||
let streak = ExtensionDataProvider.shared.getCurrentStreak()
|
||||
|
||||
return ReflectEntry(
|
||||
date: Date(),
|
||||
mood: todayEntry?.mood,
|
||||
streak: streak
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Timeline Entry
|
||||
|
||||
struct ReflectEntry: TimelineEntry {
|
||||
let date: Date
|
||||
let mood: Mood?
|
||||
let streak: Int
|
||||
}
|
||||
|
||||
// MARK: - Complication Views
|
||||
|
||||
struct ReflectComplicationEntryView: View {
|
||||
var entry: ReflectEntry
|
||||
@Environment(\.widgetFamily) var family
|
||||
|
||||
var body: some View {
|
||||
switch family {
|
||||
case .accessoryCircular:
|
||||
CircularView(entry: entry)
|
||||
case .accessoryCorner:
|
||||
CornerView(entry: entry)
|
||||
case .accessoryInline:
|
||||
InlineView(entry: entry)
|
||||
case .accessoryRectangular:
|
||||
RectangularView(entry: entry)
|
||||
default:
|
||||
CircularView(entry: entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Circular Complication
|
||||
|
||||
struct CircularView: View {
|
||||
let entry: ReflectEntry
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
AccessoryWidgetBackground()
|
||||
|
||||
if let mood = entry.mood {
|
||||
Text(mood.watchEmoji)
|
||||
.font(.system(size: 24))
|
||||
} else {
|
||||
VStack(spacing: 0) {
|
||||
Image(systemName: "face.smiling")
|
||||
.font(.system(size: 18))
|
||||
Text("Log")
|
||||
.font(.system(size: 10))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Corner Complication
|
||||
|
||||
struct CornerView: View {
|
||||
let entry: ReflectEntry
|
||||
|
||||
var body: some View {
|
||||
if let mood = entry.mood {
|
||||
Text(mood.watchEmoji)
|
||||
.font(.system(size: 20))
|
||||
.widgetLabel {
|
||||
Text(mood.widgetDisplayName)
|
||||
}
|
||||
} else {
|
||||
Image(systemName: "face.smiling")
|
||||
.font(.system(size: 20))
|
||||
.widgetLabel {
|
||||
Text("Log mood")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Inline Complication
|
||||
|
||||
struct InlineView: View {
|
||||
let entry: ReflectEntry
|
||||
|
||||
var body: some View {
|
||||
if entry.streak > 0 {
|
||||
Label("\(entry.streak) day streak", systemImage: "flame.fill")
|
||||
} else if let mood = entry.mood {
|
||||
Text("\(mood.watchEmoji) \(mood.widgetDisplayName)")
|
||||
} else {
|
||||
Label("Log your mood", systemImage: "face.smiling")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Rectangular Complication
|
||||
|
||||
struct RectangularView: View {
|
||||
let entry: ReflectEntry
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
if let mood = entry.mood {
|
||||
Text(mood.watchEmoji)
|
||||
.font(.system(size: 28))
|
||||
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text("Today")
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(.secondary)
|
||||
Text(mood.widgetDisplayName)
|
||||
.font(.system(size: 14, weight: .semibold))
|
||||
|
||||
if entry.streak > 1 {
|
||||
Label("\(entry.streak) days", systemImage: "flame.fill")
|
||||
.font(.system(size: 10))
|
||||
.foregroundColor(.orange)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Image(systemName: "face.smiling")
|
||||
.font(.system(size: 24))
|
||||
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text("Reflect")
|
||||
.font(.system(size: 14, weight: .semibold))
|
||||
Text("Tap to log mood")
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Widget Configuration
|
||||
|
||||
struct ReflectComplication: Widget {
|
||||
let kind: String = "ReflectComplication"
|
||||
|
||||
var body: some WidgetConfiguration {
|
||||
StaticConfiguration(kind: kind, provider: ReflectTimelineProvider()) { entry in
|
||||
ReflectComplicationEntryView(entry: entry)
|
||||
.containerBackground(.fill.tertiary, for: .widget)
|
||||
}
|
||||
.configurationDisplayName("Reflect")
|
||||
.description("See today's mood and streak.")
|
||||
.supportedFamilies([
|
||||
.accessoryCircular,
|
||||
.accessoryCorner,
|
||||
.accessoryInline,
|
||||
.accessoryRectangular
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Preview
|
||||
|
||||
#Preview("Circular - Mood") {
|
||||
CircularView(entry: ReflectEntry(date: Date(), mood: .great, streak: 5))
|
||||
.previewContext(WidgetPreviewContext(family: .accessoryCircular))
|
||||
}
|
||||
|
||||
#Preview("Circular - Empty") {
|
||||
CircularView(entry: ReflectEntry(date: Date(), mood: nil, streak: 0))
|
||||
.previewContext(WidgetPreviewContext(family: .accessoryCircular))
|
||||
}
|
||||
|
||||
#Preview("Rectangular - Mood") {
|
||||
RectangularView(entry: ReflectEntry(date: Date(), mood: .good, streak: 7))
|
||||
.previewContext(WidgetPreviewContext(family: .accessoryRectangular))
|
||||
}
|
||||
|
||||
#Preview("Inline - Streak") {
|
||||
InlineView(entry: ReflectEntry(date: Date(), mood: .great, streak: 5))
|
||||
.previewContext(WidgetPreviewContext(family: .accessoryInline))
|
||||
}
|
||||
|
||||
#Preview("Corner - Mood") {
|
||||
CornerView(entry: ReflectEntry(date: Date(), mood: .average, streak: 3))
|
||||
.previewContext(WidgetPreviewContext(family: .accessoryCorner))
|
||||
}
|
||||
Reference in New Issue
Block a user