Skip to content

Commit

Permalink
Merge pull request #1968 from nextcloud/feat/noid/show-upcoming-events
Browse files Browse the repository at this point in the history
feat(meetings): Show upcoming events πŸ“†
  • Loading branch information
SystemKeeper authored Feb 6, 2025
2 parents 2d5c852 + 615e2c2 commit 0f680b6
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 3 deletions.
10 changes: 10 additions & 0 deletions NextcloudTalk.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,10 @@
2C1ABDCF257E939600AEDFB6 /* NCContact.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C1ABDCD257E939600AEDFB6 /* NCContact.m */; };
2C1ABDD0257E939600AEDFB6 /* NCContact.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C1ABDCD257E939600AEDFB6 /* NCContact.m */; };
2C1ABDE5257F883400AEDFB6 /* ABContact.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C1ABDE4257F883400AEDFB6 /* ABContact.m */; };
2C1C68072D51229500A7F98A /* CalendarEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C1C68062D51229500A7F98A /* CalendarEvent.swift */; };
2C1C68082D51338400A7F98A /* CalendarEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C1C68062D51229500A7F98A /* CalendarEvent.swift */; };
2C1C68092D51338400A7F98A /* CalendarEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C1C68062D51229500A7F98A /* CalendarEvent.swift */; };
2C1C680A2D51338400A7F98A /* CalendarEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C1C68062D51229500A7F98A /* CalendarEvent.swift */; };
2C1D13A3253760EE00EC0533 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2C1D13A1253760EE00EC0533 /* LaunchScreen.xib */; };
2C1EF36B25505DCE007C9768 /* NCNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C1EF36A25505DCE007C9768 /* NCNavigationController.m */; };
2C1EF36D25505DCE007C9768 /* NCNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2C1EF36A25505DCE007C9768 /* NCNavigationController.m */; };
Expand Down Expand Up @@ -900,6 +904,7 @@
2C1ABDCD257E939600AEDFB6 /* NCContact.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NCContact.m; sourceTree = "<group>"; };
2C1ABDE3257F883400AEDFB6 /* ABContact.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ABContact.h; sourceTree = "<group>"; };
2C1ABDE4257F883400AEDFB6 /* ABContact.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ABContact.m; sourceTree = "<group>"; };
2C1C68062D51229500A7F98A /* CalendarEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarEvent.swift; sourceTree = "<group>"; };
2C1D13A2253760EE00EC0533 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
2C1EF36925505DCE007C9768 /* NCNavigationController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NCNavigationController.h; sourceTree = "<group>"; };
2C1EF36A25505DCE007C9768 /* NCNavigationController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NCNavigationController.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2185,6 +2190,7 @@
1F2058292CEA404F00AAA673 /* AiSummaryViewController.swift */,
1F20582B2CEA405700AAA673 /* AiSummaryViewController.xib */,
1F205B9F2CEE1B8800AAA673 /* AiSummaryController.swift */,
2C1C68062D51229500A7F98A /* CalendarEvent.swift */,
);
name = Chat;
sourceTree = "<group>";
Expand Down Expand Up @@ -2872,6 +2878,7 @@
1FF136132BFB6FCD006A6101 /* RLMSupport.swift in Sources */,
1F77A5ED2AB9A408007B6037 /* NCChatMessage.m in Sources */,
1F77A5EB2AB9A3EE007B6037 /* BGTaskHelper.swift in Sources */,
2C1C680A2D51338400A7F98A /* CalendarEvent.swift in Sources */,
1F205C532CEF91C500AAA673 /* UserAbsence.swift in Sources */,
1FF136182BFB74D0006A6101 /* NCChatMessage.swift in Sources */,
1F77A5FC2AB9A4ED007B6037 /* NCRoom.m in Sources */,
Expand Down Expand Up @@ -3019,6 +3026,7 @@
1F1B50472B90CDF800B0F2F4 /* TalkCapabilities.m in Sources */,
2CA1CCD01F1E1779002FE6A2 /* SearchTableViewController.m in Sources */,
1F1C0D8929AFB89900D17C6D /* VLCKitVideoViewController.swift in Sources */,
2C1C68072D51229500A7F98A /* CalendarEvent.swift in Sources */,
2C9B0B98217F6DBA00A4752C /* NCNotificationController.m in Sources */,
2CC3166E2CC698E1007CBE16 /* TextFieldTableViewCell.swift in Sources */,
2C36A04A261487BC0026F04A /* DetailedOptionsSelectorTableViewController.m in Sources */,
Expand Down Expand Up @@ -3177,6 +3185,7 @@
2C62AFFD24C1BDA5007E460A /* NCMessageParameter.m in Sources */,
1F35F8EC2AEEBC1400044BDA /* UIScrollView+SLKAdditions.m in Sources */,
1FF136172BFB74CF006A6101 /* NCChatMessage.swift in Sources */,
2C1C68092D51338400A7F98A /* CalendarEvent.swift in Sources */,
2CF338E22CED388B0029CACC /* AvatarView.swift in Sources */,
1FF4DA8A2C0262BB00C1B952 /* NCBaseSessionManager.swift in Sources */,
2C62B00C24C1BDC1007E460A /* NCNotification.m in Sources */,
Expand Down Expand Up @@ -3259,6 +3268,7 @@
F644A2DF2CE28C8D00E2ED81 /* NCChatFileStatus.swift in Sources */,
2C1ABDCF257E939600AEDFB6 /* NCContact.m in Sources */,
2CC001DC24A37AD400A20167 /* NCAppBranding.m in Sources */,
2C1C68082D51338400A7F98A /* CalendarEvent.swift in Sources */,
2C4446D42658147900DF1DBC /* TalkAccount.m in Sources */,
1FDCC3E329EC787400DEB39B /* AvatarManager.swift in Sources */,
1FF4DA852C025DC000C1B952 /* NCAPISessionManager.swift in Sources */,
Expand Down
57 changes: 57 additions & 0 deletions NextcloudTalk/CalendarEvent.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
// SPDX-License-Identifier: GPL-3.0-or-later
//

import Foundation

struct CalendarEvent {

var calendarAppUrl: String
var calendarUri: String
var location: String
var recurrenceId: String
var start: Int
var summary: String
var uri: String

init(dictionary: [String: Any]) {
self.calendarAppUrl = dictionary["calendarAppUrl"] as? String ?? ""
self.calendarUri = dictionary["calendarUri"] as? String ?? ""
self.location = dictionary["location"] as? String ?? ""
self.recurrenceId = dictionary["recurrenceId"] as? String ?? ""
self.start = dictionary["start"] as? Int ?? -1
self.summary = dictionary["summary"] as? String ?? ""
self.uri = dictionary["uri"] as? String ?? ""
}

func readableStartTime() -> String {
let eventDate = Date(timeIntervalSince1970: TimeInterval(start))
let now = Date()

// Event happening now
if eventDate <= now {
return NSLocalizedString("Now", comment: "Indicates an event happening right now")
}

// Event happening following days (except today or tomorrow)
let calendar = Calendar.current
if let nextWeek = calendar.date(byAdding: .day, value: 7, to: now),
!calendar.isDateInToday(eventDate), !calendar.isDateInTomorrow(eventDate),
eventDate < calendar.startOfDay(for: nextWeek) {
return eventDate.formatted(
.dateTime
.weekday(.wide)
.hour(.conversationalTwoDigits(amPM: .wide))
.minute(.defaultDigits))
}

// Event happening today, tomorrow or later than a week
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
dateFormatter.timeStyle = .short
dateFormatter.doesRelativeDateFormatting = true

return dateFormatter.string(from: eventDate)
}
}
45 changes: 42 additions & 3 deletions NextcloudTalk/ChatViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ import SwiftyAttributes

private var lobbyCheckTimer: Timer?

// MARK: - Call buttons in NavigationBar
// MARK: - Buttons in NavigationBar

func getCallOptionsBarButton() -> BarButtonItemWithActivity {
let symbolConfiguration = UIImage.SymbolConfiguration(pointSize: 20)
Expand Down Expand Up @@ -158,6 +158,37 @@ import SwiftyAttributes
return callOptionsButton
}()

private lazy var upcomingEventsButton: BarButtonItemWithActivity = {
let symbolConfiguration = UIImage.SymbolConfiguration(pointSize: 20)
let buttonImage = UIImage(systemName: "calendar", withConfiguration: symbolConfiguration)
let upcomingEventsButton = BarButtonItemWithActivity(width: 50, with: buttonImage)

let deferredUpcomingEvents = UIDeferredMenuElement { [weak self] completion in
guard let self = self else { return }

NCAPIController.sharedInstance().upcomingEvents(self.room, forAccount: self.account) { events in
let actions: [UIAction]
if !events.isEmpty {
actions = events.map { event in
UIAction(title: event.summary, subtitle: event.readableStartTime(), handler: { _ in })
}
} else {
actions = [UIAction(title: NSLocalizedString("No upcoming events", comment: ""), attributes: .disabled, handler: { _ in })]
}

completion(actions)
}
}

upcomingEventsButton.innerButton.menu = UIMenu(children: [deferredUpcomingEvents])
upcomingEventsButton.innerButton.showsMenuAsPrimaryAction = true

upcomingEventsButton.accessibilityLabel = NSLocalizedString("Upcoming events", comment: "")
upcomingEventsButton.accessibilityHint = NSLocalizedString("Double tap to display upcoming events", comment: "")

return upcomingEventsButton
}()

private var messageExpirationTimer: Timer?

public override init?(forRoom room: NCRoom, withAccount account: TalkAccount) {
Expand Down Expand Up @@ -209,10 +240,18 @@ import SwiftyAttributes
public override func viewDidLoad() {
super.viewDidLoad()

var barButtonsItems: [UIBarButtonItem] = []
// Call options
if room.supportsCalling {
self.navigationItem.rightBarButtonItems = [callOptionsButton]
barButtonsItems.append(callOptionsButton)
}
// Upcoming events
if NCDatabaseManager.sharedInstance().serverHasTalkCapability(kCapabilityScheduleMeeting, forAccountId: room.accountId) {
barButtonsItems.append(upcomingEventsButton)
}

self.navigationItem.rightBarButtonItems = barButtonsItems

// No sharing options in federation v1
if room.isFederated {
// When hiding the button it is still respected in the layout constraints
Expand Down Expand Up @@ -291,7 +330,7 @@ import SwiftyAttributes
// Check if new messages were added while the app was inactive (eg. via background-refresh)
self.checkForNewStoredMessages()

if !self.offlineMode {
if !self.offlineMode {
NCRoomsManager.sharedInstance().joinRoom(self.room.token, forCall: false)
}

Expand Down
23 changes: 23 additions & 0 deletions NextcloudTalk/NCAPIControllerExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -680,4 +680,27 @@ import Foundation
completionBlock(message)
}
}

// MARK: - Upcoming events

@nonobjc
func upcomingEvents(_ room: NCRoom, forAccount account: TalkAccount, completionBlock: @escaping (_ events: [CalendarEvent]) -> Void) {
guard let encodedRoomLink = room.linkURL?.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed),
let apiSessionManager = self.apiSessionManagers.object(forKey: account.accountId) as? NCAPISessionManager
else {
completionBlock([])
return
}

let urlString = "\(account.server)/ocs/v2.php/apps/dav/api/v1/events/upcoming?location=\(encodedRoomLink)"

apiSessionManager.getOcs(urlString, account: account) { ocsResponse, error in
if error == nil, let events = ocsResponse?.dataDict?["events"] as? [[String: Any]] {
let calendarEvents = events.map { CalendarEvent(dictionary: $0) }
completionBlock(calendarEvents)
} else {
completionBlock([])
}
}
}
}
1 change: 1 addition & 0 deletions NextcloudTalk/NCDatabaseManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ extern NSString * const kCapabilityCallNotificationState;
extern NSString * const kCapabilityCallForceMute;
extern NSString * const kCapabilityTalkPollsDrafts;
extern NSString * const kCapabilityEditDraftPoll;
extern NSString * const kCapabilityScheduleMeeting;

extern NSString * const kNotificationsCapabilityExists;
extern NSString * const kNotificationsCapabilityTestPush;
Expand Down
1 change: 1 addition & 0 deletions NextcloudTalk/NCDatabaseManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
NSString * const kCapabilityForceMute = @"force-mute";
NSString * const kCapabilityTalkPollsDrafts = @"talk-polls-drafts";
NSString * const kCapabilityEditDraftPoll = @"edit-draft-poll";
NSString * const kCapabilityScheduleMeeting = @"schedule-meeting";

NSString * const kNotificationsCapabilityExists = @"exists";
NSString * const kNotificationsCapabilityTestPush = @"test-push";
Expand Down
12 changes: 12 additions & 0 deletions NextcloudTalk/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,9 @@
/* No comment provided by engineer. */
"Double tap to display call options" = "Double tap to display call options";

/* No comment provided by engineer. */
"Double tap to display upcoming events" = "Double tap to display upcoming events";

/* No comment provided by engineer. */
"Double tap to edit profile" = "Double tap to edit profile";

Expand Down Expand Up @@ -1360,6 +1363,9 @@
/* No comment provided by engineer. */
"No shared items" = "No shared items";

/* No comment provided by engineer. */
"No upcoming events" = "No upcoming events";

/* No comment provided by engineer. */
"No user found" = "No user found";

Expand All @@ -1384,6 +1390,9 @@
/* No comment provided by engineer. */
"Notifications: %@" = "Notifications: %@";

/* Indicates an event happening right now */
"Now" = "Now";

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

Expand Down Expand Up @@ -2011,6 +2020,9 @@
/* No comment provided by engineer. */
"Unread messages" = "Unread messages";

/* No comment provided by engineer. */
"Upcoming events" = "Upcoming events";

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

Expand Down

0 comments on commit 0f680b6

Please sign in to comment.