Files
simplex-chat/apps/ios/Shared/Views/Helpers/CustomTimePicker.swift
spaced4ndy f592a26b00 ios, android: increase disappearing message interval limit (#2599)
* ios, android: increase disappearing message interval limit

* Apply suggestions from code review

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-06-20 10:14:07 +04:00

230 lines
8.0 KiB
Swift

//
// CustomTimePicker.swift
// SimpleX (iOS)
//
// Created by spaced4ndy on 11.05.2023.
// Copyright © 2023 SimpleX Chat. All rights reserved.
//
import SwiftUI
import SimpleXChat
struct CustomTimePicker: View {
@Binding var selection: Int?
@State var timeUnitsLimits = TimeUnitLimits.defaultUnitsLimits
@State private var selectedUnit: CustomTimeUnit = .second
@State private var selectedDuration: Int = 1
struct TimeUnitLimits {
var timeUnit: CustomTimeUnit
var minValue: Int = 1
var maxValue: Int
public static func defaultUnitLimits(_ unit: CustomTimeUnit) -> TimeUnitLimits {
switch unit {
case .second: return TimeUnitLimits.init(timeUnit: .second, maxValue: 120)
case .minute: return TimeUnitLimits.init(timeUnit: .minute, maxValue: 120)
case .hour: return TimeUnitLimits.init(timeUnit: .hour, maxValue: 72)
case .day: return TimeUnitLimits.init(timeUnit: .day, maxValue: 60)
case .week: return TimeUnitLimits.init(timeUnit: .week, maxValue: 52)
case .month: return TimeUnitLimits.init(timeUnit: .month, maxValue: 12)
}
}
public static var defaultUnitsLimits: [TimeUnitLimits] {[
defaultUnitLimits(.second),
defaultUnitLimits(.minute),
defaultUnitLimits(.hour),
defaultUnitLimits(.day),
defaultUnitLimits(.week),
defaultUnitLimits(.month),
]}
}
var body: some View {
HStack(spacing: 0) {
Group {
Picker("Duration", selection: $selectedDuration) {
let selectedUnitLimits = timeUnitsLimits.first(where: { $0.timeUnit == selectedUnit }) ?? TimeUnitLimits.defaultUnitLimits(selectedUnit)
let selectedUnitValues = Array(selectedUnitLimits.minValue...selectedUnitLimits.maxValue)
let values = selectedUnitValues + (selectedUnitValues.contains(selectedDuration) ? [] : [selectedDuration])
ForEach(values, id: \.self) { value in
Text("\(value)")
}
}
Picker("Unit", selection: $selectedUnit) {
ForEach(timeUnitsLimits.map { $0.timeUnit }, id: \.self) { timeUnit in
Text(timeUnit.text)
}
}
}
.pickerStyle(.wheel)
.frame(minWidth: 0)
.compositingGroup()
.clipped()
}
.onAppear {
if let selection = selection,
selection > 0 {
(selectedUnit, selectedDuration) = CustomTimeUnit.toTimeUnit(seconds: selection)
} else {
selection = selectedUnit.toSeconds * selectedDuration
}
}
.onChange(of: selectedUnit) { unit in
if let maxValue = timeUnitsLimits.first(where: { $0.timeUnit == unit })?.maxValue,
selectedDuration > maxValue {
selectedDuration = maxValue
} else {
selection = unit.toSeconds * selectedDuration
}
}
.onChange(of: selectedDuration) { duration in
selection = selectedUnit.toSeconds * duration
}
}
}
extension UIPickerView {
open override var intrinsicContentSize: CGSize {
return CGSize(width: UIView.noIntrinsicMetric, height: super.intrinsicContentSize.height)
}
}
struct CustomTimePickerView: View {
@Environment(\.dismiss) var dismiss
@Binding var selection: Int?
var confirmButtonText: LocalizedStringKey
var confirmButtonAction: () -> Void
var description: LocalizedStringKey? = nil
var timeUnitsLimits = CustomTimePicker.TimeUnitLimits.defaultUnitsLimits
var body: some View {
NavigationView {
customTimePickerView()
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("Cancel") {
dismiss()
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Button {
confirmButtonAction()
dismiss()
} label: {
Text(confirmButtonText)
.fontWeight(.medium)
}
.disabled(selection == nil)
}
}
}
}
private func customTimePickerView() -> some View {
VStack(alignment: .leading) {
List {
Group {
Section(description ?? "") {
CustomTimePicker(selection: $selection)
}
}
.listRowInsets(.init(top: 0, leading: 16, bottom: 0, trailing: 16))
}
.listStyle(.insetGrouped)
}
}
}
struct DropdownCustomTimePicker: View {
@Binding var selection: Int?
var label: LocalizedStringKey
var dropdownValues: [Int?]
var customPickerConfirmButtonText: LocalizedStringKey
var customPickerDescription: LocalizedStringKey? = nil
var customPickerTimeUnitsLimits = CustomTimePicker.TimeUnitLimits.defaultUnitsLimits
@State private var dropdownSelection: DropdownSelection = .dropdownValue(value: nil)
@State private var showCustomTimePicker = false
@State private var selectedCustomTime: Int? = nil
@State private var justOpened = true
enum DropdownSelection: Hashable {
case dropdownValue(value: Int?)
case custom
}
var body: some View {
Picker(label, selection: $dropdownSelection) {
let values: [DropdownSelection] =
dropdownValues.map { .dropdownValue(value: $0) }
+ (dropdownValues.contains(selection) ? [] : [.dropdownValue(value: selection)])
+ [.custom]
ForEach(values, id: \.self) { v in
switch v {
case let .dropdownValue(value): Text(timeText(value))
case .custom: Text(NSLocalizedString("custom", comment: "dropdown time picker choice"))
}
}
}
.onAppear {
if #unavailable(iOS 16) {
// this condition prevents re-setting picker
if !justOpened { return }
}
dropdownSelection = .dropdownValue(value: selection)
justOpened = false
}
.onChange(of: selection) { v in
logger.debug("*** .onChange(of: selection)")
dropdownSelection = .dropdownValue(value: v)
}
.onChange(of: dropdownSelection) { v in
logger.debug("*** .onChange(of: dropdownSelection)")
switch v {
case let .dropdownValue(value): selection = value
case .custom: showCustomTimePicker = true
}
}
.sheet(
isPresented: $showCustomTimePicker,
onDismiss: {
dropdownSelection = .dropdownValue(value: selection)
selectedCustomTime = nil
}
) {
if #available(iOS 16.0, *) {
customTimePicker()
.presentationDetents([.medium])
} else {
customTimePicker()
}
}
}
private func customTimePicker() -> some View {
CustomTimePickerView(
selection: $selectedCustomTime,
confirmButtonText: customPickerConfirmButtonText,
confirmButtonAction: {
if let time = selectedCustomTime {
selection = time
}
},
description: customPickerDescription,
timeUnitsLimits: customPickerTimeUnitsLimits
)
.onAppear {
selectedCustomTime = selection
}
}
}
struct CustomTimePicker_Previews: PreviewProvider {
static var previews: some View {
CustomTimePicker(
selection: Binding.constant(300)
)
}
}