Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[POC] Respect User selected locale in Calendar calculation #3041

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Core/Core/Common/CommonModels/AppEnvironment/Clock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ public class Cal {
return mock
}
#endif
return Calendar.current
var calendar = Calendar.current
calendar.locale = Locale.managed ?? .autoupdatingCurrent
return calendar
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,26 @@ public class LocalizationManager {
private static let instUserLocale = "InstUserLocale"
static var suspend = #selector(NSXPCConnection.suspend)

private static var effectiveLocale: String? {
private static var effectiveLocaleID: String? {
return Bundle.main.preferredLocalizations.first
}

public static var currentLocale: String? {
return UserDefaults.standard.string(forKey: instUserLocale) ?? effectiveLocale
public static var currentLocaleID: String? {
return UserDefaults.standard.string(forKey: instUserLocale) ?? effectiveLocaleID
}

public static var currentLocale: Locale? {
guard let localeID = currentLocaleID else { return nil }
return Locale(components: Locale.Components(identifier: localeID))
}

public static var effectiveLocale: Locale? {
guard let localeID = effectiveLocaleID else { return nil }
return Locale(components: Locale.Components(identifier: localeID))
}

public static var needsRestart: Bool {
return currentLocale != effectiveLocale
return currentLocaleID != effectiveLocaleID
}

static func convertCustomLocale(_ locale: String?) -> String {
Expand Down Expand Up @@ -78,3 +88,9 @@ public class LocalizationManager {
}
}
}

extension Locale {
static var managed: Locale? {
LocalizationManager.currentLocale
}
}
8 changes: 4 additions & 4 deletions Core/Core/Common/CommonUI/SwiftUIViews/CoreDatePicker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,14 @@ public struct CoreDatePickerActionSheetCard: View {
}

if let min = min, max == nil {
return min...min.addYears(2)
return min...min.inCalendar.addYears(2)
}

if let max = max, min == nil {
return max.addYears(-2)...max
return max.inCalendar.addYears(-2)...max
}

return Clock.now.addYears(-1)...Clock.now.addYears(1)
return Clock.now.inCalendar.addYears(-1)...Clock.now.inCalendar.addYears(1)
}

private func dismissPresentation() {
Expand All @@ -152,7 +152,7 @@ struct CoreDatePickerActionSheetCard_Previews: PreviewProvider {
static var previews: some View {
ForEach(ColorScheme.allCases, id: \.self) {
VStack {
CoreDatePickerActionSheetCard(selection: .constant(Date()), minDate: Clock.now, maxDate: Clock.now.addDays(5))
CoreDatePickerActionSheetCard(selection: .constant(Date()), minDate: Clock.now, maxDate: Clock.now.inCalendar.addDays(5))
}.preferredColorScheme($0)
}
}
Expand Down
225 changes: 144 additions & 81 deletions Core/Core/Common/Extensions/Foundation/DateExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,89 +32,12 @@ public extension Date {
self = date
}

// MARK: - Components

var months: Int {
Cal.currentCalendar.component(.month, from: self)
}

var daysOfMonth: Int {
Cal.currentCalendar.component(.day, from: self)
}

var hours: Int {
Cal.currentCalendar.component(.hour, from: self)
}

var minutes: Int {
Cal.currentCalendar.component(.minute, from: self)
}

// MARK: - add methods

func addYears(_ years: Int) -> Date {
Cal.currentCalendar.date(byAdding: .year, value: years, to: self)
?? self
var inCalendar: CalendarCalculation {
return CalendarCalculation(calendar: Cal.currentCalendar, date: self)
}

func addMonths(_ numberOfMonths: Int) -> Date {
Cal.currentCalendar.date(byAdding: .month, value: numberOfMonths, to: self)
?? Date()
}

func addDays(_ days: Int) -> Date {
Cal.currentCalendar.date(byAdding: .day, value: days, to: self)
?? Date()
}

func addHours(_ hours: Int) -> Date {
Cal.currentCalendar.date(byAdding: .hour, value: hours, to: self)
?? Date()
}

func addMinutes(_ minutes: Int) -> Date {
Cal.currentCalendar.date(byAdding: .minute, value: minutes, to: self)
?? Date()
}

func addSeconds(_ seconds: Int) -> Date {
Cal.currentCalendar.date(byAdding: .second, value: seconds, to: self)
?? Date()
}

// MARK: - start/end methods

func startOfMonth() -> Date {
Cal.currentCalendar.date(from: Cal.currentCalendar.dateComponents([.year, .month], from: startOfDay()))
?? Date()
}

func endOfMonth() -> Date {
Cal.currentCalendar.date(byAdding: DateComponents(month: 1, day: -1), to: startOfMonth())
?? Date()
}

func startOfWeek() -> Date {
Cal.currentCalendar.date(from: Cal.currentCalendar.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self))
?? Date()
}

func endOfWeek() -> Date {
Cal.currentCalendar.date(byAdding: .weekOfYear, value: 1, to: startOfWeek())
?? Date()
}

func startOfDay() -> Date {
Cal.currentCalendar.startOfDay(for: self)
}

func endOfDay() -> Date {
Cal.currentCalendar.date(bySettingHour: 23, minute: 59, second: 59, of: startOfDay())
?? Date()
}

func startOfHour() -> Date {
startOfDay().addHours(hours)
func inCalendar(_ calendar: Calendar) -> CalendarCalculation {
return CalendarCalculation(calendar: calendar, date: self)
}

// MARK: - Formatters
Expand Down Expand Up @@ -291,6 +214,146 @@ extension DateFormatter {
}
}

public struct CalendarCalculation {
public let calendar: Calendar
public let date: Date
}

public extension CalendarCalculation {
private static func now(in calendar: Calendar) -> CalendarCalculation {
return CalendarCalculation(calendar: calendar, date: .now)
}

// MARK: - Components

var years: Int {
calendar.component(.year, from: date)
}

var months: Int {
calendar.component(.month, from: date)
}

var daysOfMonth: Int {
calendar.component(.day, from: date)
}

var hours: Int {
calendar.component(.hour, from: date)
}

var minutes: Int {
calendar.component(.minute, from: date)
}

// MARK: - add methods

func addYears(_ years: Int) -> Date {
calendar.date(byAdding: .year, value: years, to: date)
?? date
}

func addMonths(_ numberOfMonths: Int) -> Date {
calendar.date(byAdding: .month, value: numberOfMonths, to: date)
?? Date()
}

func addDays(_ days: Int) -> Date {
calendar.date(byAdding: .day, value: days, to: date)
?? Date()
}

func addHours(_ hours: Int) -> Date {
calendar.date(byAdding: .hour, value: hours, to: date)
?? Date()
}

func addMinutes(_ minutes: Int) -> Date {
calendar.date(byAdding: .minute, value: minutes, to: date)
?? Date()
}

func addSeconds(_ seconds: Int) -> Date {
calendar.date(byAdding: .second, value: seconds, to: date)
?? Date()
}

func addingYears(_ years: Int) -> CalendarCalculation {
addYears(years).inCalendar(calendar)
}

func addingMonths(_ numberOfMonths: Int) -> CalendarCalculation {
addMonths(numberOfMonths).inCalendar(calendar)
}

func addingDays(_ days: Int) -> CalendarCalculation {
addDays(days).inCalendar(calendar)
}

func addingHours(_ hours: Int) -> CalendarCalculation {
addHours(hours).inCalendar(calendar)
}

func addingMinutes(_ minutes: Int) -> CalendarCalculation {
addMinutes(minutes).inCalendar(calendar)
}

func addingSeconds(_ seconds: Int) -> CalendarCalculation {
addSeconds(seconds).inCalendar(calendar)
}

// MARK: - start/end methods

func startOfMonth() -> CalendarCalculation {
calendar
.date(from: calendar.dateComponents([.year, .month], from: startOfDay().date))?
.inCalendar(calendar) ?? .now(in: calendar)
}

func endOfMonth() -> CalendarCalculation {
calendar
.date(byAdding: DateComponents(month: 1, day: -1), to: startOfMonth().date)?
.inCalendar(calendar) ?? .now(in: calendar)
}

func startOfWeek() -> CalendarCalculation {
calendar
.date(from: calendar.dateComponents([.yearForWeekOfYear, .weekOfYear], from: date))?
.inCalendar(calendar) ?? .now(in: calendar)
}

func endOfWeek() -> CalendarCalculation {
calendar
.date(byAdding: .weekOfYear, value: 1, to: startOfWeek().date)?
.inCalendar(calendar) ?? .now(in: calendar)
}

func startOfDay() -> CalendarCalculation {
calendar.startOfDay(for: date).inCalendar(calendar)
}

func endOfDay() -> CalendarCalculation {
calendar
.date(bySettingHour: 23, minute: 59, second: 59, of: startOfDay().date)?
.inCalendar(calendar) ?? .now(in: calendar)
}

func startOfHour() -> CalendarCalculation {
startOfDay().addingHours(hours)
}

func startOfMonth() -> Date { startOfMonth().date }
func endOfMonth() -> Date { endOfMonth().date }

func startOfWeek() -> Date { startOfWeek().date }
func endOfWeek() -> Date { endOfWeek().date }

func startOfDay() -> Date { startOfDay().date }
func endOfDay() -> Date { endOfDay().date }

func startOfHour() -> Date { startOfHour().date }
}

#if DEBUG
public extension Date {
static func make(calendar: Calendar = .current, year: Int, month: Int? = nil, day: Int? = nil, hour: Int? = nil, minute: Int? = nil, second: Int? = nil) -> Date {
Expand Down
2 changes: 1 addition & 1 deletion Core/Core/Features/Conferences/Conference.swift
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public final class Conference: NSManagedObject {
model.id = item.id.value
model.isConcluded = item.ended_at != nil
model.isLive = (
(item.started_at.map { $0 > Clock.now.addDays(-1) && $0 < Clock.now } ?? false) &&
(item.started_at.map { $0 > Clock.now.inCalendar.addDays(-1) && $0 < Clock.now } ?? false) &&
(item.ended_at ?? .distantFuture) > Clock.now
)
model.isLongRunning = item.long_running
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ public class GetK5HomeroomDueItemCount: CollectionUseCase {

public init(courseIds: [String]) {
let courseContextIds = courseIds.map { Core.Context(.course, id: $0).canvasContextID }
request = GetPlannablesRequest(userID: nil, startDate: Date().startOfDay(), endDate: Date().endOfDay(), contextCodes: courseContextIds, filter: "")
let nowCalc = Date.now.inCalendar
request = GetPlannablesRequest(userID: nil, startDate: nowCalc.startOfDay(), endDate: nowCalc.endOfDay(), contextCodes: courseContextIds, filter: "")
}

public func write(response: [APIPlannable]?, urlResponse: URLResponse?, to client: NSManagedObjectContext) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public class K5ImportantDatesViewModel: ObservableObject {
}

func addImportantDate(from event: CalendarEvent) {
guard let startDate = event.startAt, startDate >= Clock.now.startOfDay(), let course = courses.filter({ $0.id == event.context.id }).first else { return }
guard let startDate = event.startAt, startDate >= Clock.now.inCalendar.startOfDay(), let course = courses.filter({ $0.id == event.context.id }).first else { return }
let courseColor = Color(course.color)
if let existingDate = importantDates.first(where: { $0.date.dateOnlyString == startDate.dateOnlyString }) {
existingDate.addEvent(event, color: courseColor)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ public struct DiscussionEditorView: View {
if isAnnouncement {
Toggle(isOn: Binding(get: { delayedPostAt != nil }, set: { newValue in
withAnimation(.default) {
delayedPostAt = newValue ? Clock.now.startOfDay() : nil
delayedPostAt = newValue ? Clock.now.inCalendar.startOfDay() : nil
}
})) {
Text("Delay posting", bundle: .core)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ public struct APIGradingPeriod: Codable, Equatable {

#if DEBUG
extension APIGradingPeriod {
public static func make( id: ID = "1", title: String = "Grade Period X", start_date: Date? = Clock.now.addDays(-7), end_date: Date? = Clock.now.addDays(7)) -> APIGradingPeriod {
public static func make(
id: ID = "1",
title: String = "Grade Period X",
start_date: Date? = Clock.now.inCalendar.addDays(-7),
end_date: Date? = Clock.now.inCalendar.addDays(7)
) -> APIGradingPeriod {
return APIGradingPeriod(
id: id,
title: title,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,13 @@ struct ModuleFilePermissionEditorView: View {
separator.padding(.leading, 16)
DatePickerRow(date: fromBinding,
defaultDate: viewModel.defaultFromDate,
validUntil: viewModel.availableUntil?.addMinutes(-1) ?? .distantFuture,
validUntil: viewModel.availableUntil?.inCalendar.addMinutes(-1) ?? .distantFuture,
label: Text("From", bundle: .core))
.animation(.default, value: viewModel.availableFrom)
separator.padding(.leading, 16)
DatePickerRow(date: untilBinding,
defaultDate: viewModel.defaultUntilDate,
validFrom: viewModel.availableFrom?.addMinutes(1) ?? .distantPast,
validFrom: viewModel.availableFrom?.inCalendar.addMinutes(1) ?? .distantPast,
label: Text("Until", bundle: .core))
.animation(.default, value: viewModel.availableUntil)
}
Expand Down
Loading