feat: update to alpha.17, typed mobile plugin IPC arguments (#676)

Co-authored-by: Amr Bashir <amr.bashir2015@gmail.com>
This commit is contained in:
Lucas Fernandes Nogueira
2023-10-29 16:06:44 -03:00
committed by GitHub
parent 76cfdc32b4
commit e438e0a62d
158 changed files with 1677 additions and 1658 deletions
@@ -6,12 +6,7 @@ import Tauri
import UserNotifications
enum NotificationError: LocalizedError {
case contentNoId
case contentNoTitle
case contentNoBody
case triggerRepeatIntervalTooShort
case attachmentNoId
case attachmentNoUrl
case attachmentFileNotFound(path: String)
case attachmentUnableToCreate(String)
case pastScheduledTime
@@ -19,18 +14,8 @@ enum NotificationError: LocalizedError {
var errorDescription: String? {
switch self {
case .contentNoId:
return "Missing notification identifier"
case .contentNoTitle:
return "Missing notification title"
case .contentNoBody:
return "Missing notification body"
case .triggerRepeatIntervalTooShort:
return "Schedule interval too short, must be a least 1 minute"
case .attachmentNoId:
return "Missing attachment identifier"
case .attachmentNoUrl:
return "Missing attachment URL"
case .attachmentFileNotFound(let path):
return "Unable to find file \(path) for attachment"
case .attachmentUnableToCreate(let error):
@@ -43,69 +28,56 @@ enum NotificationError: LocalizedError {
}
}
func makeNotificationContent(_ notification: JSObject) throws -> UNNotificationContent {
guard let title = notification["title"] as? String else {
throw NotificationError.contentNoTitle
}
guard let body = notification["body"] as? String else {
throw NotificationError.contentNoBody
}
let extra = notification["extra"] as? JSObject ?? [:]
let schedule = notification["schedule"] as? JSObject ?? [:]
func makeNotificationContent(_ notification: Notification) throws -> UNNotificationContent {
let content = UNMutableNotificationContent()
content.title = NSString.localizedUserNotificationString(forKey: title, arguments: nil)
content.title = NSString.localizedUserNotificationString(
forKey: notification.title, arguments: nil)
content.body = NSString.localizedUserNotificationString(
forKey: body,
forKey: notification.body,
arguments: nil)
content.userInfo = [
"__EXTRA__": extra,
"__SCHEDULE__": schedule,
"__EXTRA__": notification.extra as Any,
"__SCHEDULE__": notification.schedule as Any,
]
if let actionTypeId = notification["actionTypeId"] as? String {
if let actionTypeId = notification.actionTypeId {
content.categoryIdentifier = actionTypeId
}
if let threadIdentifier = notification["group"] as? String {
if let threadIdentifier = notification.group {
content.threadIdentifier = threadIdentifier
}
if let summaryArgument = notification["summary"] as? String {
if let summaryArgument = notification.summary {
content.summaryArgument = summaryArgument
}
if let sound = notification["sound"] as? String {
if let sound = notification.sound {
content.sound = UNNotificationSound(named: UNNotificationSoundName(sound))
}
if let attachments = notification["attachments"] as? [JSObject] {
if let attachments = notification.attachments {
content.attachments = try makeAttachments(attachments)
}
return content
}
func makeAttachments(_ attachments: [JSObject]) throws -> [UNNotificationAttachment] {
func makeAttachments(_ attachments: [NotificationAttachment]) throws -> [UNNotificationAttachment] {
var createdAttachments = [UNNotificationAttachment]()
for attachment in attachments {
guard let id = attachment["id"] as? String else {
throw NotificationError.attachmentNoId
}
guard let url = attachment["url"] as? String else {
throw NotificationError.attachmentNoUrl
}
guard let urlObject = makeAttachmentUrl(url) else {
throw NotificationError.attachmentFileNotFound(path: url)
guard let urlObject = makeAttachmentUrl(attachment.url) else {
throw NotificationError.attachmentFileNotFound(path: attachment.url)
}
let options = attachment["options"] as? JSObject ?? [:]
let options = attachment.options != nil ? makeAttachmentOptions(attachment.options!) : nil
do {
let newAttachment = try UNNotificationAttachment(
identifier: id, url: urlObject, options: makeAttachmentOptions(options))
identifier: attachment.id, url: urlObject, options: options)
createdAttachments.append(newAttachment)
} catch {
throw NotificationError.attachmentUnableToCreate(error.localizedDescription)
@@ -119,50 +91,37 @@ func makeAttachmentUrl(_ path: String) -> URL? {
return URL(string: path)
}
func makeAttachmentOptions(_ options: JSObject) -> JSObject {
var opts: JSObject = [:]
func makeAttachmentOptions(_ options: NotificationAttachmentOptions) -> [AnyHashable: Any] {
var opts: [AnyHashable: Any] = [:]
if let iosUNNotificationAttachmentOptionsTypeHintKey = options[
"iosUNNotificationAttachmentOptionsTypeHintKey"] as? String
{
opts[UNNotificationAttachmentOptionsTypeHintKey] = iosUNNotificationAttachmentOptionsTypeHintKey
if let value = options.iosUNNotificationAttachmentOptionsTypeHintKey {
opts[UNNotificationAttachmentOptionsTypeHintKey] = value
}
if let iosUNNotificationAttachmentOptionsThumbnailHiddenKey = options[
"iosUNNotificationAttachmentOptionsThumbnailHiddenKey"] as? String
{
opts[UNNotificationAttachmentOptionsThumbnailHiddenKey] =
iosUNNotificationAttachmentOptionsThumbnailHiddenKey
if let value = options.iosUNNotificationAttachmentOptionsThumbnailHiddenKey {
opts[UNNotificationAttachmentOptionsThumbnailHiddenKey] = value
}
if let iosUNNotificationAttachmentOptionsThumbnailClippingRectKey = options[
"iosUNNotificationAttachmentOptionsThumbnailClippingRectKey"] as? String
{
opts[UNNotificationAttachmentOptionsThumbnailClippingRectKey] =
iosUNNotificationAttachmentOptionsThumbnailClippingRectKey
if let value = options.iosUNNotificationAttachmentOptionsThumbnailClippingRectKey {
opts[UNNotificationAttachmentOptionsThumbnailClippingRectKey] = value
}
if let iosUNNotificationAttachmentOptionsThumbnailTimeKey = options[
"iosUNNotificationAttachmentOptionsThumbnailTimeKey"] as? String
if let value = options
.iosUNNotificationAttachmentOptionsThumbnailTimeKey
{
opts[UNNotificationAttachmentOptionsThumbnailTimeKey] =
iosUNNotificationAttachmentOptionsThumbnailTimeKey
opts[UNNotificationAttachmentOptionsThumbnailTimeKey] = value
}
return opts
}
func handleScheduledNotification(_ schedule: JSObject) throws
func handleScheduledNotification(_ schedule: NotificationSchedule) throws
-> UNNotificationTrigger?
{
let kind = schedule["kind"] as? String ?? ""
let payload = schedule["data"] as? JSObject ?? [:]
switch kind {
case "At":
let date = payload["date"] as? String ?? ""
switch schedule {
case .at(let date, let repeating):
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
if let at = dateFormatter.date(from: date) {
let repeats = payload["repeats"] as? Bool ?? false
let dateInfo = Calendar.current.dateComponents(in: TimeZone.current, from: at)
if dateInfo.date! < Date() {
@@ -172,23 +131,20 @@ func handleScheduledNotification(_ schedule: JSObject) throws
let dateInterval = DateInterval(start: Date(), end: dateInfo.date!)
// Notifications that repeat have to be at least a minute between each other
if repeats && dateInterval.duration < 60 {
if repeating && dateInterval.duration < 60 {
throw NotificationError.triggerRepeatIntervalTooShort
}
return UNTimeIntervalNotificationTrigger(
timeInterval: dateInterval.duration, repeats: repeats)
timeInterval: dateInterval.duration, repeats: repeating)
} else {
throw NotificationError.invalidDate(date)
}
case "Interval":
let dateComponents = getDateComponents(payload)
case .interval(let interval):
let dateComponents = getDateComponents(interval)
return UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
case "Every":
let interval = payload["interval"] as? String ?? ""
let count = schedule["count"] as? Int ?? 1
case .every(let interval, let count):
if let repeatDateInterval = getRepeatDateInterval(interval, count) {
// Notifications that repeat have to be at least a minute between each other
if repeatDateInterval.duration < 60 {
@@ -198,9 +154,6 @@ func handleScheduledNotification(_ schedule: JSObject) throws
return UNTimeIntervalNotificationTrigger(
timeInterval: repeatDateInterval.duration, repeats: true)
}
default:
return nil
}
return nil
@@ -209,30 +162,30 @@ func handleScheduledNotification(_ schedule: JSObject) throws
/// Given our schedule format, return a DateComponents object
/// that only contains the components passed in.
func getDateComponents(_ at: JSObject) -> DateComponents {
func getDateComponents(_ at: ScheduleInterval) -> DateComponents {
// var dateInfo = Calendar.current.dateComponents(in: TimeZone.current, from: Date())
// dateInfo.calendar = Calendar.current
var dateInfo = DateComponents()
if let year = at["year"] as? Int {
if let year = at.year {
dateInfo.year = year
}
if let month = at["month"] as? Int {
if let month = at.month {
dateInfo.month = month
}
if let day = at["day"] as? Int {
if let day = at.day {
dateInfo.day = day
}
if let hour = at["hour"] as? Int {
if let hour = at.hour {
dateInfo.hour = hour
}
if let minute = at["minute"] as? Int {
if let minute = at.minute {
dateInfo.minute = minute
}
if let second = at["second"] as? Int {
if let second = at.second {
dateInfo.second = second
}
if let weekday = at["weekday"] as? Int {
if let weekday = at.weekday {
dateInfo.weekday = weekday
}
return dateInfo
@@ -242,35 +195,33 @@ func getDateComponents(_ at: JSObject) -> DateComponents {
/// interval and today. For example, if every is "month", then we
/// return the interval between today and a month from today.
func getRepeatDateInterval(_ every: String, _ count: Int) -> DateInterval? {
func getRepeatDateInterval(_ every: ScheduleEveryKind, _ count: Int) -> DateInterval? {
let cal = Calendar.current
let now = Date()
switch every {
case "Year":
case .year:
let newDate = cal.date(byAdding: .year, value: count, to: now)!
return DateInterval(start: now, end: newDate)
case "Month":
case .month:
let newDate = cal.date(byAdding: .month, value: count, to: now)!
return DateInterval(start: now, end: newDate)
case "TwoWeeks":
case .twoWeeks:
let newDate = cal.date(byAdding: .weekOfYear, value: 2 * count, to: now)!
return DateInterval(start: now, end: newDate)
case "Week":
case .week:
let newDate = cal.date(byAdding: .weekOfYear, value: count, to: now)!
return DateInterval(start: now, end: newDate)
case "Day":
case .day:
let newDate = cal.date(byAdding: .day, value: count, to: now)!
return DateInterval(start: now, end: newDate)
case "Hour":
case .hour:
let newDate = cal.date(byAdding: .hour, value: count, to: now)!
return DateInterval(start: now, end: newDate)
case "Minute":
case .minute:
let newDate = cal.date(byAdding: .minute, value: count, to: now)!
return DateInterval(start: now, end: newDate)
case "Second":
case .second:
let newDate = cal.date(byAdding: .second, value: count, to: now)!
return DateInterval(start: now, end: newDate)
default:
return nil
}
}
@@ -5,21 +5,7 @@
import Tauri
import UserNotifications
enum CategoryError: LocalizedError {
case noId
case noActionId
var errorDescription: String? {
switch self {
case .noId:
return "Action type `id` missing"
case .noActionId:
return "Action `id` missing"
}
}
}
public func makeCategories(_ actionTypes: [JSObject]) throws {
internal func makeCategories(_ actionTypes: [ActionType]) {
var createdCategories = [UNNotificationCategory]()
let generalCategory = UNNotificationCategory(
@@ -30,22 +16,16 @@ public func makeCategories(_ actionTypes: [JSObject]) throws {
createdCategories.append(generalCategory)
for type in actionTypes {
guard let id = type["id"] as? String else {
throw CategoryError.noId
}
let hiddenBodyPlaceholder = type["hiddenPreviewsBodyPlaceholder"] as? String ?? ""
let actions = type["actions"] as? [JSObject] ?? []
let newActions = try makeActions(actions)
let newActions = makeActions(type.actions)
// Create the custom actions for the TIMER_EXPIRED category.
var newCategory: UNNotificationCategory?
newCategory = UNNotificationCategory(
identifier: id,
identifier: type.id,
actions: newActions,
intentIdentifiers: [],
hiddenPreviewsBodyPlaceholder: hiddenBodyPlaceholder,
hiddenPreviewsBodyPlaceholder: type.hiddenBodyPlaceholder ?? "",
options: makeCategoryOptions(type))
createdCategories.append(newCategory!)
@@ -55,37 +35,28 @@ public func makeCategories(_ actionTypes: [JSObject]) throws {
center.setNotificationCategories(Set(createdCategories))
}
func makeActions(_ actions: [JSObject]) throws -> [UNNotificationAction] {
func makeActions(_ actions: [Action]) -> [UNNotificationAction] {
var createdActions = [UNNotificationAction]()
for action in actions {
guard let id = action["id"] as? String else {
throw CategoryError.noActionId
}
let title = action["title"] as? String ?? ""
let input = action["input"] as? Bool ?? false
var newAction: UNNotificationAction
if input {
let inputButtonTitle = action["inputButtonTitle"] as? String
let inputPlaceholder = action["inputPlaceholder"] as? String ?? ""
if inputButtonTitle != nil {
if action.input {
if action.inputButtonTitle != nil {
newAction = UNTextInputNotificationAction(
identifier: id,
title: title,
identifier: action.id,
title: action.title,
options: makeActionOptions(action),
textInputButtonTitle: inputButtonTitle!,
textInputPlaceholder: inputPlaceholder)
textInputButtonTitle: action.inputButtonTitle ?? "",
textInputPlaceholder: action.inputPlaceholder ?? "")
} else {
newAction = UNTextInputNotificationAction(
identifier: id, title: title, options: makeActionOptions(action))
identifier: action.id, title: action.title, options: makeActionOptions(action))
}
} else {
// Create the custom actions for the TIMER_EXPIRED category.
newAction = UNNotificationAction(
identifier: id,
title: title,
identifier: action.id,
title: action.title,
options: makeActionOptions(action))
}
createdActions.append(newAction)
@@ -94,40 +65,31 @@ func makeActions(_ actions: [JSObject]) throws -> [UNNotificationAction] {
return createdActions
}
func makeActionOptions(_ action: JSObject) -> UNNotificationActionOptions {
let foreground = action["foreground"] as? Bool ?? false
let destructive = action["destructive"] as? Bool ?? false
let requiresAuthentication = action["requiresAuthentication"] as? Bool ?? false
if foreground {
func makeActionOptions(_ action: Action) -> UNNotificationActionOptions {
if action.foreground {
return .foreground
}
if destructive {
if action.destructive {
return .destructive
}
if requiresAuthentication {
if action.requiresAuthentication {
return .authenticationRequired
}
return UNNotificationActionOptions(rawValue: 0)
}
func makeCategoryOptions(_ type: JSObject) -> UNNotificationCategoryOptions {
let customDismiss = type["customDismissAction"] as? Bool ?? false
let carPlay = type["allowInCarPlay"] as? Bool ?? false
let hiddenPreviewsShowTitle = type["hiddenPreviewsShowTitle"] as? Bool ?? false
let hiddenPreviewsShowSubtitle = type["hiddenPreviewsShowSubtitle"] as? Bool ?? false
if customDismiss {
func makeCategoryOptions(_ type: ActionType) -> UNNotificationCategoryOptions {
if type.customDismissAction {
return .customDismissAction
}
if carPlay {
if type.allowInCarPlay {
return .allowInCarPlay
}
if hiddenPreviewsShowTitle {
if type.hiddenPreviewsShowTitle {
return .hiddenPreviewsShowTitle
}
if hiddenPreviewsShowSubtitle {
if type.hiddenPreviewsShowSubtitle {
return .hiddenPreviewsShowSubtitle
}
@@ -9,9 +9,9 @@ public class NotificationHandler: NSObject, NotificationHandlerProtocol {
public weak var plugin: Plugin?
private var notificationsMap = [String: JSObject]()
private var notificationsMap = [String: Notification]()
public func saveNotification(_ key: String, _ notification: JSObject) {
internal func saveNotification(_ key: String, _ notification: Notification) {
notificationsMap.updateValue(notification, forKey: key)
}
@@ -30,12 +30,11 @@ public class NotificationHandler: NSObject, NotificationHandlerProtocol {
}
public func willPresent(notification: UNNotification) -> UNNotificationPresentationOptions {
let notificationData = makeNotificationRequestJSObject(notification.request)
self.plugin?.trigger("notification", data: notificationData)
let notificationData = toActiveNotification(notification.request)
try? self.plugin?.trigger("notification", data: notificationData)
if let options = notificationsMap[notification.request.identifier] {
let silent = options["silent"] as? Bool ?? false
if silent {
if options.silent {
return UNNotificationPresentationOptions.init(rawValue: 0)
}
}
@@ -48,73 +47,72 @@ public class NotificationHandler: NSObject, NotificationHandlerProtocol {
}
public func didReceive(response: UNNotificationResponse) {
var data = JSObject()
let originalNotificationRequest = response.notification.request
let actionId = response.actionIdentifier
var actionIdValue: String
// We turn the two default actions (open/dismiss) into generic strings
if actionId == UNNotificationDefaultActionIdentifier {
data["actionId"] = "tap"
actionIdValue = "tap"
} else if actionId == UNNotificationDismissActionIdentifier {
data["actionId"] = "dismiss"
actionIdValue = "dismiss"
} else {
data["actionId"] = actionId
actionIdValue = actionId
}
var inputValue: String? = nil
// If the type of action was for an input type, get the value
if let inputType = response as? UNTextInputNotificationResponse {
data["inputValue"] = inputType.userText
inputValue = inputType.userText
}
data["notification"] = makeNotificationRequestJSObject(originalNotificationRequest)
self.plugin?.trigger("actionPerformed", data: data)
try? self.plugin?.trigger(
"actionPerformed",
data: ReceivedNotification(
actionId: actionIdValue,
inputValue: inputValue,
notification: toActiveNotification(originalNotificationRequest)
))
}
/**
* Turn a UNNotificationRequest into a JSObject to return back to the client.
*/
func makeNotificationRequestJSObject(_ request: UNNotificationRequest) -> JSObject {
let notificationRequest = notificationsMap[request.identifier] ?? [:]
var notification = makePendingNotificationRequestJSObject(request)
notification["sound"] = notificationRequest["sound"] ?? ""
notification["actionTypeId"] = request.content.categoryIdentifier
notification["attachments"] = notificationRequest["attachments"] ?? [JSObject]()
return notification
func toActiveNotification(_ request: UNNotificationRequest) -> ActiveNotification {
let notificationRequest = notificationsMap[request.identifier]!
return ActiveNotification(
id: Int(request.identifier) ?? -1,
title: request.content.title,
body: request.content.body,
sound: notificationRequest.sound ?? "",
actionTypeId: request.content.categoryIdentifier,
attachments: notificationRequest.attachments
)
}
func makePendingNotificationRequestJSObject(_ request: UNNotificationRequest) -> JSObject {
var notification: JSObject = [
"id": Int(request.identifier) ?? -1,
"title": request.content.title,
"body": request.content.body,
]
if let userInfo = JSTypes.coerceDictionaryToJSObject(request.content.userInfo) {
var extra = userInfo["__EXTRA__"] as? JSObject ?? userInfo
// check for any dates and convert them to strings
for (key, value) in extra {
if let date = value as? Date {
let dateString = ISO8601DateFormatter().string(from: date)
extra[key] = dateString
}
}
notification["extra"] = extra
if var schedule = userInfo["__SCHEDULE__"] as? JSObject {
// convert schedule at date to string
if let date = schedule["at"] as? Date {
let dateString = ISO8601DateFormatter().string(from: date)
schedule["at"] = dateString
}
notification["schedule"] = schedule
}
}
return notification
func toPendingNotification(_ request: UNNotificationRequest) -> PendingNotification {
return PendingNotification(
id: Int(request.identifier) ?? -1,
title: request.content.title,
body: request.content.body
)
}
}
struct PendingNotification: Encodable {
let id: Int
let title: String
let body: String
}
struct ActiveNotification: Encodable {
let id: Int
let title: String
let body: String
let sound: String
let actionTypeId: String
let attachments: [NotificationAttachment]?
}
struct ReceivedNotification: Encodable {
let actionId: String
let inputValue: String?
let notification: ActiveNotification
}
@@ -19,9 +19,11 @@ import UserNotifications
center.delegate = self
}
public func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
public func userNotificationCenter(
_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
) {
var presentationOptions: UNNotificationPresentationOptions? = nil
if notification.request.trigger?.isKind(of: UNPushNotificationTrigger.self) != true {
@@ -31,9 +33,11 @@ import UserNotifications
completionHandler(presentationOptions ?? [])
}
public func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
public func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
if response.notification.request.trigger?.isKind(of: UNPushNotificationTrigger.self) != true {
notificationHandler?.didReceive(response: response)
}
@@ -9,14 +9,11 @@ import UserNotifications
import WebKit
enum ShowNotificationError: LocalizedError {
case noId
case make(Error)
case create(Error)
var errorDescription: String? {
switch self {
case .noId:
return "notification `id` missing"
case .make(let error):
return "Unable to make notification: \(error)"
case .create(let error):
@@ -25,13 +22,71 @@ enum ShowNotificationError: LocalizedError {
}
}
func showNotification(invoke: Invoke, notification: JSObject)
enum ScheduleEveryKind: String, Decodable {
case year
case month
case twoWeeks
case week
case day
case hour
case minute
case second
}
struct ScheduleInterval: Decodable {
let year: Int?
let month: Int?
let day: Int?
let weekday: Int?
let hour: Int?
let minute: Int?
let second: Int?
}
enum NotificationSchedule: Decodable {
case at(date: String, repeating: Bool)
case interval(interval: ScheduleInterval)
case every(interval: ScheduleEveryKind, count: Int)
}
struct NotificationAttachmentOptions: Codable {
let iosUNNotificationAttachmentOptionsTypeHintKey: String?
let iosUNNotificationAttachmentOptionsThumbnailHiddenKey: String?
let iosUNNotificationAttachmentOptionsThumbnailClippingRectKey: String?
let iosUNNotificationAttachmentOptionsThumbnailTimeKey: String?
}
struct NotificationAttachment: Codable {
let id: String
let url: String
let options: NotificationAttachmentOptions?
}
struct Notification: Decodable {
let id: Int
var title: String = ""
var body: String = ""
var extra: [String: String] = [:]
let schedule: NotificationSchedule?
let attachments: [NotificationAttachment]?
let sound: String?
let group: String?
let actionTypeId: String?
let summary: String?
var silent = false
}
struct RemoveActiveNotification: Decodable {
let id: Int
}
struct RemoveActiveArgs: Decodable {
let notifications: [RemoveActiveNotification]
}
func showNotification(invoke: Invoke, notification: Notification)
throws -> UNNotificationRequest
{
guard let identifier = notification["id"] as? Int else {
throw ShowNotificationError.noId
}
var content: UNNotificationContent
do {
content = try makeNotificationContent(notification)
@@ -42,7 +97,7 @@ func showNotification(invoke: Invoke, notification: JSObject)
var trigger: UNNotificationTrigger?
do {
if let schedule = notification["schedule"] as? JSObject {
if let schedule = notification.schedule {
try trigger = handleScheduledNotification(schedule)
}
} catch {
@@ -51,7 +106,7 @@ func showNotification(invoke: Invoke, notification: JSObject)
// Schedule the request.
let request = UNNotificationRequest(
identifier: "\(identifier)", content: content, trigger: trigger
identifier: "\(notification.id)", content: content, trigger: trigger
)
let center = UNUserNotificationCenter.current()
@@ -64,6 +119,40 @@ func showNotification(invoke: Invoke, notification: JSObject)
return request
}
struct CancelArgs: Decodable {
let notifications: [Int]
}
struct Action: Decodable {
let id: String
let title: String
var requiresAuthentication: Bool = false
var foreground: Bool = false
var destructive: Bool = false
var input: Bool = false
let inputButtonTitle: String?
let inputPlaceholder: String?
}
struct ActionType: Decodable {
let id: String
let actions: [Action]
let hiddenPreviewsBodyPlaceholder: String?
var customDismissAction = false
var allowInCarPlay = false
var hiddenPreviewsShowTitle = false
var hiddenPreviewsShowSubtitle = false
let hiddenBodyPlaceholder: String?
}
struct RegisterActionTypesArgs: Decodable {
let types: [ActionType]
}
struct BatchArgs: Decodable {
let notifications: [Notification]
}
class NotificationPlugin: Plugin {
let notificationHandler = NotificationHandler()
let notificationManager = NotificationManager()
@@ -75,29 +164,24 @@ class NotificationPlugin: Plugin {
}
@objc public func show(_ invoke: Invoke) throws {
let request = try showNotification(invoke: invoke, notification: invoke.data)
notificationHandler.saveNotification(request.identifier, invoke.data)
invoke.resolve([
"id": Int(request.identifier) ?? -1
])
let notification = try invoke.parseArgs(Notification.self)
let request = try showNotification(invoke: invoke, notification: notification)
notificationHandler.saveNotification(request.identifier, notification)
invoke.resolve(Int(request.identifier) ?? -1)
}
@objc public func batch(_ invoke: Invoke) throws {
guard let notifications = invoke.getArray("notifications", JSObject.self) else {
invoke.reject("`notifications` array is required")
return
}
let args = try invoke.parseArgs(BatchArgs.self)
var ids = [Int]()
for notification in notifications {
for notification in args.notifications {
let request = try showNotification(invoke: invoke, notification: notification)
notificationHandler.saveNotification(request.identifier, notification)
ids.append(Int(request.identifier) ?? -1)
}
invoke.resolve([
"notifications": ids
])
invoke.resolve(ids)
}
@objc public override func requestPermissions(_ invoke: Invoke) {
@@ -129,18 +213,11 @@ class NotificationPlugin: Plugin {
}
}
@objc func cancel(_ invoke: Invoke) {
guard let notifications = invoke.getArray("notifications", NSNumber.self),
notifications.count > 0
else {
invoke.reject("`notifications` input is required")
return
}
@objc func cancel(_ invoke: Invoke) throws {
let args = try invoke.parseArgs(CancelArgs.self)
UNUserNotificationCenter.current().removePendingNotificationRequests(
withIdentifiers: notifications.map({ (id) -> String in
return id.stringValue
})
withIdentifiers: args.notifications.map { String($0) }
)
invoke.resolve()
}
@@ -148,30 +225,27 @@ class NotificationPlugin: Plugin {
@objc func getPending(_ invoke: Invoke) {
UNUserNotificationCenter.current().getPendingNotificationRequests(completionHandler: {
(notifications) in
let ret = notifications.compactMap({ [weak self] (notification) -> JSObject? in
return self?.notificationHandler.makePendingNotificationRequestJSObject(notification)
let ret = notifications.compactMap({ [weak self] (notification) -> PendingNotification? in
return self?.notificationHandler.toPendingNotification(notification)
})
invoke.resolve([
"notifications": ret
])
invoke.resolve(ret)
})
}
@objc func registerActionTypes(_ invoke: Invoke) throws {
guard let types = invoke.getArray("types", JSObject.self) else {
return
}
try makeCategories(types)
let args = try invoke.parseArgs(RegisterActionTypesArgs.self)
makeCategories(args.types)
invoke.resolve()
}
@objc func removeActive(_ invoke: Invoke) {
if let notifications = invoke.getArray("notifications", JSObject.self) {
let ids = notifications.map { "\($0["id"] ?? "")" }
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: ids)
do {
let args = try invoke.parseArgs(RemoveActiveArgs.self)
UNUserNotificationCenter.current().removeDeliveredNotifications(
withIdentifiers: args.notifications.map { String($0.id) })
invoke.resolve()
} else {
} catch {
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
DispatchQueue.main.async(execute: {
UIApplication.shared.applicationIconBadgeNumber = 0
@@ -183,13 +257,11 @@ class NotificationPlugin: Plugin {
@objc func getActive(_ invoke: Invoke) {
UNUserNotificationCenter.current().getDeliveredNotifications(completionHandler: {
(notifications) in
let ret = notifications.map({ (notification) -> [String: Any] in
return self.notificationHandler.makeNotificationRequestJSObject(
let ret = notifications.map({ (notification) -> ActiveNotification in
return self.notificationHandler.toActiveNotification(
notification.request)
})
invoke.resolve([
"notifications": ret
])
invoke.resolve(ret)
})
}