// // SharingStylePickerView.swift // Reflect // // A horizontal pager that lets users swipe between design variations // for a sharing template, then export the selected design. // import SwiftUI struct SharingDesign: Identifiable { let id = UUID() let name: String let shareView: AnyView let image: () -> UIImage } struct SharePickerData: Identifiable { let id = UUID() let title: String let designs: [SharingDesign] } struct SharingStylePickerView: View { let title: String let designs: [SharingDesign] @State private var selectedIndex = 0 @StateObject private var shareImage = ShareImageStateViewModel() @Environment(\.presentationMode) var presentationMode @AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system private var textColor: Color { theme.currentTheme.labelColor } private var safeIndex: Int { guard !designs.isEmpty else { return 0 } return min(selectedIndex, designs.count - 1) } var body: some View { if designs.isEmpty { Text("No designs available") .foregroundColor(textColor) } else { VStack(spacing: 0) { // Title bar HStack { Button(action: { presentationMode.wrappedValue.dismiss() }) { Text("Exit") .font(.headline) .foregroundColor(.red) } .accessibilityIdentifier(AccessibilityID.Sharing.exitButton) Spacer() Text(title) .font(.headline) .foregroundColor(textColor) Spacer() // Invisible placeholder for symmetry Text("Exit") .font(.headline) .hidden() } .padding(.horizontal) .padding(.vertical, 12) // Pager showing scaled-down shareViews TabView(selection: $selectedIndex) { ForEach(Array(designs.enumerated()), id: \.offset) { index, design in design.shareView .scaleEffect(0.45, anchor: .center) .frame(maxWidth: .infinity, maxHeight: .infinity) .clipped() .tag(index) } } .tabViewStyle(.page(indexDisplayMode: .always)) // Design name label Text(designs[safeIndex].name) .font(.title3) .fontWeight(.semibold) .foregroundColor(textColor) .padding(.top, 8) .animation(.none, value: selectedIndex) // Share button Button(action: { let img = designs[safeIndex].image() shareImage.selectedShareImage = img shareImage.showSheet = true }) { Text("Share") .font(.title2) .fontWeight(.bold) .foregroundColor(.white) .frame(maxWidth: .infinity) .padding(.vertical, 14) .background(Color.green) .cornerRadius(14) } .accessibilityIdentifier(AccessibilityID.Sharing.shareButton) .padding(.horizontal, 24) .padding(.top, 12) .padding(.bottom, 24) } .background(theme.currentTheme.bg.edgesIgnoringSafeArea(.all)) .sheet(isPresented: $shareImage.showSheet) { if let uiImage = shareImage.selectedShareImage { ShareSheet(photo: uiImage) } } } } } // MARK: - Longest Streak Picker (mood selection + style picker) struct LongestStreakPickerView: View { let startDate: Date let endDate: Date @State private var selectedMood: Mood = .great @State private var streakEntries: [MoodEntryModel] = [] @State private var selectedIndex = 0 @StateObject private var shareImage = ShareImageStateViewModel() @Environment(\.presentationMode) var presentationMode @AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system private var textColor: Color { theme.currentTheme.labelColor } private var designs: [SharingDesign] { [ SharingDesign( name: "Gradient Bar", shareView: AnyView(LongestStreakV2(streakEntries: streakEntries, selectedMood: selectedMood)), image: { LongestStreakV2(streakEntries: streakEntries, selectedMood: selectedMood).image } ), SharingDesign( name: "Dark Badge", shareView: AnyView(LongestStreakV3(streakEntries: streakEntries, selectedMood: selectedMood)), image: { LongestStreakV3(streakEntries: streakEntries, selectedMood: selectedMood).image } ), ] } var body: some View { VStack(spacing: 0) { // Title bar with mood picker HStack { Button(action: { presentationMode.wrappedValue.dismiss() }) { Text("Exit") .font(.headline) .foregroundColor(.red) } .accessibilityIdentifier(AccessibilityID.Sharing.exitButton) Spacer() Menu { ForEach(Mood.allValues) { mood in Button(mood.strValue) { selectedMood = mood recomputeStreak() } .accessibilityIdentifier(AccessibilityID.Sharing.moodMenuButton(mood.strValue)) } } label: { HStack(spacing: 6) { Text(selectedMood.strValue) .font(.headline) .foregroundColor(textColor) Image(systemName: "chevron.down") .font(.caption) .foregroundColor(textColor.opacity(0.6)) } } .accessibilityIdentifier(AccessibilityID.Sharing.moodMenu) Spacer() Text("Exit") .font(.headline) .hidden() } .padding(.horizontal) .padding(.vertical, 12) // Pager TabView(selection: $selectedIndex) { ForEach(Array(designs.enumerated()), id: \.offset) { index, design in design.shareView .scaleEffect(0.45, anchor: .center) .frame(maxWidth: .infinity, maxHeight: .infinity) .clipped() .tag(index) } } .tabViewStyle(.page(indexDisplayMode: .always)) // Design name Text(designs[selectedIndex].name) .font(.title3) .fontWeight(.semibold) .foregroundColor(textColor) .padding(.top, 8) .animation(.none, value: selectedIndex) // Share button Button(action: { let img = designs[selectedIndex].image() shareImage.selectedShareImage = img shareImage.showSheet = true }) { Text("Share") .font(.title2) .fontWeight(.bold) .foregroundColor(.white) .frame(maxWidth: .infinity) .padding(.vertical, 14) .background(Color.green) .cornerRadius(14) } .accessibilityIdentifier(AccessibilityID.Sharing.shareButton) .padding(.horizontal, 24) .padding(.top, 12) .padding(.bottom, 24) } .background(theme.currentTheme.bg.edgesIgnoringSafeArea(.all)) .sheet(isPresented: $shareImage.showSheet) { if let uiImage = shareImage.selectedShareImage { ShareSheet(photo: uiImage) } } .onAppear { recomputeStreak() } } @MainActor private func recomputeStreak() { let allEntries = DataController.shared.getData( startDate: startDate, endDate: endDate, includedDays: [1, 2, 3, 4, 5, 6, 7] ) var splitArrays = createSubArrays(fromMoodEntries: allEntries, splitOn: selectedMood) splitArrays.sort { $0.count > $1.count } streakEntries = splitArrays.first ?? [] } private func createSubArrays(fromMoodEntries: [MoodEntryModel], splitOn: Mood) -> [[MoodEntryModel]] { var splitArrays = [[MoodEntryModel]]() var currentSplit = [MoodEntryModel]() for entry in fromMoodEntries { if entry.mood == splitOn { currentSplit.append(entry) } else { splitArrays.append(currentSplit) currentSplit.removeAll() } } splitArrays.append(currentSplit) return splitArrays } }