Add default disappearing message timer
This commit is contained in:
parent
b12120afc5
commit
6783db210e
@ -845,6 +845,7 @@
|
||||
88D6E93F254CF712003142D9 /* group_call_leave.aiff in Resources */ = {isa = PBXBuildFile; fileRef = 88D6E93D254CF712003142D9 /* group_call_leave.aiff */; };
|
||||
88D6E940254CF712003142D9 /* group_call_join.aiff in Resources */ = {isa = PBXBuildFile; fileRef = 88D6E93E254CF712003142D9 /* group_call_join.aiff */; };
|
||||
88D6E94325535D49003142D9 /* CVTextTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88D6E94125535482003142D9 /* CVTextTest.swift */; };
|
||||
88DBDFB9263731C800C2101C /* DefaultDisappearingMessageTimerInteraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88DBDFB8263731C800C2101C /* DefaultDisappearingMessageTimerInteraction.swift */; };
|
||||
88E34F2722F269E900966CC2 /* StorageServiceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88E34F2622F269E900966CC2 /* StorageServiceManager.swift */; };
|
||||
88E34F2922F26CC100966CC2 /* StorageServiceProto+Sync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88E34F2822F26CC100966CC2 /* StorageServiceProto+Sync.swift */; };
|
||||
88E728FF25F0241100A2E4A4 /* AccountSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88E728FE25F0241100A2E4A4 /* AccountSettingsViewController.swift */; };
|
||||
@ -1980,6 +1981,7 @@
|
||||
88D6E93D254CF712003142D9 /* group_call_leave.aiff */ = {isa = PBXFileReference; lastKnownFileType = audio.aiff; path = group_call_leave.aiff; sourceTree = "<group>"; };
|
||||
88D6E93E254CF712003142D9 /* group_call_join.aiff */ = {isa = PBXFileReference; lastKnownFileType = audio.aiff; path = group_call_join.aiff; sourceTree = "<group>"; };
|
||||
88D6E94125535482003142D9 /* CVTextTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CVTextTest.swift; sourceTree = "<group>"; };
|
||||
88DBDFB8263731C800C2101C /* DefaultDisappearingMessageTimerInteraction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultDisappearingMessageTimerInteraction.swift; sourceTree = "<group>"; };
|
||||
88E34F2622F269E900966CC2 /* StorageServiceManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageServiceManager.swift; sourceTree = "<group>"; };
|
||||
88E34F2822F26CC100966CC2 /* StorageServiceProto+Sync.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StorageServiceProto+Sync.swift"; sourceTree = "<group>"; };
|
||||
88E728FE25F0241100A2E4A4 /* AccountSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountSettingsViewController.swift; sourceTree = "<group>"; };
|
||||
@ -2508,6 +2510,7 @@
|
||||
88D1D40322EF8A9700F472C5 /* ThreadDetailsInteraction.swift */,
|
||||
34B6A904218B4C90007C4606 /* TypingIndicatorInteraction.swift */,
|
||||
346CD5C126306578004162D6 /* UnknownThreadWarningInteraction.swift */,
|
||||
88DBDFB8263731C800C2101C /* DefaultDisappearingMessageTimerInteraction.swift */,
|
||||
4CFF115223A9C2130007F9D7 /* UnreadIndicatorInteraction.swift */,
|
||||
);
|
||||
path = ConversationView;
|
||||
@ -5465,6 +5468,7 @@
|
||||
346EAA14250199A400E8AB6F /* MemberRequestView.swift in Sources */,
|
||||
3426A37A2563F0EA0036407F /* CVComponentBottomButtons.swift in Sources */,
|
||||
32ECD08824BFBF8000EDDED0 /* HelpViewController.swift in Sources */,
|
||||
88DBDFB9263731C800C2101C /* DefaultDisappearingMessageTimerInteraction.swift in Sources */,
|
||||
3497972125DAA86100E99FA4 /* SendPaymentViewController.swift in Sources */,
|
||||
346B66311F4E29B200E5122F /* CropScaleImageViewController.swift in Sources */,
|
||||
45E5A6991F61E6DE001E4A8A /* MarqueeLabel.swift in Sources */,
|
||||
|
||||
@ -61,7 +61,7 @@ class PrivacySettingsViewController: OWSTableViewController2 {
|
||||
))
|
||||
}
|
||||
|
||||
if FeatureFlags.phoneNumberDiscoverability {
|
||||
if FeatureFlags.phoneNumberDiscoverability, tsAccountManager.isPrimaryDevice {
|
||||
whoCanSection.add(.disclosureItem(
|
||||
withText: NSLocalizedString(
|
||||
"SETTINGS_PHONE_NUMBER_DISCOVERABILITY",
|
||||
@ -108,6 +108,49 @@ class PrivacySettingsViewController: OWSTableViewController2 {
|
||||
))
|
||||
contents.addSection(messagingSection)
|
||||
|
||||
let disappearingMessagesSection = OWSTableSection()
|
||||
disappearingMessagesSection.headerTitle = NSLocalizedString(
|
||||
"SETTINGS_DISAPPEARING_MESSAGES",
|
||||
comment: "Label for the 'disappearig messages' privacy settings."
|
||||
)
|
||||
disappearingMessagesSection.footerTitle = NSLocalizedString(
|
||||
"SETTINGS_DISAPPEARING_MESSAGES_FOOTER",
|
||||
comment: "Explanation for the 'disappearig messages' privacy settings."
|
||||
)
|
||||
let disappearingMessagesConfiguration = databaseStorage.read { transaction in
|
||||
OWSDisappearingMessagesConfiguration.fetchOrBuildDefaultUniversalConfiguration(with: transaction)
|
||||
}
|
||||
disappearingMessagesSection.add(.init(
|
||||
customCellBlock: { [weak self] in
|
||||
guard let self = self else { return UITableViewCell() }
|
||||
let cell = OWSTableItem.buildIconNameCell(
|
||||
icon: disappearingMessagesConfiguration.isEnabled
|
||||
? .settingsTimer
|
||||
: .settingsTimerDisabled,
|
||||
itemName: NSLocalizedString(
|
||||
"SETTINGS_DEFAULT_DISAPPEARING_MESSAGES",
|
||||
comment: "table cell label in conversation settings"
|
||||
),
|
||||
accessoryText: disappearingMessagesConfiguration.isEnabled
|
||||
? NSString.formatDurationSeconds(disappearingMessagesConfiguration.durationSeconds, useShortFormat: true)
|
||||
: CommonStrings.switchOff,
|
||||
accessoryType: .disclosureIndicator,
|
||||
accessoryImage: nil,
|
||||
accessibilityIdentifier: UIView.accessibilityIdentifier(in: self, name: "disappearing_messages")
|
||||
)
|
||||
return cell
|
||||
}, actionBlock: { [weak self] in
|
||||
let vc = DisappearingMessagesTimerSettingsViewController(configuration: disappearingMessagesConfiguration) { configuration in
|
||||
self?.databaseStorage.write { transaction in
|
||||
configuration.anyUpsert(transaction: transaction)
|
||||
}
|
||||
self?.updateTableContents()
|
||||
}
|
||||
self?.presentFormSheet(OWSNavigationController(rootViewController: vc), animated: true)
|
||||
}
|
||||
))
|
||||
contents.addSection(disappearingMessagesSection)
|
||||
|
||||
let appSecuritySection = OWSTableSection()
|
||||
appSecuritySection.headerTitle = NSLocalizedString("SETTINGS_SECURITY_TITLE", comment: "Section header")
|
||||
|
||||
|
||||
@ -302,6 +302,7 @@ public enum CVComponentKey: CustomStringConvertible, CaseIterable {
|
||||
case threadDetails
|
||||
case failedOrPendingDownloads
|
||||
case unknownThreadWarning
|
||||
case defaultDisappearingMessageTimer
|
||||
|
||||
public var description: String {
|
||||
switch self {
|
||||
@ -349,6 +350,8 @@ public enum CVComponentKey: CustomStringConvertible, CaseIterable {
|
||||
return ".failedOrPendingDownloads"
|
||||
case .sendFailureBadge:
|
||||
return ".sendFailureBadge"
|
||||
case .defaultDisappearingMessageTimer:
|
||||
return ".defaultDisappearingMessageTimer"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -210,6 +210,9 @@ public class CVComponentState: Equatable, Dependencies {
|
||||
typealias UnknownThreadWarning = CVComponentState.SystemMessage
|
||||
let unknownThreadWarning: UnknownThreadWarning?
|
||||
|
||||
typealias DefaultDisappearingMessageTimer = CVComponentState.SystemMessage
|
||||
let defaultDisappearingMessageTimer: DefaultDisappearingMessageTimer?
|
||||
|
||||
struct BottomButtons: Equatable {
|
||||
let actions: [CVMessageAction]
|
||||
}
|
||||
@ -243,6 +246,7 @@ public class CVComponentState: Equatable, Dependencies {
|
||||
typingIndicator: TypingIndicator?,
|
||||
threadDetails: ThreadDetails?,
|
||||
unknownThreadWarning: UnknownThreadWarning?,
|
||||
defaultDisappearingMessageTimer: DefaultDisappearingMessageTimer?,
|
||||
bottomButtons: BottomButtons?,
|
||||
failedOrPendingDownloads: FailedOrPendingDownloads?,
|
||||
sendFailureBadge: SendFailureBadge?) {
|
||||
@ -266,6 +270,7 @@ public class CVComponentState: Equatable, Dependencies {
|
||||
self.typingIndicator = typingIndicator
|
||||
self.threadDetails = threadDetails
|
||||
self.unknownThreadWarning = unknownThreadWarning
|
||||
self.defaultDisappearingMessageTimer = defaultDisappearingMessageTimer
|
||||
self.bottomButtons = bottomButtons
|
||||
self.failedOrPendingDownloads = failedOrPendingDownloads
|
||||
self.sendFailureBadge = sendFailureBadge
|
||||
@ -293,6 +298,7 @@ public class CVComponentState: Equatable, Dependencies {
|
||||
lhs.typingIndicator == rhs.typingIndicator &&
|
||||
lhs.threadDetails == rhs.threadDetails &&
|
||||
lhs.unknownThreadWarning == rhs.unknownThreadWarning &&
|
||||
lhs.defaultDisappearingMessageTimer == rhs.defaultDisappearingMessageTimer &&
|
||||
lhs.bottomButtons == rhs.bottomButtons &&
|
||||
lhs.failedOrPendingDownloads == rhs.failedOrPendingDownloads &&
|
||||
lhs.sendFailureBadge == rhs.sendFailureBadge)
|
||||
@ -318,6 +324,7 @@ public class CVComponentState: Equatable, Dependencies {
|
||||
typealias TypingIndicator = CVComponentState.TypingIndicator
|
||||
typealias ThreadDetails = CVComponentState.ThreadDetails
|
||||
typealias UnknownThreadWarning = CVComponentState.UnknownThreadWarning
|
||||
typealias DefaultDisappearingMessageTimer = CVComponentState.DefaultDisappearingMessageTimer
|
||||
typealias FailedOrPendingDownloads = CVComponentState.FailedOrPendingDownloads
|
||||
typealias BottomButtons = CVComponentState.BottomButtons
|
||||
typealias SendFailureBadge = CVComponentState.SendFailureBadge
|
||||
@ -342,6 +349,7 @@ public class CVComponentState: Equatable, Dependencies {
|
||||
var typingIndicator: TypingIndicator?
|
||||
var threadDetails: ThreadDetails?
|
||||
var unknownThreadWarning: UnknownThreadWarning?
|
||||
var defaultDisappearingMessageTimer: DefaultDisappearingMessageTimer?
|
||||
var reactions: Reactions?
|
||||
var failedOrPendingDownloads: FailedOrPendingDownloads?
|
||||
var sendFailureBadge: SendFailureBadge?
|
||||
@ -378,6 +386,7 @@ public class CVComponentState: Equatable, Dependencies {
|
||||
typingIndicator: typingIndicator,
|
||||
threadDetails: threadDetails,
|
||||
unknownThreadWarning: unknownThreadWarning,
|
||||
defaultDisappearingMessageTimer: defaultDisappearingMessageTimer,
|
||||
bottomButtons: bottomButtons,
|
||||
failedOrPendingDownloads: failedOrPendingDownloads,
|
||||
sendFailureBadge: sendFailureBadge)
|
||||
@ -409,6 +418,9 @@ public class CVComponentState: Equatable, Dependencies {
|
||||
if unknownThreadWarning != nil {
|
||||
return .unknownThreadWarning
|
||||
}
|
||||
if defaultDisappearingMessageTimer != nil {
|
||||
return .defaultDisappearingMessageTimer
|
||||
}
|
||||
if systemMessage != nil {
|
||||
return .systemMessage
|
||||
}
|
||||
@ -504,6 +516,9 @@ public class CVComponentState: Equatable, Dependencies {
|
||||
if unknownThreadWarning != nil {
|
||||
result.insert(.unknownThreadWarning)
|
||||
}
|
||||
if defaultDisappearingMessageTimer != nil {
|
||||
result.insert(.defaultDisappearingMessageTimer)
|
||||
}
|
||||
if bottomButtons != nil {
|
||||
result.insert(.bottomButtons)
|
||||
}
|
||||
@ -611,6 +626,13 @@ fileprivate extension CVComponentState.Builder {
|
||||
threadViewModel: threadViewModel,
|
||||
transaction: transaction)
|
||||
return build()
|
||||
case .defaultDisappearingMessageTimer:
|
||||
self.unknownThreadWarning = CVComponentSystemMessage.buildDefaultDisappearingMessageTimerState(
|
||||
interaction: interaction,
|
||||
threadViewModel: threadViewModel,
|
||||
transaction: transaction
|
||||
)
|
||||
return build()
|
||||
case .typingIndicator:
|
||||
guard let typingIndicatorInteraction = interaction as? TypingIndicatorInteraction else {
|
||||
owsFailDebug("Invalid typingIndicator.")
|
||||
|
||||
@ -120,7 +120,7 @@ public class CVComponentMessage: CVComponentBase, CVRootComponent {
|
||||
// We don't render sender avatars with a subcomponent.
|
||||
case .senderAvatar:
|
||||
return nil
|
||||
case .systemMessage, .dateHeader, .unreadIndicator, .typingIndicator, .threadDetails, .failedOrPendingDownloads, .sendFailureBadge, .unknownThreadWarning:
|
||||
case .systemMessage, .dateHeader, .unreadIndicator, .typingIndicator, .threadDetails, .failedOrPendingDownloads, .sendFailureBadge, .unknownThreadWarning, .defaultDisappearingMessageTimer:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -1239,7 +1239,7 @@ public class CVComponentMessage: CVComponentBase, CVRootComponent {
|
||||
case .senderAvatar:
|
||||
owsFailDebug("Invalid component key: \(key)")
|
||||
return nil
|
||||
case .systemMessage, .dateHeader, .unreadIndicator, .typingIndicator, .threadDetails, .failedOrPendingDownloads, .sendFailureBadge, .unknownThreadWarning:
|
||||
case .systemMessage, .dateHeader, .unreadIndicator, .typingIndicator, .threadDetails, .failedOrPendingDownloads, .sendFailureBadge, .unknownThreadWarning, .defaultDisappearingMessageTimer:
|
||||
owsFailDebug("Invalid component key: \(key)")
|
||||
return nil
|
||||
}
|
||||
@ -1277,7 +1277,7 @@ public class CVComponentMessage: CVComponentBase, CVRootComponent {
|
||||
// We don't render sender avatars with a subcomponent.
|
||||
case .senderAvatar:
|
||||
owsAssertDebug(subcomponentView == nil)
|
||||
case .systemMessage, .dateHeader, .unreadIndicator, .typingIndicator, .threadDetails, .failedOrPendingDownloads, .sendFailureBadge, .unknownThreadWarning:
|
||||
case .systemMessage, .dateHeader, .unreadIndicator, .typingIndicator, .threadDetails, .failedOrPendingDownloads, .sendFailureBadge, .unknownThreadWarning, .defaultDisappearingMessageTimer:
|
||||
owsAssertDebug(subcomponentView == nil)
|
||||
}
|
||||
}
|
||||
|
||||
@ -782,6 +782,36 @@ extension CVComponentSystemMessage {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Default Disappearing Message Timer
|
||||
|
||||
static func buildDefaultDisappearingMessageTimerState(
|
||||
interaction: TSInteraction,
|
||||
threadViewModel: ThreadViewModel,
|
||||
transaction: SDSAnyReadTransaction
|
||||
) -> CVComponentState.SystemMessage {
|
||||
let configuration = OWSDisappearingMessagesConfiguration.fetchOrBuildDefaultUniversalConfiguration(with: transaction)
|
||||
|
||||
let labelText = NSMutableAttributedString()
|
||||
labelText.appendImage(
|
||||
Theme.iconImage(.timer16).withRenderingMode(.alwaysTemplate),
|
||||
font: Self.titleLabelFont,
|
||||
heightReference: ImageAttachmentHeightReference.lineHeight
|
||||
)
|
||||
labelText.append(" ", attributes: [:])
|
||||
|
||||
let titleFormat = NSLocalizedString(
|
||||
"SYSTEM_MESSAGE_DEFAULT_DISAPPEARING_MESSAGE_TIMER_FORMAT",
|
||||
comment: "Indicator that the default disappearing message timer will be applied when you send a message. Embeds {default disappearing message time}"
|
||||
)
|
||||
labelText.append(String(format: titleFormat, configuration.durationString))
|
||||
|
||||
return CVComponentState.SystemMessage(
|
||||
title: labelText,
|
||||
titleColor: Theme.secondaryTextAndIconColor,
|
||||
action: nil
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
static func action(forInteraction interaction: TSInteraction,
|
||||
|
||||
@ -214,7 +214,7 @@ extension CVItemViewModelImpl {
|
||||
}
|
||||
|
||||
switch messageCellType {
|
||||
case .unknown, .dateHeader, .typingIndicator, .unreadIndicator, .threadDetails, .systemMessage, .unknownThreadWarning:
|
||||
case .unknown, .dateHeader, .typingIndicator, .unreadIndicator, .threadDetails, .systemMessage, .unknownThreadWarning, .defaultDisappearingMessageTimer:
|
||||
return false
|
||||
case .textOnlyMessage, .audio, .genericAttachment, .contactShare, .bodyMedia, .viewOnce, .stickerMessage:
|
||||
return !hasUnloadedAttachments
|
||||
|
||||
@ -91,7 +91,7 @@ struct CVItemModelBuilder: CVItemBuilding, Dependencies {
|
||||
// The thread details should have a stable timestamp.
|
||||
let threadDetailsTimestamp: UInt64
|
||||
if let firstInteraction = messageMapping.loadedInteractions.first {
|
||||
threadDetailsTimestamp = max(1, firstInteraction.timestamp) - 2
|
||||
threadDetailsTimestamp = max(1, firstInteraction.timestamp) - 3
|
||||
} else {
|
||||
threadDetailsTimestamp = 1
|
||||
}
|
||||
@ -109,7 +109,7 @@ struct CVItemModelBuilder: CVItemBuilding, Dependencies {
|
||||
// The "Unknown Thread Warning" should have a stable timestamp.
|
||||
let timestamp: UInt64
|
||||
if let firstInteraction = messageMapping.loadedInteractions.first {
|
||||
timestamp = max(1, firstInteraction.timestamp) - 1
|
||||
timestamp = max(1, firstInteraction.timestamp) - 2
|
||||
} else {
|
||||
timestamp = 2
|
||||
}
|
||||
@ -119,6 +119,25 @@ struct CVItemModelBuilder: CVItemBuilding, Dependencies {
|
||||
owsAssertDebug(item != nil)
|
||||
}
|
||||
|
||||
if messageMapping.shouldShowDefaultTimerIndicator(
|
||||
thread: thread,
|
||||
transaction: transaction
|
||||
) {
|
||||
// The default timer message should have a stable timestamp.
|
||||
let timestamp: UInt64
|
||||
if let firstInteraction = messageMapping.loadedInteractions.first {
|
||||
timestamp = max(1, firstInteraction.timestamp) - 1
|
||||
} else {
|
||||
timestamp = 3
|
||||
}
|
||||
let interaction = DefaultDisappearingMessageTimerInteraction(
|
||||
thread: thread,
|
||||
timestamp: timestamp
|
||||
)
|
||||
let item = addItem(interaction: interaction)
|
||||
owsAssertDebug(item != nil)
|
||||
}
|
||||
|
||||
var interactionIds = Set<String>()
|
||||
for interaction in messageMapping.loadedInteractions {
|
||||
guard !interactionIds.contains(interaction.uniqueId) else {
|
||||
@ -623,6 +642,11 @@ fileprivate extension CVMessageMapping {
|
||||
!canLoadOlder && Self.contactsManagerImpl.shouldShowUnknownThreadWarning(thread: thread,
|
||||
transaction: transaction)
|
||||
}
|
||||
func shouldShowDefaultTimerIndicator(thread: TSThread, transaction: SDSAnyReadTransaction) -> Bool {
|
||||
guard !thread.shouldThreadBeVisible else { return false }
|
||||
guard thread.lastInteractionRowId == 0 else { return false }
|
||||
return OWSDisappearingMessagesConfiguration.fetchOrBuildDefaultUniversalConfiguration(with: transaction).isEnabled
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
@ -655,7 +679,7 @@ private class ItemBuilder {
|
||||
|
||||
var canShowDate: Bool {
|
||||
switch interaction.interactionType() {
|
||||
case .unknown, .typingIndicator, .threadDetails, .dateHeader, .unknownThreadWarning:
|
||||
case .unknown, .typingIndicator, .threadDetails, .dateHeader, .unknownThreadWarning, .defaultDisappearingMessageTimer:
|
||||
return false
|
||||
case .info:
|
||||
guard let infoMessage = interaction as? TSInfoMessage else {
|
||||
|
||||
@ -24,6 +24,7 @@ public enum CVMessageCellType: Int, CustomStringConvertible, Equatable {
|
||||
case threadDetails
|
||||
case systemMessage
|
||||
case unknownThreadWarning
|
||||
case defaultDisappearingMessageTimer
|
||||
|
||||
// MARK: - CustomStringConvertible
|
||||
|
||||
@ -44,6 +45,7 @@ public enum CVMessageCellType: Int, CustomStringConvertible, Equatable {
|
||||
case .threadDetails: return "threadDetails"
|
||||
case .systemMessage: return "systemMessage"
|
||||
case .unknownThreadWarning: return "unknownThreadWarning"
|
||||
case .defaultDisappearingMessageTimer: return "defaultDisappearingMessageTimer"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -631,7 +631,7 @@ extension ConversationViewController {
|
||||
switch interaction.interactionType() {
|
||||
case .unknown, .unreadIndicator, .dateHeader, .typingIndicator:
|
||||
return false
|
||||
case .incomingMessage, .outgoingMessage, .error, .call, .info, .threadDetails, .unknownThreadWarning:
|
||||
case .incomingMessage, .outgoingMessage, .error, .call, .info, .threadDetails, .unknownThreadWarning, .defaultDisappearingMessageTimer:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@ -1370,7 +1370,7 @@ typedef enum : NSUInteger {
|
||||
[self removeGroupCallTooltip];
|
||||
|
||||
// We initiated a call, so if there was a pending message request we should accept it.
|
||||
[ThreadUtil addThreadToProfileWhitelistIfEmptyOrPendingRequestWithSneakyTransaction:self.thread];
|
||||
[ThreadUtil addThreadToProfileWhitelistIfEmptyOrPendingRequestAndSetDefaultTimerWithSneakyTransaction:self.thread];
|
||||
|
||||
[GroupCallViewController presentLobbyForThread:(TSGroupThread *)self.thread];
|
||||
}
|
||||
@ -1420,7 +1420,7 @@ typedef enum : NSUInteger {
|
||||
}
|
||||
|
||||
// We initiated a call, so if there was a pending message request we should accept it.
|
||||
[ThreadUtil addThreadToProfileWhitelistIfEmptyOrPendingRequestWithSneakyTransaction:self.thread];
|
||||
[ThreadUtil addThreadToProfileWhitelistIfEmptyOrPendingRequestAndSetDefaultTimerWithSneakyTransaction:self.thread];
|
||||
|
||||
[self.outboundIndividualCallInitiator initiateCallWithAddress:contactThread.contactAddress isVideo:isVideo];
|
||||
}
|
||||
@ -2370,8 +2370,9 @@ typedef enum : NSUInteger {
|
||||
__block BOOL didAddToProfileWhitelist;
|
||||
TSThread *thread = self.thread;
|
||||
DatabaseStorageAsyncWrite(SDSDatabaseStorage.shared, ^(SDSAnyWriteTransaction *transaction) {
|
||||
didAddToProfileWhitelist = [ThreadUtil addThreadToProfileWhitelistIfEmptyOrPendingRequest:thread
|
||||
transaction:transaction];
|
||||
didAddToProfileWhitelist =
|
||||
[ThreadUtil addThreadToProfileWhitelistIfEmptyOrPendingRequestAndSetDefaultTimer:thread
|
||||
transaction:transaction];
|
||||
|
||||
// TODO - in line with QuotedReply and other message attachments, saving should happen as part of sending
|
||||
// preparation rather than duplicated here and in the SAE
|
||||
@ -2777,7 +2778,8 @@ typedef enum : NSUInteger {
|
||||
|
||||
DatabaseStorageWrite(self.databaseStorage, ^(SDSAnyWriteTransaction *transaction) {
|
||||
// We updated the group, so if there was a pending message request we should accept it.
|
||||
[ThreadUtil addThreadToProfileWhitelistIfEmptyOrPendingRequest:self.thread transaction:transaction];
|
||||
[ThreadUtil addThreadToProfileWhitelistIfEmptyOrPendingRequestAndSetDefaultTimer:self.thread
|
||||
transaction:transaction];
|
||||
});
|
||||
}
|
||||
|
||||
@ -2915,8 +2917,8 @@ typedef enum : NSUInteger {
|
||||
}
|
||||
}
|
||||
|
||||
BOOL didAddToProfileWhitelist =
|
||||
[ThreadUtil addThreadToProfileWhitelistIfEmptyOrPendingRequestWithSneakyTransaction:self.thread];
|
||||
BOOL didAddToProfileWhitelist = [ThreadUtil
|
||||
addThreadToProfileWhitelistIfEmptyOrPendingRequestAndSetDefaultTimerWithSneakyTransaction:self.thread];
|
||||
|
||||
__block TSOutgoingMessage *message;
|
||||
[self.databaseStorage uiReadWithBlock:^(SDSAnyReadTransaction *_Nonnull transaction) {
|
||||
@ -3436,8 +3438,8 @@ typedef enum : NSUInteger {
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL didAddToProfileWhitelist =
|
||||
[ThreadUtil addThreadToProfileWhitelistIfEmptyOrPendingRequestWithSneakyTransaction:self.thread];
|
||||
BOOL didAddToProfileWhitelist = [ThreadUtil
|
||||
addThreadToProfileWhitelistIfEmptyOrPendingRequestAndSetDefaultTimerWithSneakyTransaction:self.thread];
|
||||
__block TSOutgoingMessage *message;
|
||||
|
||||
[self.databaseStorage uiReadWithBlock:^(SDSAnyReadTransaction *transaction) {
|
||||
|
||||
@ -0,0 +1,51 @@
|
||||
//
|
||||
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc(OWSDefaultDisappearingMessageTimerInteraction)
|
||||
public class DefaultDisappearingMessageTimerInteraction: TSInteraction {
|
||||
|
||||
@objc
|
||||
public override func isDynamicInteraction() -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
@objc
|
||||
public override func interactionType() -> OWSInteractionType {
|
||||
return .defaultDisappearingMessageTimer
|
||||
}
|
||||
|
||||
@available(*, unavailable, message: "use other constructor instead.")
|
||||
@objc
|
||||
public required init(coder aDecoder: NSCoder) {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
@available(*, unavailable, message: "use other constructor instead.")
|
||||
@objc
|
||||
public required init(dictionary dictionaryValue: [String: Any]!) throws {
|
||||
notImplemented()
|
||||
}
|
||||
|
||||
@objc
|
||||
public init(thread: TSThread, timestamp: UInt64) {
|
||||
// Include timestamp in uniqueId to ensure invariant that
|
||||
// interactions don't move in the chat history ordering.
|
||||
super.init(
|
||||
uniqueId: "DefaultDisappearingMessageTimer_\(timestamp)",
|
||||
timestamp: timestamp,
|
||||
thread: thread
|
||||
)
|
||||
}
|
||||
|
||||
public override var shouldBeSaved: Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@objc
|
||||
public override func anyWillInsert(with transaction: SDSAnyWriteTransaction) {
|
||||
owsFailDebug("The transient interaction should not be saved in the database.")
|
||||
}
|
||||
}
|
||||
@ -103,6 +103,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
groupId:nil
|
||||
name:groupName
|
||||
avatarData:nil
|
||||
disappearingMessageToken:DisappearingMessageToken.disabledToken
|
||||
newGroupSeed:nil
|
||||
shouldSendMessage:YES
|
||||
success:^(TSGroupThread *thread) { [SignalApp.shared presentConversationForThread:thread animated:YES]; }
|
||||
|
||||
@ -589,6 +589,7 @@ class DebugUIGroupsV2: DebugUIPage {
|
||||
return firstly {
|
||||
GroupManager.localCreateNewGroup(members: [otherUserAddress],
|
||||
name: "Real group, both users are in the group",
|
||||
disappearingMessageToken: .disabledToken,
|
||||
shouldSendMessage: false)
|
||||
}.map(on: .global()) { (groupThread: TSGroupThread) in
|
||||
guard let validGroupModelV2 = groupThread.groupModel as? TSGroupModelV2 else {
|
||||
@ -602,6 +603,7 @@ class DebugUIGroupsV2: DebugUIPage {
|
||||
return firstly {
|
||||
GroupManager.localCreateNewGroup(members: [],
|
||||
name: "Real group, recipient is not in the group",
|
||||
disappearingMessageToken: .disabledToken,
|
||||
shouldSendMessage: false)
|
||||
}.map(on: .global()) { (groupThread: TSGroupThread) in
|
||||
guard let missingOtherUserGroupModelV2 = groupThread.groupModel as? TSGroupModelV2 else {
|
||||
@ -617,6 +619,7 @@ class DebugUIGroupsV2: DebugUIPage {
|
||||
return firstly { () -> Promise<TSGroupThread> in
|
||||
GroupManager.localCreateNewGroup(members: [otherUserAddress],
|
||||
name: "Real group, sender is not in the group",
|
||||
disappearingMessageToken: .disabledToken,
|
||||
shouldSendMessage: false)
|
||||
}.then(on: .global()) { (groupThread: TSGroupThread) -> Promise<TSGroupThread> in
|
||||
guard let groupModel = groupThread.groupModel as? TSGroupModelV2 else {
|
||||
|
||||
@ -64,7 +64,7 @@ public extension DebugUIMessages {
|
||||
let members = uuidMembers + [TSAccountManager.localAddress!]
|
||||
let groupName = "UUID Group"
|
||||
|
||||
_ = GroupManager.localCreateNewGroup(members: members, name: groupName, shouldSendMessage: true)
|
||||
_ = GroupManager.localCreateNewGroup(members: members, name: groupName, disappearingMessageToken: .disabledToken, shouldSendMessage: true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4362,14 +4362,11 @@ typedef OWSContact * (^OWSContactBlock)(SDSAnyWriteTransaction *transaction);
|
||||
groupId:nil
|
||||
name:groupName
|
||||
avatarData:nil
|
||||
disappearingMessageToken:DisappearingMessageToken.disabledToken
|
||||
newGroupSeed:nil
|
||||
shouldSendMessage:YES
|
||||
success:^(TSGroupThread *thread) {
|
||||
success(thread);
|
||||
}
|
||||
failure:^(NSError *error) {
|
||||
OWSFailDebug(@"Error: %@", error);
|
||||
}];
|
||||
success:^(TSGroupThread *thread) { success(thread); }
|
||||
failure:^(NSError *error) { OWSFailDebug(@"Error: %@", error); }];
|
||||
}
|
||||
|
||||
+ (void)testIndicScriptsInThread:(TSThread *)thread
|
||||
|
||||
@ -575,6 +575,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
groupId:nil
|
||||
name:NSUUID.UUID.UUIDString
|
||||
avatarData:nil
|
||||
disappearingMessageToken:DisappearingMessageToken.disabledToken
|
||||
newGroupSeed:nil
|
||||
shouldSendMessage:NO
|
||||
success:^(TSGroupThread *thread) { [SignalApp.shared presentConversationForThread:thread animated:YES]; }
|
||||
|
||||
@ -26,6 +26,7 @@ public extension DebugUIStress {
|
||||
groupId: nil,
|
||||
name: groupName,
|
||||
avatarData: oldGroupThread.groupModel.groupAvatarData,
|
||||
disappearingMessageToken: .disabledToken,
|
||||
newGroupSeed: nil,
|
||||
shouldSendMessage: false)
|
||||
}.done { newGroupThread in
|
||||
@ -103,6 +104,7 @@ public extension DebugUIStress {
|
||||
groupId: nil,
|
||||
name: groupName,
|
||||
avatarData: oldGroupThread.groupModel.groupAvatarData,
|
||||
disappearingMessageToken: .disabledToken,
|
||||
newGroupSeed: nil,
|
||||
shouldSendMessage: false)
|
||||
}.done { (newGroupThread) in
|
||||
|
||||
@ -142,7 +142,7 @@ extension ForwardMessageNavigationController {
|
||||
|
||||
pushViewController(approvalViewController, animated: true)
|
||||
case .unknown, .viewOnce, .dateHeader, .unreadIndicator, .typingIndicator,
|
||||
.threadDetails, .systemMessage, .unknownThreadWarning:
|
||||
.threadDetails, .systemMessage, .unknownThreadWarning, .defaultDisappearingMessageTimer:
|
||||
throw OWSAssertionError("Invalid message type.")
|
||||
}
|
||||
}
|
||||
@ -257,7 +257,7 @@ extension ForwardMessageNavigationController {
|
||||
}
|
||||
// }
|
||||
case .unknown, .viewOnce, .dateHeader, .unreadIndicator, .typingIndicator,
|
||||
.threadDetails, .systemMessage, .unknownThreadWarning:
|
||||
.threadDetails, .systemMessage, .unknownThreadWarning, .defaultDisappearingMessageTimer:
|
||||
throw OWSAssertionError("Invalid message type.")
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,6 +27,10 @@ public class NewGroupConfirmViewController: OWSViewController {
|
||||
|
||||
private let recipientTableView = OWSTableViewController2()
|
||||
|
||||
private lazy var disappearingMessagesConfiguration = databaseStorage.read { transaction in
|
||||
OWSDisappearingMessagesConfiguration.fetchOrBuildDefaultUniversalConfiguration(with: transaction)
|
||||
}
|
||||
|
||||
required init(newGroupState: NewGroupState) {
|
||||
self.newGroupState = newGroupState
|
||||
|
||||
@ -203,6 +207,39 @@ public class NewGroupConfirmViewController: OWSViewController {
|
||||
// MARK: -
|
||||
|
||||
private func updateTableContents() {
|
||||
let contents = OWSTableContents()
|
||||
|
||||
let disappearingMessagesSection = OWSTableSection()
|
||||
disappearingMessagesSection.add(.init(
|
||||
customCellBlock: { [weak self] in
|
||||
guard let self = self else { return UITableViewCell() }
|
||||
let cell = OWSTableItem.buildIconNameCell(
|
||||
icon: self.disappearingMessagesConfiguration.isEnabled
|
||||
? .settingsTimer
|
||||
: .settingsTimerDisabled,
|
||||
itemName: NSLocalizedString(
|
||||
"DISAPPEARING_MESSAGES",
|
||||
comment: "table cell label in conversation settings"
|
||||
),
|
||||
accessoryText: self.disappearingMessagesConfiguration.isEnabled
|
||||
? NSString.formatDurationSeconds(self.disappearingMessagesConfiguration.durationSeconds, useShortFormat: true)
|
||||
: CommonStrings.switchOff,
|
||||
accessoryType: .disclosureIndicator,
|
||||
accessoryImage: nil,
|
||||
accessibilityIdentifier: UIView.accessibilityIdentifier(in: self, name: "disappearing_messages")
|
||||
)
|
||||
return cell
|
||||
}, actionBlock: { [weak self] in
|
||||
guard let self = self else { return }
|
||||
let vc = DisappearingMessagesTimerSettingsViewController(configuration: self.disappearingMessagesConfiguration) { configuration in
|
||||
self.disappearingMessagesConfiguration = configuration
|
||||
self.updateTableContents()
|
||||
}
|
||||
self.presentFormSheet(OWSNavigationController(rootViewController: vc), animated: true)
|
||||
}
|
||||
))
|
||||
contents.addSection(disappearingMessagesSection)
|
||||
|
||||
let section = OWSTableSection()
|
||||
section.headerTitle = NSLocalizedString("GROUP_MEMBERS_SECTION_TITLE_MEMBERS",
|
||||
comment: "Title for the 'members' section of the 'group members' view.")
|
||||
@ -251,9 +288,8 @@ public class NewGroupConfirmViewController: OWSViewController {
|
||||
return cell
|
||||
}, actionBlock: nil))
|
||||
}
|
||||
|
||||
let contents = OWSTableContents()
|
||||
contents.addSection(section)
|
||||
|
||||
recipientTableView.contents = contents
|
||||
}
|
||||
|
||||
@ -284,6 +320,7 @@ public class NewGroupConfirmViewController: OWSViewController {
|
||||
let memberSet = Set([localAddress] + recipientSet.orderedMembers.compactMap { $0.address })
|
||||
let members = Array(memberSet)
|
||||
let newGroupSeed = groupSeed
|
||||
let disappearingMessageToken = disappearingMessagesConfiguration.asToken
|
||||
|
||||
// GroupsV2 TODO: Should we allow cancel here?
|
||||
ModalActivityIndicatorViewController.present(fromViewController: self,
|
||||
@ -293,6 +330,7 @@ public class NewGroupConfirmViewController: OWSViewController {
|
||||
groupId: nil,
|
||||
name: groupName,
|
||||
avatarData: avatarData,
|
||||
disappearingMessageToken: disappearingMessageToken,
|
||||
newGroupSeed: newGroupSeed,
|
||||
shouldSendMessage: true)
|
||||
}.done { groupThread in
|
||||
|
||||
@ -40,6 +40,7 @@ class GroupAttributesEditorHelper: NSObject {
|
||||
private let iconViewSize: UInt
|
||||
|
||||
private let cameraButton = GroupAttributesEditorHelper.buildCameraButtonForCenter()
|
||||
private let cameraCornerButton = GroupAttributesEditorHelper.buildCameraButtonForCorner()
|
||||
|
||||
let nameTextField = UITextField()
|
||||
|
||||
@ -101,6 +102,10 @@ class GroupAttributesEditorHelper: NSObject {
|
||||
avatarWrapper.addSubview(cameraButton)
|
||||
cameraButton.autoCenterInSuperview()
|
||||
|
||||
avatarWrapper.addSubview(cameraCornerButton)
|
||||
cameraCornerButton.autoPinEdge(toSuperviewEdge: .trailing)
|
||||
cameraCornerButton.autoPinEdge(toSuperviewEdge: .bottom)
|
||||
|
||||
nameTextField.text = groupNameOriginal
|
||||
nameTextField.font = .ows_dynamicTypeBody
|
||||
nameTextField.backgroundColor = .clear
|
||||
@ -143,7 +148,7 @@ class GroupAttributesEditorHelper: NSObject {
|
||||
|
||||
public static func buildCameraButtonForCenter() -> UIView {
|
||||
let cameraImageView = UIImageView()
|
||||
cameraImageView.setTemplateImageName("camera-outline-24", tintColor: Theme.accentBlueColor)
|
||||
cameraImageView.setTemplateImageName("camera-outline-24", tintColor: Theme.primaryIconColor)
|
||||
let iconSize: CGFloat = 32
|
||||
cameraImageView.autoSetDimensions(to: CGSize(square: iconSize))
|
||||
return cameraImageView
|
||||
@ -152,12 +157,16 @@ class GroupAttributesEditorHelper: NSObject {
|
||||
private func updateAvatarView(groupAvatar: GroupAvatar?) {
|
||||
if let groupAvatar = groupAvatar {
|
||||
avatarImageView.image = groupAvatar.image
|
||||
avatarImageView.backgroundColor = nil
|
||||
avatarImageView.layer.borderWidth = 0
|
||||
avatarImageView.layer.borderColor = nil
|
||||
cameraButton.isHidden = true
|
||||
cameraCornerButton.isHidden = false
|
||||
} else {
|
||||
avatarImageView.image = nil
|
||||
avatarImageView.backgroundColor = Theme.washColor
|
||||
avatarImageView.layer.borderWidth = 2
|
||||
avatarImageView.layer.borderColor = Theme.outlineColor.cgColor
|
||||
cameraButton.isHidden = false
|
||||
cameraCornerButton.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -71,11 +71,6 @@ class GroupAttributesViewController: OWSTableViewController2 {
|
||||
helper.delegate = self
|
||||
helper.buildContents(avatarViewHelperDelegate: self)
|
||||
|
||||
let cameraButton = GroupAttributesEditorHelper.buildCameraButtonForCorner()
|
||||
helper.avatarWrapper.addSubview(cameraButton)
|
||||
cameraButton.autoPinEdge(toSuperviewEdge: .trailing)
|
||||
cameraButton.autoPinEdge(toSuperviewEdge: .bottom)
|
||||
|
||||
updateTableContents()
|
||||
}
|
||||
|
||||
|
||||
@ -4708,6 +4708,9 @@
|
||||
/* The title for the data settings. */
|
||||
"SETTINGS_DATA_TITLE" = "Data Usage";
|
||||
|
||||
/* table cell label in conversation settings */
|
||||
"SETTINGS_DEFAULT_DISAPPEARING_MESSAGES" = "Default Timer for New Chats";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"SETTINGS_DELETE_ACCOUNT_BUTTON" = "Delete Account";
|
||||
|
||||
@ -4732,6 +4735,12 @@
|
||||
/* Confirmation text for button which deletes all message, calling, attachments, etc. */
|
||||
"SETTINGS_DELETE_HISTORYLOG_CONFIRMATION_BUTTON" = "Delete Everything";
|
||||
|
||||
/* Label for the 'disappearig messages' privacy settings. */
|
||||
"SETTINGS_DISAPPEARING_MESSAGES" = "Disappearing Messages";
|
||||
|
||||
/* Explanation for the 'disappearig messages' privacy settings. */
|
||||
"SETTINGS_DISAPPEARING_MESSAGES_FOOTER" = "Set a default disappearing message timer for all new chats started by you.";
|
||||
|
||||
/* Title for the 'donate to signal' link in settings. */
|
||||
"SETTINGS_DONATE" = "Donate to Signal";
|
||||
|
||||
@ -5539,6 +5548,9 @@
|
||||
/* Label for button to verify a user's safety number. */
|
||||
"SYSTEM_MESSAGE_ACTION_VERIFY_SAFETY_NUMBER" = "Verify";
|
||||
|
||||
/* Indicator that the default disappearing message timer will be applied when you send a message. Embeds {default disappearing message time} */
|
||||
"SYSTEM_MESSAGE_DEFAULT_DISAPPEARING_MESSAGE_TIMER_FORMAT" = "The disappearing message time will be set to %@ when you message them.";
|
||||
|
||||
/* Indicator warning about an unknown contact thread. */
|
||||
"SYSTEM_MESSAGE_UNKNOWN_THREAD_WARNING_CONTACT" = "No groups in common. Review requests carefully.";
|
||||
|
||||
|
||||
@ -40,7 +40,7 @@ public class AttachmentMultisend: Dependencies {
|
||||
}
|
||||
|
||||
// If this thread has a pending message request, treat it as accepted.
|
||||
ThreadUtil.addThread(toProfileWhitelistIfEmptyOrPendingRequest: thread, transaction: transaction)
|
||||
ThreadUtil.addThread(toProfileWhitelistIfEmptyOrPendingRequestAndSetDefaultTimer: thread, transaction: transaction)
|
||||
|
||||
let message = try! ThreadUtil.createUnsentMessage(with: approvalMessageBody,
|
||||
mediaAttachments: attachments,
|
||||
@ -113,7 +113,7 @@ public class AttachmentMultisend: Dependencies {
|
||||
}
|
||||
|
||||
// If this thread has a pending message request, treat it as accepted.
|
||||
ThreadUtil.addThread(toProfileWhitelistIfEmptyOrPendingRequest: thread, transaction: transaction)
|
||||
ThreadUtil.addThread(toProfileWhitelistIfEmptyOrPendingRequestAndSetDefaultTimer: thread, transaction: transaction)
|
||||
|
||||
let message = try! ThreadUtil.createUnsentMessage(with: approvalMessageBody,
|
||||
mediaAttachments: attachments,
|
||||
|
||||
@ -84,9 +84,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
// the local user took an action like initiating a call or updating the DM timer.
|
||||
//
|
||||
// Returns YES IFF the thread was just added to the profile whitelist.
|
||||
+ (BOOL)addThreadToProfileWhitelistIfEmptyOrPendingRequestWithSneakyTransaction:(TSThread *)thread NS_SWIFT_NAME(addToProfileWhitelistIfEmptyOrPendingRequestWithSneakyTransaction(thread:));
|
||||
+ (BOOL)addThreadToProfileWhitelistIfEmptyOrPendingRequest:(TSThread *)thread
|
||||
transaction:(SDSAnyWriteTransaction *)transaction;
|
||||
+ (BOOL)addThreadToProfileWhitelistIfEmptyOrPendingRequestAndSetDefaultTimerWithSneakyTransaction:(TSThread *)thread
|
||||
NS_SWIFT_NAME(addToProfileWhitelistIfEmptyOrPendingRequestWithSneakyTransaction(thread:));
|
||||
+ (BOOL)addThreadToProfileWhitelistIfEmptyOrPendingRequestAndSetDefaultTimer:(TSThread *)thread
|
||||
transaction:(SDSAnyWriteTransaction *)transaction;
|
||||
|
||||
#pragma mark - Delete Content
|
||||
|
||||
|
||||
@ -271,15 +271,39 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#pragma mark - Profile Whitelist
|
||||
|
||||
+ (BOOL)addThreadToProfileWhitelistIfEmptyOrPendingRequestWithSneakyTransaction:(TSThread *)thread
|
||||
+ (BOOL)addThreadToProfileWhitelistIfEmptyOrPendingRequestAndSetDefaultTimerWithSneakyTransaction:(TSThread *)thread
|
||||
{
|
||||
OWSAssertDebug(thread);
|
||||
|
||||
__block BOOL hasPendingMessageRequest;
|
||||
__block BOOL needsDefaultTimerSet;
|
||||
__block DisappearingMessageToken *defaultTimerToken;
|
||||
[self.databaseStorage readWithBlock:^(SDSAnyReadTransaction *transaction) {
|
||||
hasPendingMessageRequest = [thread hasPendingMessageRequestWithTransaction:transaction.unwrapGrdbRead];
|
||||
OWSDisappearingMessagesConfiguration *configuration =
|
||||
[thread disappearingMessagesConfigurationWithTransaction:transaction];
|
||||
needsDefaultTimerSet = thread.lastInteractionRowId == 0
|
||||
&& ![OWSDisappearingMessagesConfiguration anyExistsWithUniqueId:configuration.uniqueId
|
||||
transaction:transaction];
|
||||
defaultTimerToken = configuration.asToken;
|
||||
}];
|
||||
|
||||
if (needsDefaultTimerSet) {
|
||||
DatabaseStorageWrite(self.databaseStorage, ^(SDSAnyWriteTransaction *transaction) {
|
||||
OWSDisappearingMessagesConfiguration *configuration =
|
||||
[OWSDisappearingMessagesConfiguration applyToken:defaultTimerToken
|
||||
toThread:thread
|
||||
transaction:transaction];
|
||||
|
||||
OWSDisappearingConfigurationUpdateInfoMessage *infoMessage =
|
||||
[[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithThread:thread
|
||||
configuration:configuration
|
||||
createdByRemoteName:nil
|
||||
createdInExistingGroup:NO];
|
||||
[infoMessage anyInsertWithTransaction:transaction];
|
||||
});
|
||||
}
|
||||
|
||||
// If we're creating this thread or we have a pending message request,
|
||||
// any action we trigger should share our profile.
|
||||
if (!thread.shouldThreadBeVisible || hasPendingMessageRequest) {
|
||||
@ -290,11 +314,30 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (BOOL)addThreadToProfileWhitelistIfEmptyOrPendingRequest:(TSThread *)thread
|
||||
transaction:(SDSAnyWriteTransaction *)transaction
|
||||
+ (BOOL)addThreadToProfileWhitelistIfEmptyOrPendingRequestAndSetDefaultTimer:(TSThread *)thread
|
||||
transaction:(SDSAnyWriteTransaction *)transaction
|
||||
{
|
||||
OWSAssertDebug(thread);
|
||||
|
||||
OWSDisappearingMessagesConfiguration *defaultConfiguration =
|
||||
[thread disappearingMessagesConfigurationWithTransaction:transaction];
|
||||
BOOL needsDefaultTimerSet = thread.lastInteractionRowId == 0
|
||||
&& ![OWSDisappearingMessagesConfiguration anyExistsWithUniqueId:defaultConfiguration.uniqueId
|
||||
transaction:transaction];
|
||||
DisappearingMessageToken *defaultTimerToken = defaultConfiguration.asToken;
|
||||
|
||||
if (needsDefaultTimerSet) {
|
||||
OWSDisappearingMessagesConfiguration *configuration =
|
||||
[OWSDisappearingMessagesConfiguration applyToken:defaultTimerToken toThread:thread transaction:transaction];
|
||||
|
||||
OWSDisappearingConfigurationUpdateInfoMessage *infoMessage =
|
||||
[[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithThread:thread
|
||||
configuration:configuration
|
||||
createdByRemoteName:nil
|
||||
createdInExistingGroup:NO];
|
||||
[infoMessage anyInsertWithTransaction:transaction];
|
||||
}
|
||||
|
||||
BOOL hasPendingMessageRequest = [thread hasPendingMessageRequestWithTransaction:transaction.unwrapGrdbRead];
|
||||
// If we're creating this thread or we have a pending message request,
|
||||
// any action we trigger should share our profile.
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
//
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <SignalServiceKit/BaseModel.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#define OWSDisappearingMessagesConfigurationDefaultExpirationDuration kDayInterval
|
||||
#define OWSDisappearingMessagesConfigurationDefaultExpirationDuration 0
|
||||
|
||||
@class SDSAnyReadTransaction;
|
||||
@class TSThread;
|
||||
@ -49,6 +49,8 @@ NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:durationSeconds:ena
|
||||
// for a given thread; do not use anyFetchWithUniqueId.
|
||||
+ (instancetype)fetchOrBuildDefaultWithThread:(TSThread *)thread transaction:(SDSAnyReadTransaction *)transaction;
|
||||
|
||||
+ (instancetype)fetchOrBuildDefaultUniversalConfigurationWithTransaction:(SDSAnyReadTransaction *)transaction;
|
||||
|
||||
+ (NSArray<NSNumber *> *)validDurationsSeconds;
|
||||
+ (uint32_t)maxDurationSeconds;
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSDisappearingMessagesConfiguration.h"
|
||||
@ -9,6 +9,8 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
static NSString *const kUniversalTimerThreadId = @"kUniversalTimerThreadId";
|
||||
|
||||
@interface OWSDisappearingMessagesConfiguration ()
|
||||
|
||||
@property (nonatomic, getter=isEnabled) BOOL enabled;
|
||||
@ -96,10 +98,15 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
}
|
||||
|
||||
return [[self alloc] initWithThreadId:threadId
|
||||
enabled:NO
|
||||
enabled:OWSDisappearingMessagesConfigurationDefaultExpirationDuration > 0
|
||||
durationSeconds:OWSDisappearingMessagesConfigurationDefaultExpirationDuration];
|
||||
}
|
||||
|
||||
+ (instancetype)fetchOrBuildDefaultUniversalConfigurationWithTransaction:(SDSAnyReadTransaction *)transaction
|
||||
{
|
||||
return [self fetchOrBuildDefaultWithThreadId:kUniversalTimerThreadId transaction:transaction];
|
||||
}
|
||||
|
||||
+ (NSArray<NSNumber *> *)validDurationsSeconds
|
||||
{
|
||||
return @[
|
||||
@ -113,7 +120,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@(6 * kHourInterval),
|
||||
@(12 * kHourInterval),
|
||||
@(24 * kHourInterval),
|
||||
@(1 * kWeekInterval)
|
||||
@(1 * kWeekInterval),
|
||||
@(4 * kWeekInterval)
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
//
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
@ -66,10 +66,12 @@ public class DisappearingMessageToken: MTLModel {
|
||||
// MARK: -
|
||||
|
||||
public extension OWSDisappearingMessagesConfiguration {
|
||||
@objc
|
||||
var asToken: DisappearingMessageToken {
|
||||
return DisappearingMessageToken(isEnabled: isEnabled, durationSeconds: durationSeconds)
|
||||
}
|
||||
|
||||
@objc
|
||||
static func applyToken(_ token: DisappearingMessageToken,
|
||||
toThread thread: TSThread,
|
||||
transaction: SDSAnyWriteTransaction) -> OWSDisappearingMessagesConfiguration {
|
||||
@ -78,6 +80,7 @@ public extension OWSDisappearingMessagesConfiguration {
|
||||
return oldConfiguration.applyToken(token, transaction: transaction)
|
||||
}
|
||||
|
||||
@objc
|
||||
func applyToken(_ token: DisappearingMessageToken,
|
||||
transaction: SDSAnyWriteTransaction) -> OWSDisappearingMessagesConfiguration {
|
||||
let newConfiguration: OWSDisappearingMessagesConfiguration
|
||||
|
||||
@ -1469,8 +1469,18 @@ extension GroupUpdateCopy {
|
||||
|
||||
guard let oldToken = oldToken else {
|
||||
if newToken.isEnabled {
|
||||
let format = NSLocalizedString("DISAPPEARING_MESSAGES_CONFIGURATION_GROUP_EXISTING_FORMAT",
|
||||
comment: "Info Message when added to a group which has enabled disappearing messages. Embeds {{time amount}} before messages disappear. See the *_TIME_AMOUNT strings for context.")
|
||||
let format: String
|
||||
if updater == .localUser {
|
||||
format = NSLocalizedString(
|
||||
"YOU_UPDATED_DISAPPEARING_MESSAGES_CONFIGURATION",
|
||||
comment: "Info Message when you update disappearing messages duration. Embeds a {{time amount}} before messages disappear. see the *_TIME_AMOUNT strings for context."
|
||||
)
|
||||
} else {
|
||||
format = NSLocalizedString(
|
||||
"DISAPPEARING_MESSAGES_CONFIGURATION_GROUP_EXISTING_FORMAT",
|
||||
comment: "Info Message when added to a group which has enabled disappearing messages. Embeds {{time amount}} before messages disappear. See the *_TIME_AMOUNT strings for context."
|
||||
)
|
||||
}
|
||||
addItem(.disappearingMessagesState_enabled, format: format, durationString)
|
||||
}
|
||||
return
|
||||
@ -1831,7 +1841,7 @@ extension GroupUpdateCopy {
|
||||
|
||||
// MARK: - Updater
|
||||
|
||||
enum Updater {
|
||||
enum Updater: Equatable {
|
||||
case localUser
|
||||
case otherUser(updaterName: String, updaterAddress: SignalServiceAddress)
|
||||
case unknown
|
||||
|
||||
@ -20,7 +20,8 @@ typedef NS_CLOSED_ENUM(NSInteger, OWSInteractionType) {
|
||||
OWSInteractionType_ThreadDetails,
|
||||
OWSInteractionType_UnreadIndicator,
|
||||
OWSInteractionType_DateHeader,
|
||||
OWSInteractionType_UnknownThreadWarning
|
||||
OWSInteractionType_UnknownThreadWarning,
|
||||
OWSInteractionType_DefaultDisappearingMessageTimer
|
||||
};
|
||||
|
||||
NSString *NSStringFromOWSInteractionType(OWSInteractionType value);
|
||||
|
||||
@ -34,6 +34,8 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value)
|
||||
return @"OWSInteractionType_DateHeader";
|
||||
case OWSInteractionType_UnknownThreadWarning:
|
||||
return @"OWSInteractionType_UnknownThreadWarning";
|
||||
case OWSInteractionType_DefaultDisappearingMessageTimer:
|
||||
return @"OWSInteractionType_DefaultDisappearingMessageTimer";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -225,6 +225,7 @@ public class GroupManager: NSObject {
|
||||
groupId: Data? = nil,
|
||||
name: String? = nil,
|
||||
avatarImage: UIImage?,
|
||||
disappearingMessageToken: DisappearingMessageToken,
|
||||
newGroupSeed: NewGroupSeed? = nil,
|
||||
shouldSendMessage: Bool) -> Promise<TSGroupThread> {
|
||||
|
||||
@ -235,6 +236,7 @@ public class GroupManager: NSObject {
|
||||
groupId: groupId,
|
||||
name: name,
|
||||
avatarData: avatarData,
|
||||
disappearingMessageToken: disappearingMessageToken,
|
||||
newGroupSeed: newGroupSeed,
|
||||
shouldSendMessage: shouldSendMessage)
|
||||
}
|
||||
@ -245,6 +247,7 @@ public class GroupManager: NSObject {
|
||||
groupId: Data? = nil,
|
||||
name: String? = nil,
|
||||
avatarData: Data? = nil,
|
||||
disappearingMessageToken: DisappearingMessageToken,
|
||||
newGroupSeed: NewGroupSeed? = nil,
|
||||
shouldSendMessage: Bool) -> Promise<TSGroupThread> {
|
||||
|
||||
@ -252,9 +255,6 @@ public class GroupManager: NSObject {
|
||||
return Promise(error: OWSAssertionError("Missing localAddress."))
|
||||
}
|
||||
|
||||
// By default, DMs are disable for new groups.
|
||||
let disappearingMessageToken = DisappearingMessageToken.disabledToken
|
||||
|
||||
return firstly { () -> Promise<Void> in
|
||||
return self.ensureLocalProfileHasCommitmentIfNecessary()
|
||||
}.then(on: .global()) { () -> Promise<Void> in
|
||||
@ -496,6 +496,7 @@ public class GroupManager: NSObject {
|
||||
groupId: Data?,
|
||||
name: String,
|
||||
avatarImage: UIImage?,
|
||||
disappearingMessageToken: DisappearingMessageToken,
|
||||
newGroupSeed: NewGroupSeed?,
|
||||
shouldSendMessage: Bool,
|
||||
success: @escaping (TSGroupThread) -> Void,
|
||||
@ -505,6 +506,7 @@ public class GroupManager: NSObject {
|
||||
groupId: groupId,
|
||||
name: name,
|
||||
avatarImage: avatarImage,
|
||||
disappearingMessageToken: disappearingMessageToken,
|
||||
newGroupSeed: newGroupSeed,
|
||||
shouldSendMessage: shouldSendMessage)
|
||||
}.done { thread in
|
||||
@ -520,6 +522,7 @@ public class GroupManager: NSObject {
|
||||
groupId: Data?,
|
||||
name: String,
|
||||
avatarData: Data?,
|
||||
disappearingMessageToken: DisappearingMessageToken,
|
||||
newGroupSeed: NewGroupSeed?,
|
||||
shouldSendMessage: Bool,
|
||||
success: @escaping (TSGroupThread) -> Void,
|
||||
@ -529,6 +532,7 @@ public class GroupManager: NSObject {
|
||||
groupId: groupId,
|
||||
name: name,
|
||||
avatarData: avatarData,
|
||||
disappearingMessageToken: disappearingMessageToken,
|
||||
newGroupSeed: newGroupSeed,
|
||||
shouldSendMessage: shouldSendMessage)
|
||||
}.done { thread in
|
||||
|
||||
Loading…
Reference in New Issue
Block a user