Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 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
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ public struct CVLongPressHandler {

switch gestureLocation {
case .`default`:
// TODO: Rename from "Text view item" to "default"?
// TODO: Rename from "Text view item" to "default"? - "TextViewItem" is more descriptive imho. Lets keep that.
delegate.didLongPressTextViewItem(cell,
itemViewModel: itemViewModel,
shouldAllowReply: shouldAllowReply)
Expand Down
141 changes: 90 additions & 51 deletions Signal/Notifications/NotificationActionHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -111,72 +111,111 @@ public class NotificationActionHandler {

private class func markAsRead(userInfo: [AnyHashable: Any]) throws -> Promise<Void> {
return firstly {
self.notificationMessage(forUserInfo: userInfo)
self.getNotificationMessage(forUserInfo: userInfo)
}.then(on: DispatchQueue.global()) { (notificationMessage: NotificationMessage) in
self.markMessageAsRead(notificationMessage: notificationMessage)
}
}

private class func reply(userInfo: [AnyHashable: Any], replyText: String) throws -> Promise<Void> {
return firstly { () -> Promise<NotificationMessage> in
self.notificationMessage(forUserInfo: userInfo)
return self.getNotificationMessage(forUserInfo: userInfo)
}.then(on: DispatchQueue.global()) { (notificationMessage: NotificationMessage) -> Promise<Void> in
let thread = notificationMessage.thread
let interaction = notificationMessage.interaction
guard (interaction is TSOutgoingMessage) || (interaction is TSIncomingMessage) else {
throw OWSAssertionError("Unexpected interaction type.")
try sendReplyToNotificationMessage(replyText: replyText, notificationMessage: notificationMessage)
}
}

//TODO: Lets invest a lot of time and resources in order to convert promises into using await Task as this below was the problem that await Task was created to solve.
private class func sendReplyToNotificationMessage(replyText: String, notificationMessage: NotificationMessage) throws -> Promise<Void> {
let thread = notificationMessage.thread
let interaction = notificationMessage.interaction
guard (interaction is TSOutgoingMessage) || (interaction is TSIncomingMessage) else {
throw OWSAssertionError("Unexpected interaction type.")
}
return firstly { () -> Promise<DraftQuotedReplyModel.ForSending?> in
return try getDraftQuotedReplyModelForSendingFromIncomingMessage(notificationMessage: notificationMessage)
}.then(on: DispatchQueue.global()) { (draftModelForSending: DraftQuotedReplyModel.ForSending?) -> Promise<Void> in
try sendReplyToNoficationMessageWithMessageToReplyTo(replyText: replyText, messageBeingRespondedTo: draftModelForSending, notificationMessage: notificationMessage)
}.recover(on: DispatchQueue.global()) { error -> Promise<Void> in
Logger.warn("Failed to send reply message from notification with error: \(error)")
SSKEnvironment.shared.notificationPresenterRef.notifyUserOfFailedSend(inThread: thread)
throw error
}.then(on: DispatchQueue.global()) { () -> Promise<Void> in
self.markMessageAsRead(notificationMessage: notificationMessage)
}
}

private class func getDraftQuotedReplyModelForSendingFromIncomingMessage(notificationMessage: NotificationMessage) throws -> Promise<DraftQuotedReplyModel.ForSending?> {
return firstly { () -> Promise<DraftQuotedReplyModel?> in
return getDraftQuotedReplyModelFromIncomingMessage(notificationMessage: notificationMessage)
}.then(on: DispatchQueue.global()) { (draftModel: DraftQuotedReplyModel?) -> Promise<DraftQuotedReplyModel.ForSending?> in
return firstly(on: DispatchQueue.global()) {
if let nonNilDraftModel = draftModel {
return try DependenciesBridge.shared.quotedReplyManager.prepareDraftForSending(nonNilDraftModel)
}
return nil
}

return firstly(on: DispatchQueue.global()) { () -> Promise<Void> in
SSKEnvironment.shared.databaseStorageRef.write { transaction in
let builder: TSOutgoingMessageBuilder = .withDefaultValues(thread: thread)
builder.messageBody = replyText

// If we're replying to a group story reply, keep the reply within that context.
if
let incomingMessage = interaction as? TSIncomingMessage,
notificationMessage.isGroupStoryReply,
let storyTimestamp = incomingMessage.storyTimestamp,
let storyAuthorAci = incomingMessage.storyAuthorAci
{
builder.storyTimestamp = storyTimestamp
builder.storyAuthorAci = storyAuthorAci
} else {
// We only use the thread's DM timer for normal messages & 1:1 story
// replies -- group story replies last for the lifetime of the story.
let dmConfigurationStore = DependenciesBridge.shared.disappearingMessagesConfigurationStore
let dmConfig = dmConfigurationStore.fetchOrBuildDefault(for: .thread(thread), tx: transaction.asV2Read)
builder.expiresInSeconds = dmConfig.durationSeconds
builder.expireTimerVersion = NSNumber(value: dmConfig.timerVersion)
}

let unpreparedMessage = UnpreparedOutgoingMessage.forMessage(TSOutgoingMessage(
outgoingMessageWith: builder,
additionalRecipients: [],
explicitRecipients: [],
skippedRecipients: [],
transaction: transaction
))
do {
let preparedMessage = try unpreparedMessage.prepare(tx: transaction)
return ThreadUtil.enqueueMessagePromise(message: preparedMessage, transaction: transaction)
} catch {
return Promise(error: error)
}
}

private class func getDraftQuotedReplyModelFromIncomingMessage(notificationMessage: NotificationMessage) -> Promise<DraftQuotedReplyModel?> {
return firstly(on: DispatchQueue.global()) {
return SSKEnvironment.shared.databaseStorageRef.read { readTransaction in
//TODO: I dont know if this is the correct way to create a DraftQuotedReplyModel, but this is what I saw on ConversationViewController+MessageActionsDelegate line 190
if let incomingMessage = notificationMessage.interaction as? TSIncomingMessage {
if let draftQuotedReplyModel = DependenciesBridge.shared.quotedReplyManager.buildDraftQuotedReply(originalMessage: incomingMessage, tx: readTransaction.asV2Read) {
return draftQuotedReplyModel
}
}
}.recover(on: DispatchQueue.global()) { error -> Promise<Void> in
Logger.warn("Failed to send reply message from notification with error: \(error)")
SSKEnvironment.shared.notificationPresenterRef.notifyUserOfFailedSend(inThread: thread)
throw error
}.then(on: DispatchQueue.global()) { () -> Promise<Void> in
self.markMessageAsRead(notificationMessage: notificationMessage)
return nil
}
}
}

private class func sendReplyToNoficationMessageWithMessageToReplyTo(replyText: String, messageBeingRespondedTo: DraftQuotedReplyModel.ForSending?, notificationMessage: NotificationMessage) throws -> Promise<Void> {
return SSKEnvironment.shared.databaseStorageRef.write { transaction in
let builder: TSOutgoingMessageBuilder = .withDefaultValues(thread: notificationMessage.thread)
builder.messageBody = replyText

// If we're replying to a group story reply, keep the reply within that context.
if
let incomingMessage = notificationMessage.interaction as? TSIncomingMessage,
notificationMessage.isGroupStoryReply,
let storyTimestamp = incomingMessage.storyTimestamp,
let storyAuthorAci = incomingMessage.storyAuthorAci
{
builder.storyTimestamp = storyTimestamp
builder.storyAuthorAci = storyAuthorAci
} else {
// We only use the thread's DM timer for normal messages & 1:1 story
// replies -- group story replies last for the lifetime of the story.
let dmConfigurationStore = DependenciesBridge.shared.disappearingMessagesConfigurationStore
let dmConfig = dmConfigurationStore.fetchOrBuildDefault(for: .thread(notificationMessage.thread), tx: transaction.asV2Read)
builder.expiresInSeconds = dmConfig.durationSeconds
builder.expireTimerVersion = NSNumber(value: dmConfig.timerVersion)
}

let outgoingMessage = TSOutgoingMessage(
outgoingMessageWith: builder,
additionalRecipients: [],
explicitRecipients: [],
skippedRecipients: [],
transaction: transaction
)

let unpreparedMessage = UnpreparedOutgoingMessage.forMessage(outgoingMessage, quotedReplyDraft: messageBeingRespondedTo)
do {
let preparedMessage = try unpreparedMessage.prepare(tx: transaction)
return ThreadUtil.enqueueMessagePromise(message: preparedMessage, transaction: transaction)
} catch {
return Promise(error: error)
}
}
}

private class func showThread(userInfo: [AnyHashable: Any]) throws -> Promise<Void> {
return firstly { () -> Promise<NotificationMessage> in
self.notificationMessage(forUserInfo: userInfo)
self.getNotificationMessage(forUserInfo: userInfo)
}.done(on: DispatchQueue.main) { notificationMessage in
if notificationMessage.isGroupStoryReply {
self.showGroupStoryReplyThread(notificationMessage: notificationMessage)
Expand Down Expand Up @@ -248,7 +287,7 @@ public class NotificationActionHandler {

private class func reactWithThumbsUp(userInfo: [AnyHashable: Any]) throws -> Promise<Void> {
return firstly { () -> Promise<NotificationMessage> in
self.notificationMessage(forUserInfo: userInfo)
self.getNotificationMessage(forUserInfo: userInfo)
}.then(on: DispatchQueue.global()) { (notificationMessage: NotificationMessage) -> Promise<Void> in
let thread = notificationMessage.thread
let interaction = notificationMessage.interaction
Expand Down Expand Up @@ -379,7 +418,7 @@ public class NotificationActionHandler {
let hasPendingMessageRequest: Bool
}

private class func notificationMessage(forUserInfo userInfo: [AnyHashable: Any]) -> Promise<NotificationMessage> {
private class func getNotificationMessage(forUserInfo userInfo: [AnyHashable: Any]) -> Promise<NotificationMessage> {
firstly(on: DispatchQueue.global()) { () throws -> NotificationMessage in
guard let threadId = userInfo[AppNotificationUserInfoKey.threadId] as? String else {
throw OWSAssertionError("threadId was unexpectedly nil")
Expand Down