Skip to content

[stable21] Fix invalid SwiftUI DatePicker dates #2073

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

Merged
merged 2 commits into from
Apr 25, 2025
Merged
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
32 changes: 27 additions & 5 deletions NextcloudTalk/NCAPIController+CalDAV.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,34 @@ extension NCAPIController {
return writableCalendars
}

public enum CreateMeetingResponse: Int {
case unknownError = 0
case success = 1
case calendarError = 2
case emailError = 3
case startError = 4
case endError = 5

init(errorKey: String?) {
switch errorKey {
case nil: self = .success
case "calendar": self = .calendarError
case "email": self = .emailError
case "start": self = .startError
case "end": self = .endError
default: self = .unknownError
}
}
}

// swiftlint:disable function_parameter_count
public func createMeeting(account: TalkAccount, token: String, title: String?, description: String?, start: Int, end: Int, calendarUri: String, attendeeIds: [Int]?, completionBlock: @escaping (_ error: Error?) -> Void) {
public func createMeeting(account: TalkAccount, token: String, title: String?, description: String?, start: Int, end: Int, calendarUri: String, attendeeIds: [Int]?, completionBlock: @escaping (_ error: CreateMeetingResponse) -> Void) {
guard let apiSessionManager = self.apiSessionManagers.object(forKey: account.accountId) as? NCAPISessionManager,
let encodedToken = token.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
else { return }
else {
completionBlock(.unknownError)
return
}

let urlString = self.getRequestURL(forConversationEndpoint: "room/\(encodedToken)/meeting", for: account)
var parameters: [String: Any] = ["calendarUri": calendarUri]
Expand All @@ -99,9 +122,8 @@ extension NCAPIController {
parameters["attendeeIds"] = attendeeIds
}

apiSessionManager.postOcs(urlString, account: account, parameters: parameters) { ocsResponse, ocsError in
let room = NCRoom(dictionary: ocsResponse?.dataDict, andAccountId: account.accountId)
completionBlock(ocsError?.error)
apiSessionManager.postOcs(urlString, account: account, parameters: parameters) { _, ocsError in
completionBlock(CreateMeetingResponse(errorKey: ocsError?.errorKey))
}
}
}
22 changes: 19 additions & 3 deletions NextcloudTalk/NCAPIControllerExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -578,21 +578,37 @@ import Foundation
}
}

public enum SetUserAbsenceResponse: Int {
case unknownError = 0
case success = 1
case statusLengthError = 2
case firstDayError = 3

init(errorKey: String?) {
switch errorKey {
case nil: self = .success
case "statusLength": self = .statusLengthError
case "firstDay": self = .firstDayError
default: self = .unknownError
}
}
}

@nonobjc
public func setUserAbsence(forAccountId accountId: String, forUserId userId: String, withAbsence absenceData: UserAbsence, completionBlock: @escaping (_ success: Bool) -> Void) {
public func setUserAbsence(forAccountId accountId: String, forUserId userId: String, withAbsence absenceData: UserAbsence, completionBlock: @escaping (_ response: SetUserAbsenceResponse) -> Void) {
guard let account = NCDatabaseManager.sharedInstance().talkAccount(forAccountId: accountId),
let apiSessionManager = self.apiSessionManagers.object(forKey: account.accountId) as? NCAPISessionManager,
let encodedUserId = userId.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed),
let absenceDictionary = absenceData.asDictionary()
else {
completionBlock(false)
completionBlock(.unknownError)
return
}

let urlString = "\(account.server)/ocs/v2.php/apps/dav/api/v1/outOfOffice/\(encodedUserId)"

apiSessionManager.postOcs(urlString, account: account, parameters: absenceDictionary) { _, ocsError in
completionBlock(ocsError == nil)
completionBlock(SetUserAbsenceResponse(errorKey: ocsError?.errorKey))
}
}

Expand Down
4 changes: 4 additions & 0 deletions NextcloudTalk/OcsError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ import Foundation
return ocsDict?["data"] as? [String: AnyObject]
}()

lazy var errorKey: String? = {
return dataDict?["error"] as? String
}()

lazy var errorMessage: String? = {
return dataDict?["message"] as? String
}()
Expand Down
39 changes: 32 additions & 7 deletions NextcloudTalk/ScheduleMeetingSwiftUIView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,14 @@ struct ScheduleMeetingSwiftUIView: View {
attendeeIds = selectedParticipants.map(\.attendeeId)
}

// Work around for a SwiftUI bug in DatePicker
// The UI does not allow to pick an end date smaller than (start + 15 min). We can still
// end up in this situation, if only the start date was modified, but not the end date.
// Therefore it can only happen if end should be (start + 15 min), so we set it here again
if start >= end {
end = start.addingTimeInterval(60 * 15)
}

isCreatingMeeting = true
NCAPIController.sharedInstance().createMeeting(
account: account,
Expand All @@ -269,15 +277,32 @@ struct ScheduleMeetingSwiftUIView: View {
end: Int(end.timeIntervalSince1970),
calendarUri: calendarUri,
attendeeIds: attendeeIds
) { error in
) { response in
self.isCreatingMeeting = false
if error == nil {
onMeetingCreationSuccess?()
NotificationPresenter.shared().present(text: NSLocalizedString("Meeting created", comment: ""), dismissAfterDelay: 5.0, includedStyle: .dark)
dismiss()
} else {
NCUserInterfaceController.sharedInstance().presentAlert(withTitle: NSLocalizedString("Could not create meeting", comment: ""), withMessage: NSLocalizedString("An error occurred while creating the meeting", comment: ""))

guard response == .success else {
var errorMessage: String

switch response {
case .calendarError:
errorMessage = NSLocalizedString("Failed to get calendar to schedule a meeting", comment: "")
case .emailError:
errorMessage = NSLocalizedString("Invalid email address", comment: "")
case .startError:
errorMessage = NSLocalizedString("Invalid start date", comment: "")
case .endError:
errorMessage = NSLocalizedString("Invalid end date", comment: "")
default:
errorMessage = NSLocalizedString("An error occurred while creating the meeting", comment: "")
}

NCUserInterfaceController.sharedInstance().presentAlert(withTitle: NSLocalizedString("Could not create meeting", comment: ""), withMessage: errorMessage)
return
}

onMeetingCreationSuccess?()
NotificationPresenter.shared().present(text: NSLocalizedString("Meeting created", comment: ""), dismissAfterDelay: 5.0, includedStyle: .dark)
dismiss()
}
}
}
Expand Down
34 changes: 27 additions & 7 deletions NextcloudTalk/UserStatusAbsenceSwiftUIView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,15 +94,35 @@ struct UserStatusAbsenceSwiftUIView: View {
}

func setActiveUserStatus() {
// Work around for a SwiftUI bug in DatePicker
// The UI does not allow to pick an end date smaller than start. We can still
// end up in this situation, if only the start date was modified, but not the end date.
// Therefore it can only happen if end should be equal to start, so we set it here again
if self.absenceStatus.firstDay >= self.absenceStatus.lastDay {
self.absenceStatus.lastDay = self.absenceStatus.firstDay
}

let activeAccount = NCDatabaseManager.sharedInstance().activeAccount()
NCAPIController.sharedInstance().setUserAbsence(forAccountId: activeAccount.accountId, forUserId: activeAccount.user, withAbsence: self.absenceStatus) { success in
if success {
dismiss()
changed.toggle()
AppStoreReviewController.recordAction(AppStoreReviewController.updateStatus)
} else {
NCUserInterfaceController.sharedInstance().presentAlert(withTitle: NSLocalizedString("Could not set absence", comment: ""), withMessage: NSLocalizedString("An error occurred while setting absence", comment: ""))
NCAPIController.sharedInstance().setUserAbsence(forAccountId: activeAccount.accountId, forUserId: activeAccount.user, withAbsence: self.absenceStatus) { response in
guard response == .success else {
var errorMessage: String

switch response {
case .firstDayError:
errorMessage = NSLocalizedString("Invalid date range", comment: "")
case .statusLengthError:
errorMessage = NSLocalizedString("Short absence status is too long", comment: "")
default:
errorMessage = NSLocalizedString("An error occurred while setting absence", comment: "")
}

NCUserInterfaceController.sharedInstance().presentAlert(withTitle: NSLocalizedString("Could not set absence", comment: ""), withMessage: errorMessage)
return
}

dismiss()
changed.toggle()
AppStoreReviewController.recordAction(AppStoreReviewController.updateStatus)
}
}

Expand Down
18 changes: 18 additions & 0 deletions NextcloudTalk/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,9 @@
/* No comment provided by engineer. */
"Failed to forward message" = "Failed to forward message";

/* No comment provided by engineer. */
"Failed to get calendar to schedule a meeting" = "Failed to get calendar to schedule a meeting";

/* No comment provided by engineer. */
"Failed to reject invitation" = "Failed to reject invitation";

Expand Down Expand Up @@ -1108,9 +1111,21 @@
/* Internal note about why a user/guest was banned */
"Internal note" = "Internal note";

/* No comment provided by engineer. */
"Invalid date range" = "Invalid date range";

/* No comment provided by engineer. */
"Invalid email address" = "Invalid email address";

/* No comment provided by engineer. */
"Invalid end date" = "Invalid end date";

/* No comment provided by engineer. */
"Invalid server address" = "Invalid server address";

/* No comment provided by engineer. */
"Invalid start date" = "Invalid start date";

/* No comment provided by engineer. */
"Invisible" = "Invisible";

Expand Down Expand Up @@ -1825,6 +1840,9 @@
/* No comment provided by engineer. */
"Short absence status" = "Short absence status";

/* No comment provided by engineer. */
"Short absence status is too long" = "Short absence status is too long";

/* No comment provided by engineer. */
"Show more…" = "Show more…";

Expand Down
Loading