// // ComposePollView.swift // Tusker // // Created by Shadowfacts on 4/28/21. // Copyright © 2021 Shadowfacts. All rights reserved. // import SwiftUI struct ComposePollView: View { private static let formatter: DateComponentsFormatter = { let f = DateComponentsFormatter() f.maximumUnitCount = 1 f.unitsStyle = .full f.allowedUnits = [.weekOfMonth, .day, .hour, .minute] return f }() @ObservedObject var draft: Draft @ObservedObject var poll: Draft.Poll @Environment(\.colorScheme) var colorScheme: ColorScheme @State private var duration: Duration init(draft: Draft, poll: Draft.Poll) { self.draft = draft self.poll = poll self._duration = State(initialValue: .fromTimeInterval(poll.duration) ?? .oneDay) } var body: some View { VStack { HStack { Text("Poll") .font(.headline) Spacer() Button(action: self.removePoll) { Image(systemName: "xmark") .imageScale(.small) .padding(4) } .accentColor(buttonForegroundColor) .background(Circle().foregroundColor(buttonBackgroundColor)) .hoverEffect() } ForEach(Array(poll.options.enumerated()), id: \.element.id) { (e) in ComposePollOption(poll: poll, option: e.element, optionIndex: e.offset) } .transition(.slide) Button(action: self.addOption) { Label("Add Option", systemImage: "plus") } HStack { // use .animation(nil) on pickers so frame doesn't have a size change animation when the text changes Picker(selection: $poll.multiple, label: Text(poll.multiple ? "Allow multiple choices" : "Single choice")) { Text("Allow multiple choices").tag(true) Text("Single choice").tag(false) } .animation(nil) .pickerStyle(MenuPickerStyle()) .frame(maxWidth: .infinity) Picker(selection: $duration, label: Text(verbatim: ComposePollView.formatter.string(from: duration.timeInterval)!)) { ForEach(Duration.allCases, id: \.self) { (duration) in Text(ComposePollView.formatter.string(from: duration.timeInterval)!).tag(duration) } } .animation(nil) .pickerStyle(MenuPickerStyle()) .frame(maxWidth: .infinity) } } .padding(8) .background( backgroundColor .cornerRadius(10) ) .onChange(of: duration, perform: { (value) in poll.duration = value.timeInterval }) } private var backgroundColor: Color { // in light mode, .secondarySystemBackground has a blue-ish hue, which we don't want colorScheme == .dark ? Color(UIColor.secondarySystemBackground) : Color(white: 0.95) } private var buttonBackgroundColor: Color { Color(white: colorScheme == .dark ? 0.1 : 0.8) } private var buttonForegroundColor: Color { Color(UIColor.label) } private func removePoll() { withAnimation { self.draft.poll = nil } } private func addOption() { withAnimation(.easeInOut(duration: 0.25)) { poll.options.append(Draft.Poll.Option("")) } } } extension ComposePollView { enum Duration: Hashable, Equatable, CaseIterable { case fiveMinutes, thirtyMinutes, oneHour, sixHours, oneDay, threeDays, sevenDays static func fromTimeInterval(_ ti: TimeInterval) -> Duration? { for it in allCases where it.timeInterval == ti { return it } return nil } var timeInterval: TimeInterval { switch self { case .fiveMinutes: return 5 * 60 case .thirtyMinutes: return 30 * 60 case .oneHour: return 60 * 60 case .sixHours: return 6 * 60 * 60 case .oneDay: return 24 * 60 * 60 case .threeDays: return 3 * 24 * 60 * 60 case .sevenDays: return 7 * 24 * 60 * 60 } } } } struct ComposePollOption: View { @ObservedObject var poll: Draft.Poll @ObservedObject var option: Draft.Poll.Option let optionIndex: Int var body: some View { HStack(spacing: 4) { Checkbox(radiusFraction: poll.multiple ? 0.1 : 0.5, borderWidth: 2) .animation(.default) textField Button(action: self.removeOption) { Image(systemName: "minus.circle.fill") } .foregroundColor(poll.options.count == 1 ? .gray : .red) .disabled(poll.options.count == 1) .hoverEffect() } } private var textField: some View { var field = ComposeEmojiTextField(text: $option.text, placeholder: "Option \(optionIndex + 1)") return field.backgroundColor(.systemBackground) } private func removeOption() { _ = withAnimation { poll.options.remove(at: optionIndex) } } struct Checkbox: View { private let radiusFraction: CGFloat private let size: CGFloat = 20 private let innerSize: CGFloat init(radiusFraction: CGFloat, borderWidth: CGFloat) { self.radiusFraction = radiusFraction self.innerSize = self.size - 2 * borderWidth } var body: some View { ZStack { Rectangle() .foregroundColor(.gray) .frame(width: size, height: size) .cornerRadius(radiusFraction * size) Rectangle() .foregroundColor(Color(UIColor.systemBackground)) .frame(width: innerSize, height: innerSize) .cornerRadius(radiusFraction * innerSize) } } } } //struct ComposePollView_Previews: PreviewProvider { // static var previews: some View { // ComposePollView() // } //}