From 733f01adfd595ed0164b28a1d5236489d51bc78c Mon Sep 17 00:00:00 2001 From: kean Date: Fri, 20 Jun 2025 11:10:58 -0400 Subject: [PATCH 1/2] Remove CalendarViewController --- .../Posts/WeekdaysHeaderViewTests.swift | 31 -- .../Activity/CalendarViewController.swift | 285 ----------- .../Scheduling/CalendarCollectionView.swift | 460 ------------------ .../Post/Scheduling/CalendarMonthView.swift | 31 -- 4 files changed, 807 deletions(-) delete mode 100644 Tests/KeystoneTests/Tests/Features/Posts/WeekdaysHeaderViewTests.swift delete mode 100644 WordPress/Classes/ViewRelated/Activity/CalendarViewController.swift delete mode 100644 WordPress/Classes/ViewRelated/Post/Scheduling/CalendarCollectionView.swift delete mode 100644 WordPress/Classes/ViewRelated/Post/Scheduling/CalendarMonthView.swift diff --git a/Tests/KeystoneTests/Tests/Features/Posts/WeekdaysHeaderViewTests.swift b/Tests/KeystoneTests/Tests/Features/Posts/WeekdaysHeaderViewTests.swift deleted file mode 100644 index c953a0f7403d..000000000000 --- a/Tests/KeystoneTests/Tests/Features/Posts/WeekdaysHeaderViewTests.swift +++ /dev/null @@ -1,31 +0,0 @@ -import XCTest -@testable import WordPress - -class WeekdaysHeaderViewTests: XCTestCase { - - func testDisplayedWeekdaysMonday() { - // A calendar with Monday as first weekday - var calendar = Calendar(identifier: .gregorian) - calendar.firstWeekday = 2 - - let headerView = WeekdaysHeaderView(calendar: calendar) - let firstWeekday = headerView.subviews.first as? UILabel - let lastWeekday = headerView.subviews.last as? UILabel - - XCTAssertEqual(firstWeekday?.text, "M", "Label should show Monday as first weekday") - XCTAssertEqual(lastWeekday?.text, "S", "Label should show Sunday as last weekday") - } - - func testDisplayedWeekdaysSunday() { - // A calendar with Monday as first weekday - var calendar = Calendar(identifier: .gregorian) - calendar.firstWeekday = 1 - - let headerView = WeekdaysHeaderView(calendar: calendar) - let firstWeekday = headerView.subviews.first as? UILabel - let lastWeekday = headerView.subviews.last as? UILabel - - XCTAssertEqual(firstWeekday?.text, "S", "Label should show Sunday as first weekday") - XCTAssertEqual(lastWeekday?.text, "S", "Label should show Saturday as last weekday") - } -} diff --git a/WordPress/Classes/ViewRelated/Activity/CalendarViewController.swift b/WordPress/Classes/ViewRelated/Activity/CalendarViewController.swift deleted file mode 100644 index 4a69c729dec0..000000000000 --- a/WordPress/Classes/ViewRelated/Activity/CalendarViewController.swift +++ /dev/null @@ -1,285 +0,0 @@ -import UIKit -import WordPressUI - -protocol CalendarViewControllerDelegate: AnyObject { - func didCancel(calendar: CalendarViewController) - func didSelect(calendar: CalendarViewController, startDate: Date?, endDate: Date?) -} - -class CalendarViewController: UIViewController { - - private var calendarCollectionView: CalendarCollectionView! - private var startDateLabel: UILabel! - private var separatorDateLabel: UILabel! - private var endDateLabel: UILabel! - private var header: UIStackView! - private let gradient = GradientView() - - private var startDate: Date? - private var endDate: Date? - - weak var delegate: CalendarViewControllerDelegate? - - private lazy var formatter: DateFormatter = { - let formatter = DateFormatter() - formatter.setLocalizedDateFormatFromTemplate("MMM d, yyyy") - return formatter - }() - - private enum Constants { - static let headerPadding: CGFloat = 16 - static let endDateLabel = NSLocalizedString("End Date", comment: "Placeholder for the end date in calendar range selection") - static let startDateLabel = NSLocalizedString("Start Date", comment: "Placeholder for the start date in calendar range selection") - static let rangeSummaryAccessibilityLabel = NSLocalizedString( - "Selected range: %1$@ to %2$@", - comment: "Accessibility label for summary of currently selected range. %1$@ is the start date, %2$@ is " + - "the end date.") - static let singleDateRangeSummaryAccessibilityLabel = NSLocalizedString( - "Selected range: %1$@ only", - comment: "Accessibility label for summary of currently single date. %1$@ is the date") - static let noRangeSelectedAccessibilityLabelPlaceholder = NSLocalizedString( - "No date range selected", - comment: "Accessibility label for no currently selected range.") - } - - /// Creates a full screen year calendar controller - /// - /// - Parameters: - /// - startDate: An optional Date representing the first selected date - /// - endDate: An optional Date representing the end selected date - init(startDate: Date? = nil, endDate: Date? = nil) { - self.startDate = startDate - self.endDate = endDate - super.init(nibName: nil, bundle: nil) - } - - required init?(coder: NSCoder) { - super.init(coder: coder) - } - - override func viewDidLoad() { - super.viewDidLoad() - - title = NSLocalizedString("Choose date range", comment: "Title to choose date range in a calendar") - - // Configure Calendar - let calendar = Calendar.current - self.calendarCollectionView = CalendarCollectionView( - calendar: calendar, - style: .year, - startDate: startDate, - endDate: endDate - ) - - // Configure headers and add the calendar to the view - configureHeader() - let stackView = UIStackView(arrangedSubviews: [ - header, - calendarCollectionView - ]) - stackView.axis = .vertical - stackView.translatesAutoresizingMaskIntoConstraints = false - stackView.setCustomSpacing(Constants.headerPadding, after: header) - view.addSubview(stackView) - view.pinSubviewToAllEdges(stackView, insets: UIEdgeInsets(top: Constants.headerPadding, left: 0, bottom: 0, right: 0)) - view.backgroundColor = .systemBackground - edgesForExtendedLayout = [] - - setupNavButtons() - - setUpGradient() - - calendarCollectionView.calDataSource.didSelect = { [weak self] startDate, endDate in - self?.updateDates(startDate: startDate, endDate: endDate) - } - - calendarCollectionView.scrollsToTop = false - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - - scrollToVisibleDate() - } - - override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { - super.viewWillTransition(to: size, with: coordinator) - - coordinator.animate(alongsideTransition: { _ in - self.calendarCollectionView.reloadData(withAnchor: self.startDate ?? Date(), completionHandler: nil) - }, completion: nil) - } - - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - - setUpGradientColors() - } - - private func setupNavButtons() { - let doneButton = UIBarButtonItem(title: NSLocalizedString("Done", comment: "Label for Done button"), style: .done, target: self, action: #selector(done)) - navigationItem.setRightBarButton(doneButton, animated: false) - - navigationItem.setLeftBarButton(UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(cancel)), animated: false) - } - - private func updateDates(startDate: Date?, endDate: Date?) { - self.startDate = startDate - self.endDate = endDate - - updateLabels() - } - - private func updateLabels() { - guard let startDate else { - resetLabels() - return - } - - startDateLabel.text = formatter.string(from: startDate) - startDateLabel.textColor = .label - startDateLabel.font = WPStyleGuide.fontForTextStyle(.title3, fontWeight: .semibold) - - if let endDate { - endDateLabel.text = formatter.string(from: endDate) - endDateLabel.textColor = .label - endDateLabel.font = WPStyleGuide.fontForTextStyle(.title3, fontWeight: .semibold) - separatorDateLabel.textColor = .label - separatorDateLabel.font = WPStyleGuide.fontForTextStyle(.title3, fontWeight: .semibold) - } else { - endDateLabel.text = Constants.endDateLabel - endDateLabel.font = WPStyleGuide.fontForTextStyle(.title3) - endDateLabel.textColor = .secondaryLabel - separatorDateLabel.textColor = .secondaryLabel - } - - header.accessibilityLabel = accessibilityLabelForRangeSummary(startDate: startDate, endDate: endDate) - } - - private func configureHeader() { - header = startEndDateHeader() - resetLabels() - } - - private func startEndDateHeader() -> UIStackView { - let header = UIStackView(frame: .zero) - header.distribution = .fill - - let startDate = UILabel() - startDate.isAccessibilityElement = false - startDateLabel = startDate - startDate.font = WPStyleGuide.fontForTextStyle(.title3, fontWeight: .semibold) - if view.effectiveUserInterfaceLayoutDirection == .leftToRight { - // swiftlint:disable:next inverse_text_alignment - startDate.textAlignment = .right - } else { - // swiftlint:disable:next natural_text_alignment - startDate.textAlignment = .left - } - header.addArrangedSubview(startDate) - startDate.widthAnchor.constraint(equalTo: header.widthAnchor, multiplier: 0.47).isActive = true - - let separator = UILabel() - separator.isAccessibilityElement = false - separatorDateLabel = separator - separator.font = WPStyleGuide.fontForTextStyle(.title3, fontWeight: .semibold) - separator.textAlignment = .center - header.addArrangedSubview(separator) - separator.widthAnchor.constraint(equalTo: header.widthAnchor, multiplier: 0.06).isActive = true - - let endDate = UILabel() - endDate.isAccessibilityElement = false - endDateLabel = endDate - endDate.font = WPStyleGuide.fontForTextStyle(.title3, fontWeight: .semibold) - if view.effectiveUserInterfaceLayoutDirection == .leftToRight { - // swiftlint:disable:next natural_text_alignment - endDate.textAlignment = .left - } else { - // swiftlint:disable:next inverse_text_alignment - endDate.textAlignment = .right - } - header.addArrangedSubview(endDate) - endDate.widthAnchor.constraint(equalTo: header.widthAnchor, multiplier: 0.47).isActive = true - - header.isAccessibilityElement = true - header.accessibilityTraits = [.header, .summaryElement] - - return header - } - - private func scrollToVisibleDate() { - if calendarCollectionView.frame.height == 0 { - calendarCollectionView.superview?.layoutIfNeeded() - } - - if let startDate { - calendarCollectionView.scrollToDate(startDate, - animateScroll: true, - preferredScrollPosition: .centeredVertically, - extraAddedOffset: -(self.calendarCollectionView.frame.height / 2)) - } else { - calendarCollectionView.setContentOffset(CGPoint( - x: 0, - y: calendarCollectionView.contentSize.height - calendarCollectionView.frame.size.height - ), animated: false) - } - - } - - private func resetLabels() { - startDateLabel.text = Constants.startDateLabel - - separatorDateLabel.text = "-" - - endDateLabel.text = Constants.endDateLabel - - [startDateLabel, separatorDateLabel, endDateLabel].forEach { label in - label?.textColor = .secondaryLabel - label?.font = WPStyleGuide.fontForTextStyle(.title3) - } - - header.accessibilityLabel = accessibilityLabelForRangeSummary(startDate: nil, endDate: nil) - } - - private func accessibilityLabelForRangeSummary(startDate: Date?, endDate: Date?) -> String { - switch (startDate, endDate) { - case (nil, _): - return Constants.noRangeSelectedAccessibilityLabelPlaceholder - case (.some(let startDate), nil): - let startDateString = formatter.string(from: startDate) - return String.localizedStringWithFormat(Constants.singleDateRangeSummaryAccessibilityLabel, startDateString) - case (.some(let startDate), .some(let endDate)): - let startDateString = formatter.string(from: startDate) - let endDateString = formatter.string(from: endDate) - return String.localizedStringWithFormat(Constants.rangeSummaryAccessibilityLabel, startDateString, endDateString) - } - } - - private func setUpGradient() { - gradient.isUserInteractionEnabled = false - gradient.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(gradient) - - NSLayoutConstraint.activate([ - gradient.heightAnchor.constraint(equalToConstant: 50), - gradient.topAnchor.constraint(equalTo: calendarCollectionView.topAnchor), - gradient.leadingAnchor.constraint(equalTo: calendarCollectionView.leadingAnchor), - gradient.trailingAnchor.constraint(equalTo: calendarCollectionView.trailingAnchor) - ]) - - setUpGradientColors() - } - - private func setUpGradientColors() { - gradient.fromColor = .systemBackground - gradient.toColor = UIColor.systemBackground.withAlphaComponent(0) - } - - @objc private func done() { - delegate?.didSelect(calendar: self, startDate: startDate, endDate: endDate) - } - - @objc private func cancel() { - delegate?.didCancel(calendar: self) - } -} diff --git a/WordPress/Classes/ViewRelated/Post/Scheduling/CalendarCollectionView.swift b/WordPress/Classes/ViewRelated/Post/Scheduling/CalendarCollectionView.swift deleted file mode 100644 index 0481d02df06f..000000000000 --- a/WordPress/Classes/ViewRelated/Post/Scheduling/CalendarCollectionView.swift +++ /dev/null @@ -1,460 +0,0 @@ -import UIKit -import JTAppleCalendar -import WordPressUI - -enum CalendarCollectionViewStyle { - case month - case year -} - -class CalendarCollectionView: WPJTACMonthView { - - let calDataSource: CalendarDataSource - let style: CalendarCollectionViewStyle - - init(calendar: Calendar, - style: CalendarCollectionViewStyle = .month, - startDate: Date? = nil, - endDate: Date? = nil) { - calDataSource = CalendarDataSource( - calendar: calendar, - style: style, - startDate: startDate, - endDate: endDate - ) - - self.style = style - super.init() - - setup() - } - - required init?(coder aDecoder: NSCoder) { - calDataSource = CalendarDataSource(calendar: Calendar.current, style: .month) - style = .month - super.init(coder: aDecoder) - - setup() - } - - private func setup() { - register(DateCell.self, forCellWithReuseIdentifier: DateCell.Constants.reuseIdentifier) - register(CalendarYearHeaderView.self, - forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, - withReuseIdentifier: CalendarYearHeaderView.reuseIdentifier) - - backgroundColor = .clear - - switch style { - case .month: - scrollDirection = .horizontal - scrollingMode = .stopAtEachCalendarFrame - case .year: - scrollDirection = .vertical - - allowsMultipleSelection = true - allowsRangedSelection = true - rangeSelectionMode = .continuous - - minimumLineSpacing = 0 - minimumInteritemSpacing = 0 - - cellSize = 50 - } - - showsHorizontalScrollIndicator = false - isDirectionalLockEnabled = true - - calendarDataSource = calDataSource - calendarDelegate = calDataSource - } - - /// VoiceOver scrollback workaround - /// When using VoiceOver, moving focus from the surrounding elements (usually the next month button) to the calendar DateCells, a - /// scrollback to 0 was triggered by the system. This appears to be expected (though irritating) behaviour with a paging UICollectionView. - /// The impact of this scrollback for the month view calendar (as used to schedule a post) is that the calendar jumps to 1951-01-01, with - /// the only way to navigate forwards being to tap the "next month" button repeatedly. - /// Ignoring these scrolls back to 0 when VoiceOver is in use prevents this issue, while not impacting other use of the calendar. - /// Similar behaviour sometimes occurs with the non-paging year view calendar (as used for activity log filtering) which is harder to reproduce, - /// but also remedied by this change. - override func setContentOffset(_ contentOffset: CGPoint, animated: Bool) { - if shouldPreventAccessibilityFocusScrollback(for: contentOffset) { - return - } - super.setContentOffset(contentOffset, animated: animated) - } - - func shouldPreventAccessibilityFocusScrollback(for newContentOffset: CGPoint) -> Bool { - if UIAccessibility.isVoiceOverRunning { - switch style { - case .month: - return newContentOffset.x == 0 && contentOffset.x > 0 - case .year: - return newContentOffset.y == 0 && contentOffset.y > 0 - } - } - return false - } -} - -class CalendarDataSource: JTACMonthViewDataSource { - - var willScroll: ((DateSegmentInfo) -> Void)? - var didScroll: ((DateSegmentInfo) -> Void)? - var didSelect: ((Date?, Date?) -> Void)? - - // First selected date - var firstDate: Date? - - // End selected date - var endDate: Date? - - private let calendar: Calendar - private let style: CalendarCollectionViewStyle - - init(calendar: Calendar, - style: CalendarCollectionViewStyle, - startDate: Date? = nil, - endDate: Date? = nil) { - self.calendar = calendar - self.style = style - self.firstDate = startDate - self.endDate = endDate - } - - func configureCalendar(_ calendar: JTACMonthView) -> ConfigurationParameters { - /// When style is year, display the last 20 years til this month - if style == .year { - var dateComponent = DateComponents() - dateComponent.year = -20 - let startDate = Calendar.current.date(byAdding: dateComponent, to: Date()) - let endDate = Date().endOfMonth - - if let startDate, let endDate { - return ConfigurationParameters(startDate: startDate, endDate: endDate, calendar: self.calendar) - } - } - - let startDate = Date.farPastDate - let endDate = Date.farFutureDate - return ConfigurationParameters(startDate: startDate, endDate: endDate, calendar: self.calendar) - } -} - -extension CalendarDataSource: JTACMonthViewDelegate { - func calendar(_ calendar: JTACMonthView, cellForItemAt date: Date, cellState: CellState, indexPath: IndexPath) -> JTACDayCell { - let cell = calendar.dequeueReusableJTAppleCell(withReuseIdentifier: DateCell.Constants.reuseIdentifier, for: indexPath) - if let dateCell = cell as? DateCell { - configure(cell: dateCell, with: cellState) - } - return cell - } - - func calendar(_ calendar: JTACMonthView, willDisplay cell: JTACDayCell, forItemAt date: Date, cellState: CellState, indexPath: IndexPath) { - configure(cell: cell, with: cellState) - } - - func calendar(_ calendar: JTACMonthView, willScrollToDateSegmentWith visibleDates: DateSegmentInfo) { - willScroll?(visibleDates) - } - - func calendar(_ calendar: JTACMonthView, didScrollToDateSegmentWith visibleDates: DateSegmentInfo) { - didScroll?(visibleDates) - } - - func calendar(_ calendar: JTACMonthView, didSelectDate date: Date, cell: JTACDayCell?, cellState: CellState, indexPath: IndexPath) { - if style == .year { - // If the date is in the future, bail out - if date > Date() { - return - } - - if let firstDate { - if let endDate { - // When tapping a selected firstDate or endDate reset the rest - if date == firstDate || date == endDate { - self.firstDate = date - self.endDate = nil - // Increase the range at the left side - } else if date < firstDate { - self.firstDate = date - // Increase the range at the right side - } else { - self.endDate = date - } - // When tapping a single selected date, deselect everything - } else if date == firstDate { - self.firstDate = nil - self.endDate = nil - // When selecting a second date - } else { - self.firstDate = min(firstDate, date) - endDate = max(firstDate, date) - } - // When selecting the first date - } else { - firstDate = date - } - // Monthly calendar only selects a single date - } else { - firstDate = date - } - - didSelect?(firstDate, endDate) - UIView.performWithoutAnimation { - calendar.reloadItems(at: calendar.indexPathsForVisibleItems) - } - - configure(cell: cell, with: cellState) - } - - func calendar(_ calendar: JTACMonthView, didDeselectDate date: Date, cell: JTACDayCell?, cellState: CellState, indexPath: IndexPath) { - configure(cell: cell, with: cellState) - } - - func calendarSizeForMonths(_ calendar: JTACMonthView?) -> MonthSize? { - return style == .year ? MonthSize(defaultSize: 50) : nil - } - - func calendar(_ calendar: JTACMonthView, headerViewForDateRange range: (start: Date, end: Date), at indexPath: IndexPath) -> JTACMonthReusableView { - let date = range.start - let formatter = DateFormatter() - formatter.dateFormat = "MMMM yyyy" - let header = calendar.dequeueReusableJTAppleSupplementaryView(withReuseIdentifier: CalendarYearHeaderView.reuseIdentifier, for: indexPath) - (header as! CalendarYearHeaderView).titleLabel.text = formatter.string(from: date) - return header - } - - private func configure(cell: JTACDayCell?, with state: CellState) { - let cell = cell as? DateCell - cell?.configure(with: state, startDate: firstDate, endDate: endDate, hideInOutDates: style == .year) - } -} - -class DateCell: JTACDayCell { - - struct Constants { - static let labelSize: CGFloat = 28 - static let reuseIdentifier = "dateCell" - static var selectedColor: UIColor { - UIColor(light: UIAppColor.primary(.shade5), dark: UIAppColor.primary(.shade90)) - } - } - - let dateLabel = UILabel() - let leftPlaceholder = UIView() - let rightPlaceholder = UIView() - - let dateFormatter = DateFormatter() - - override init(frame: CGRect) { - super.init(frame: frame) - - setup() - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setup() { - dateLabel.translatesAutoresizingMaskIntoConstraints = false - dateLabel.textAlignment = .center - dateLabel.font = UIFont.preferredFont(forTextStyle: .callout) - - // Show circle behind text for selected day - dateLabel.clipsToBounds = true - dateLabel.layer.cornerRadius = Constants.labelSize / 2 - - addSubview(dateLabel) - - NSLayoutConstraint.activate([ - dateLabel.widthAnchor.constraint(equalToConstant: Constants.labelSize), - dateLabel.heightAnchor.constraint(equalTo: dateLabel.widthAnchor), - dateLabel.centerYAnchor.constraint(equalTo: centerYAnchor), - dateLabel.centerXAnchor.constraint(equalTo: centerXAnchor) - ]) - - leftPlaceholder.translatesAutoresizingMaskIntoConstraints = false - rightPlaceholder.translatesAutoresizingMaskIntoConstraints = false - - addSubview(leftPlaceholder) - addSubview(rightPlaceholder) - - NSLayoutConstraint.activate([ - leftPlaceholder.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.6), - leftPlaceholder.heightAnchor.constraint(equalTo: dateLabel.heightAnchor), - leftPlaceholder.trailingAnchor.constraint(equalTo: centerXAnchor), - leftPlaceholder.centerYAnchor.constraint(equalTo: centerYAnchor) - ]) - - NSLayoutConstraint.activate([ - rightPlaceholder.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.5), - rightPlaceholder.heightAnchor.constraint(equalTo: dateLabel.heightAnchor), - rightPlaceholder.leadingAnchor.constraint(equalTo: centerXAnchor, constant: 0), - rightPlaceholder.centerYAnchor.constraint(equalTo: centerYAnchor) - ]) - - bringSubviewToFront(dateLabel) - } -} - -extension DateCell { - /// Configure the DateCell - /// - /// - Parameters: - /// - state: the representation of the cell state - /// - startDate: the first Date selected - /// - endDate: the last Date selected - /// - hideInOutDates: a Bool to hide/display dates outside of the current month (filling the entire row) - /// - Returns: UIColor. Red in cases of error - func configure(with state: CellState, - startDate: Date? = nil, - endDate: Date? = nil, - hideInOutDates: Bool = false) { - - dateLabel.text = state.text - - dateFormatter.setLocalizedDateFormatFromTemplate("EEE MMM d, yyyy") - dateLabel.accessibilityLabel = dateFormatter.string(from: state.date) - dateLabel.accessibilityTraits = .button - - var textColor: UIColor - - if hideInOutDates && state.dateBelongsTo != .thisMonth { - isHidden = true - } else { - isHidden = false - } - - // Reset state - leftPlaceholder.backgroundColor = .clear - rightPlaceholder.backgroundColor = .clear - dateLabel.backgroundColor = .clear - textColor = .label - dateLabel.accessibilityTraits = .button - if state.isSelected { - dateLabel.accessibilityTraits.insert(.selected) - } - - switch position(for: state.date, startDate: startDate, endDate: endDate) { - case .middle: - textColor = .label - leftPlaceholder.backgroundColor = Constants.selectedColor - rightPlaceholder.backgroundColor = Constants.selectedColor - dateLabel.backgroundColor = .clear - case .left: - textColor = .white - dateLabel.backgroundColor = UIAppColor.primary - rightPlaceholder.backgroundColor = Constants.selectedColor - case .right: - textColor = .white - dateLabel.backgroundColor = UIAppColor.primary - leftPlaceholder.backgroundColor = Constants.selectedColor - case .full: - textColor = .invertedLabel - leftPlaceholder.backgroundColor = .clear - rightPlaceholder.backgroundColor = .clear - dateLabel.backgroundColor = UIAppColor.primary - case .none: - leftPlaceholder.backgroundColor = .clear - rightPlaceholder.backgroundColor = .clear - dateLabel.backgroundColor = .clear - if state.date > Date() { - textColor = .secondaryLabel - } else if state.dateBelongsTo == .thisMonth { - textColor = .label - } else { - textColor = .secondaryLabel - } - } - - dateLabel.textColor = textColor - } - - func position(for date: Date, startDate: Date?, endDate: Date?) -> SelectionRangePosition { - if let startDate, let endDate { - if date == startDate { - return .left - } else if date == endDate { - return .right - } else if date > startDate && date < endDate { - return .middle - } - } else if let startDate { - if date == startDate { - return .full - } - } - - return .none - } -} - -// MARK: - Year Header View -class CalendarYearHeaderView: JTACMonthReusableView { - static let reuseIdentifier = "CalendarYearHeaderView" - - let titleLabel: UILabel = UILabel() - - override init(frame: CGRect) { - super.init(frame: frame) - let stackView = UIStackView() - stackView.axis = .vertical - stackView.spacing = Constants.stackViewSpacing - - addSubview(stackView) - stackView.translatesAutoresizingMaskIntoConstraints = false - pinSubviewToSafeArea(stackView) - - stackView.addArrangedSubview(titleLabel) - titleLabel.font = .preferredFont(forTextStyle: .headline) - titleLabel.textAlignment = .center - titleLabel.textColor = Constants.titleColor - titleLabel.accessibilityTraits = .header - - let weekdaysView = WeekdaysHeaderView(calendar: Calendar.current) - stackView.addArrangedSubview(weekdaysView) - - stackView.setCustomSpacing(Constants.spacingAfterWeekdays, after: weekdaysView) - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private enum Constants { - static let stackViewSpacing: CGFloat = 16 - static let spacingAfterWeekdays: CGFloat = 8 - static let titleColor = UIColor(light: UIAppColor.gray(.shade70), dark: .secondaryLabel) - } -} - -extension Date { - var startOfMonth: Date? { - return Calendar.current.date(from: Calendar.current.dateComponents([.year, .month], from: Calendar.current.startOfDay(for: self))) - } - - var endOfMonth: Date? { - guard let startOfMonth else { - return nil - } - - return Calendar.current.date(byAdding: DateComponents(month: 1, day: -1), to: startOfMonth) - } -} - -class WPJTACMonthView: JTACMonthView { - - // Avoids content to scroll above/below the maximum/minimum size - override func setContentOffset(_ contentOffset: CGPoint, animated: Bool) { - let maxY = contentSize.height - frame.size.height - if contentOffset.y > maxY { - super.setContentOffset(CGPoint(x: contentOffset.x, y: maxY), animated: animated) - } else if contentOffset.y < 0 { - super.setContentOffset(CGPoint(x: contentOffset.x, y: 0), animated: animated) - } else { - super.setContentOffset(contentOffset, animated: animated) - } - } -} diff --git a/WordPress/Classes/ViewRelated/Post/Scheduling/CalendarMonthView.swift b/WordPress/Classes/ViewRelated/Post/Scheduling/CalendarMonthView.swift deleted file mode 100644 index f89932cee816..000000000000 --- a/WordPress/Classes/ViewRelated/Post/Scheduling/CalendarMonthView.swift +++ /dev/null @@ -1,31 +0,0 @@ -import UIKit -import WordPressUI - -/// A view containing weekday symbols horizontally aligned for use in a calendar header -class WeekdaysHeaderView: UIStackView { - convenience init(calendar: Calendar) { - /// Adjust the weekday symbols array so that the first week day matches - let weekdaySymbols = calendar.veryShortWeekdaySymbols.rotateLeft(calendar.firstWeekday - 1) - self.init(arrangedSubviews: weekdaySymbols.map({ symbol in - let label = UILabel() - label.text = symbol - label.textAlignment = .center - label.font = UIFont.preferredFont(forTextStyle: .caption1) - label.textColor = UIAppColor.neutral(.shade30) - label.isAccessibilityElement = false - return label - })) - self.distribution = .fillEqually - } -} - -extension Collection { - /// Rotates the array to the left ([1,2,3,4] -> [2,3,4,1]) - /// - Parameter offset: The offset by which to shift the array. - func rotateLeft(_ offset: Int) -> [Self.Element] { - let initialDigits = (abs(offset) % self.count) - let elementToPutAtEnd = Array(self[startIndex.. Date: Fri, 20 Jun 2025 11:11:55 -0400 Subject: [PATCH 2/2] Remove JTAppleCalendar dependency --- Modules/Package.resolved | 11 +---------- Modules/Package.swift | 2 -- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/Modules/Package.resolved b/Modules/Package.resolved index a4b19f453615..e22d45bf0323 100644 --- a/Modules/Package.resolved +++ b/Modules/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "fa035de2741e39c23eaba17409869db3ebef935eb88585d1f08d5dc2497e6d7d", + "originHash" : "8c8d98aed7eab1f454a6d3f34ad7f1d90ae38d5b0ca1d5e9ce24c7675adf295c", "pins" : [ { "identity" : "alamofire", @@ -153,15 +153,6 @@ "version" : "0.3.0" } }, - { - "identity" : "jtapplecalendar", - "kind" : "remoteSourceControl", - "location" : "https://github.com/patchthecode/JTAppleCalendar", - "state" : { - "revision" : "718f0ab68ba0fcd2bc134f6e9d30edc1b9b038e1", - "version" : "8.0.5" - } - }, { "identity" : "lottie-ios", "kind" : "remoteSourceControl", diff --git a/Modules/Package.swift b/Modules/Package.swift index 7e40bab002a0..80a28b4f912c 100644 --- a/Modules/Package.swift +++ b/Modules/Package.swift @@ -37,7 +37,6 @@ let package = Package( .package(url: "https://github.com/erikdoe/ocmock", revision: "2c0bfd373289f4a7716db5d6db471640f91a6507"), .package(url: "https://github.com/johnxnguyen/Down", branch: "master"), .package(url: "https://github.com/kaishin/Gifu", from: "3.4.1"), - .package(url: "https://github.com/patchthecode/JTAppleCalendar", from: "8.0.5"), .package(url: "https://github.com/Quick/Nimble", from: "10.0.0"), .package(url: "https://github.com/scinfu/SwiftSoup", exact: "2.7.5"), .package(url: "https://github.com/squarefrog/UIDeviceIdentifier", from: "2.3.0"), @@ -300,7 +299,6 @@ enum XcodeSupport { .product(name: "GravatarUI", package: "Gravatar-SDK-iOS"), .product(name: "Gridicons", package: "Gridicons-iOS"), .product(name: "GutenbergKit", package: "GutenbergKit"), - .product(name: "JTAppleCalendar", package: "JTAppleCalendar"), .product(name: "Lottie", package: "lottie-ios"), .product(name: "MediaEditor", package: "MediaEditor-iOS"), .product(name: "NSObject-SafeExpectations", package: "NSObject-SafeExpectations"),