// // VotingLayoutPickerView.swift // Feels (iOS) // // Created by Claude Code on 12/9/24. // import SwiftUI struct VotingLayoutPickerView: View { @AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system @AppStorage(UserDefaultsStore.Keys.votingLayoutStyle.rawValue, store: GroupUserDefaults.groupDefaults) private var votingLayoutStyle: Int = 0 private var textColor: Color { theme.currentTheme.labelColor } private var currentLayout: VotingLayoutStyle { VotingLayoutStyle(rawValue: votingLayoutStyle) ?? .horizontal } var body: some View { ZStack { theme.currentTheme.secondaryBGColor VStack(alignment: .leading, spacing: 12) { Text("Voting Layout") .font(.headline) .foregroundColor(textColor) .padding(.horizontal) .padding(.top) ScrollView(.horizontal, showsIndicators: false) { HStack(spacing: 8) { ForEach(VotingLayoutStyle.allCases, id: \.rawValue) { layout in Button(action: { if UIAccessibility.isReduceMotionEnabled { votingLayoutStyle = layout.rawValue } else { withAnimation(.easeInOut(duration: 0.2)) { votingLayoutStyle = layout.rawValue } } EventLogger.log(event: "change_voting_layout", withData: ["layout": layout.displayName]) }) { VStack(spacing: 6) { layoutIcon(for: layout) .frame(width: 44, height: 44) .foregroundColor(currentLayout == layout ? .accentColor : textColor.opacity(0.6)) Text(layout.displayName) .font(.caption) .foregroundColor(currentLayout == layout ? .accentColor : textColor.opacity(0.8)) } .frame(width: 70) .padding(.vertical, 12) .background( RoundedRectangle(cornerRadius: 10) .fill(currentLayout == layout ? Color.accentColor.opacity(0.15) : Color.clear) ) .overlay( RoundedRectangle(cornerRadius: 10) .stroke(currentLayout == layout ? Color.accentColor : Color.clear, lineWidth: 2) ) } .buttonStyle(.plain) } } .padding(.horizontal) } .padding(.bottom) } } .fixedSize(horizontal: false, vertical: true) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) } @ViewBuilder private func layoutIcon(for layout: VotingLayoutStyle) -> some View { switch layout { case .horizontal: HStack(spacing: 4) { ForEach(0..<5) { _ in Circle() .frame(width: 6, height: 6) } } case .cards: LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible())], spacing: 3) { ForEach(0..<6) { _ in RoundedRectangle(cornerRadius: 2) .frame(width: 10, height: 12) } } case .radial: ZStack { ForEach(0..<5) { index in Circle() .frame(width: 6, height: 6) .offset(radialOffset(index: index, total: 5, radius: 16)) } } case .stacked: VStack(spacing: 3) { ForEach(0..<4) { _ in RoundedRectangle(cornerRadius: 2) .frame(width: 32, height: 6) } } case .aura: // Glowing orbs in 2 rows VStack(spacing: 4) { HStack(spacing: 6) { ForEach(0..<3, id: \.self) { _ in ZStack { Circle() .fill(RadialGradient(colors: [.green.opacity(0.5), .clear], center: .center, startRadius: 0, endRadius: 8)) .frame(width: 14, height: 14) Circle() .fill(.green) .frame(width: 8, height: 8) } } } HStack(spacing: 10) { ForEach(0..<2, id: \.self) { _ in ZStack { Circle() .fill(RadialGradient(colors: [.green.opacity(0.5), .clear], center: .center, startRadius: 0, endRadius: 8)) .frame(width: 14, height: 14) Circle() .fill(.green) .frame(width: 8, height: 8) } } } } case .orbit: // Center core with orbiting planets ZStack { // Orbital ring Circle() .stroke(Color.primary.opacity(0.2), lineWidth: 1) .frame(width: 32, height: 32) // Center core Circle() .fill(Color.primary.opacity(0.8)) .frame(width: 8, height: 8) // Orbiting planets ForEach(0..<5, id: \.self) { index in Circle() .fill(Color.accentColor) .frame(width: 6, height: 6) .offset(orbitOffset(index: index, total: 5, radius: 16)) } } case .neon: // Equalizer bars ZStack { // Grid background hint RoundedRectangle(cornerRadius: 4) .fill(Color.black) .frame(width: 36, height: 36) // Equalizer bars HStack(spacing: 2) { ForEach(0..<5, id: \.self) { index in let heights: [CGFloat] = [24, 18, 14, 10, 8] RoundedRectangle(cornerRadius: 1) .fill( LinearGradient( colors: [Color(red: 0, green: 1, blue: 0.82), Color(red: 1, green: 0, blue: 0.8)], startPoint: .top, endPoint: .bottom ) ) .frame(width: 4, height: heights[index]) } } } } } private func radialOffset(index: Int, total: Int, radius: CGFloat) -> CGSize { let angle = Double.pi - (Double.pi * Double(index) / Double(total - 1)) return CGSize( width: radius * CGFloat(cos(angle)), height: -radius * CGFloat(sin(angle)) + 4 ) } private func orbitOffset(index: Int, total: Int, radius: CGFloat) -> CGSize { // Start from top (-π/2) and go clockwise let startAngle = -Double.pi / 2 let angleStep = (2 * Double.pi) / Double(total) let angle = startAngle + angleStep * Double(index) return CGSize( width: radius * CGFloat(cos(angle)), height: radius * CGFloat(sin(angle)) ) } } struct VotingLayoutPickerView_Previews: PreviewProvider { static var previews: some View { VotingLayoutPickerView() .padding() } }