From 0c09f2bfc54e2a3f0fe1ab035a0271d28e7deb3a Mon Sep 17 00:00:00 2001 From: Morgan Pretty Date: Mon, 3 Oct 2022 18:23:34 +1100 Subject: [PATCH] Fixed a few more QA issues, added a minor feature Updated the document download UI Minor font tweaks to match settings more closely Added profile data to the MessageRequestResponse Fixed the broken tests --- Session.xcodeproj/project.pbxproj | 4 - .../Content Views/DocumentView.swift | 56 +- .../Message Cells/VisibleMessageCell.swift | 12 +- .../ThreadDisappearingMessagesViewModel.swift | 17 +- .../Translations/fa.lproj/Localizable.strings | 1 - .../NotificationContentViewModel.swift | 9 +- .../Types/SessionTableViewModel+NavItem.swift | 17 +- Session/Shared/Views/SessionCell.swift | 4 +- .../Database/Models/OpenGroup.swift | 4 +- .../Database/Models/Profile.swift | 2 +- .../MessageRequestResponse.swift | 25 +- .../VisibleMessage+Profile.swift | 50 +- .../Visible Messages/VisibleMessage.swift | 2 +- .../Protos/Generated/SNProto.swift | 270 ++++--- .../Protos/Generated/SessionProtos.pb.swift | 632 +++++++++------- .../Generated/WebSocketResources.pb.swift | 56 +- .../Protos/SessionProtos.proto | 14 +- .../MessageReceiver+MessageRequests.swift | 19 + .../Sending & Receiving/MessageSender.swift | 6 +- .../Open Groups/Models/OpenGroupSpec.swift | 2 +- ...eadDisappearingMessagesViewModelSpec.swift | 680 +++++++----------- SessionTests/SessionTests.swift | 30 - .../NotificationContentViewModelSpec.swift | 120 ++-- SessionUtilitiesKit/Database/Storage.swift | 10 +- 24 files changed, 1081 insertions(+), 961 deletions(-) delete mode 100644 SessionTests/SessionTests.swift diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index 27eb88dfd..81e10e60d 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -671,7 +671,6 @@ FD71160028C8253500B47552 /* UIView+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD7115FF28C8253500B47552 /* UIView+Combine.swift */; }; FD71160228C8255900B47552 /* UIControl+Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD71160128C8255900B47552 /* UIControl+Combine.swift */; }; FD71160428C95B5600B47552 /* PhotoCollectionPickerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD71160328C95B5600B47552 /* PhotoCollectionPickerViewModel.swift */; }; - FD71160C28D00BAE00B47552 /* SessionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD71160B28D00BAE00B47552 /* SessionTests.swift */; }; FD71161528D00D6700B47552 /* ThreadDisappearingMessagesViewModelSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD71161428D00D6700B47552 /* ThreadDisappearingMessagesViewModelSpec.swift */; }; FD71161728D00DA400B47552 /* ThreadSettingsViewModelSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD71161628D00DA400B47552 /* ThreadSettingsViewModelSpec.swift */; }; FD71161A28D00E1100B47552 /* NotificationContentViewModelSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD71161928D00E1100B47552 /* NotificationContentViewModelSpec.swift */; }; @@ -1756,7 +1755,6 @@ FD71160128C8255900B47552 /* UIControl+Combine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIControl+Combine.swift"; sourceTree = ""; }; FD71160328C95B5600B47552 /* PhotoCollectionPickerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoCollectionPickerViewModel.swift; sourceTree = ""; }; FD71160928D00BAE00B47552 /* SessionTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SessionTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - FD71160B28D00BAE00B47552 /* SessionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionTests.swift; sourceTree = ""; }; FD71161428D00D6700B47552 /* ThreadDisappearingMessagesViewModelSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadDisappearingMessagesViewModelSpec.swift; sourceTree = ""; }; FD71161628D00DA400B47552 /* ThreadSettingsViewModelSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadSettingsViewModelSpec.swift; sourceTree = ""; }; FD71161928D00E1100B47552 /* NotificationContentViewModelSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationContentViewModelSpec.swift; sourceTree = ""; }; @@ -3788,7 +3786,6 @@ children = ( FD71161228D00D5300B47552 /* Conversations */, FD71161828D00E0100B47552 /* Settings */, - FD71160B28D00BAE00B47552 /* SessionTests.swift */, ); path = SessionTests; sourceTree = ""; @@ -5758,7 +5755,6 @@ FD71161728D00DA400B47552 /* ThreadSettingsViewModelSpec.swift in Sources */, FD71161528D00D6700B47552 /* ThreadDisappearingMessagesViewModelSpec.swift in Sources */, FD71161A28D00E1100B47552 /* NotificationContentViewModelSpec.swift in Sources */, - FD71160C28D00BAE00B47552 /* SessionTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Session/Conversations/Message Cells/Content Views/DocumentView.swift b/Session/Conversations/Message Cells/Content Views/DocumentView.swift index e5acc8ea9..b3f892cb8 100644 --- a/Session/Conversations/Message Cells/Content Views/DocumentView.swift +++ b/Session/Conversations/Message Cells/Content Views/DocumentView.swift @@ -5,8 +5,6 @@ import SessionUIKit import SessionMessagingKit final class DocumentView: UIView { - private static let iconImageViewSize: CGSize = CGSize(width: 31, height: 40) - // MARK: - Lifecycle init(attachment: Attachment, textColor: ThemeValue) { @@ -24,18 +22,23 @@ final class DocumentView: UIView { } private func setUpViewHierarchy(attachment: Attachment, textColor: ThemeValue) { - // Image view - let imageView = UIImageView(image: UIImage(named: "File")?.withRenderingMode(.alwaysTemplate)) - imageView.themeTintColor = textColor - imageView.contentMode = .center + let imageBackgroundView: UIView = UIView() + imageBackgroundView.themeBackgroundColor = .messageBubble_overlay + addSubview(imageBackgroundView) - let iconImageViewSize = DocumentView.iconImageViewSize - imageView.set(.width, to: iconImageViewSize.width) - imageView.set(.height, to: iconImageViewSize.height) + // Image view + let imageView = UIImageView( + image: UIImage(systemName: "doc")? + .withRenderingMode(.alwaysTemplate) + ) + imageView.setContentCompressionResistancePriority(.required, for: .horizontal) + imageView.setContentHuggingPriority(.required, for: .horizontal) + imageView.themeTintColor = textColor + imageView.set(.height, to: 22) // Body label let titleLabel = UILabel() - titleLabel.font = .systemFont(ofSize: Values.smallFontSize, weight: .light) + titleLabel.font = .systemFont(ofSize: Values.mediumFontSize) titleLabel.text = (attachment.sourceFilename ?? "File") titleLabel.themeTextColor = textColor titleLabel.lineBreakMode = .byTruncatingTail @@ -51,12 +54,41 @@ final class DocumentView: UIView { let labelStackView = UIStackView(arrangedSubviews: [ titleLabel, sizeLabel ]) labelStackView.axis = .vertical + // Download image view + let downloadImageView = UIImageView( + image: UIImage(systemName: "arrow.down")? + .withRenderingMode(.alwaysTemplate) + ) + downloadImageView.setContentCompressionResistancePriority(.required, for: .horizontal) + downloadImageView.setContentHuggingPriority(.required, for: .horizontal) + downloadImageView.themeTintColor = textColor + downloadImageView.set(.height, to: 16) + // Stack view - let stackView = UIStackView(arrangedSubviews: [ imageView, labelStackView ]) + let stackView = UIStackView( + arrangedSubviews: [ + imageView, + UIView.spacer(withWidth: 0), + labelStackView, + downloadImageView + ] + ) stackView.axis = .horizontal - stackView.spacing = Values.verySmallSpacing + stackView.spacing = Values.mediumSpacing stackView.alignment = .center + stackView.layoutMargins = UIEdgeInsets( + top: Values.smallSpacing, + leading: Values.mediumSpacing, + bottom: Values.smallSpacing, + trailing: Values.mediumSpacing + ) + stackView.isLayoutMarginsRelativeArrangement = true addSubview(stackView) stackView.pin(to: self) + + imageBackgroundView.pin(.top, to: .top, of: self) + imageBackgroundView.pin(.leading, to: .leading, of: self) + imageBackgroundView.pin(.trailing, to: .trailing, of: imageView, withInset: Values.mediumSpacing) + imageBackgroundView.pin(.bottom, to: .bottom, of: self) } } diff --git a/Session/Conversations/Message Cells/VisibleMessageCell.swift b/Session/Conversations/Message Cells/VisibleMessageCell.swift index caa9722c3..e54312a92 100644 --- a/Session/Conversations/Message Cells/VisibleMessageCell.swift +++ b/Session/Conversations/Message Cells/VisibleMessageCell.swift @@ -589,7 +589,7 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate { let inset: CGFloat = 12 let maxWidth = (VisibleMessageCell.getMaxWidth(for: cellViewModel) - 2 * inset) - + // Stack view let stackView = UIStackView(arrangedSubviews: []) stackView.axis = .vertical @@ -601,6 +601,7 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate { // Body text view if let body: String = cellViewModel.body, !body.isEmpty { // delegate should always be set at this point + let bodyContainerView: UIView = UIView() let bodyTappableLabel = VisibleMessageCell.getBodyTappableLabel( for: cellViewModel, with: maxWidth, @@ -610,11 +611,16 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate { ) self.bodyTappableLabel = bodyTappableLabel - stackView.addArrangedSubview(bodyTappableLabel) + bodyContainerView.addSubview(bodyTappableLabel) + bodyTappableLabel.pin(.top, to: .top, of: bodyContainerView) + bodyTappableLabel.pin(.leading, to: .leading, of: bodyContainerView, withInset: 12) + bodyTappableLabel.pin(.trailing, to: .trailing, of: bodyContainerView, withInset: -12) + bodyTappableLabel.pin(.bottom, to: .bottom, of: bodyContainerView, withInset: -12) + stackView.addArrangedSubview(bodyContainerView) } bubbleView.addSubview(stackView) - stackView.pin(to: bubbleView, withInset: inset) + stackView.pin(to: bubbleView) snContentView.addArrangedSubview(bubbleBackgroundView) } } diff --git a/Session/Conversations/Settings/ThreadDisappearingMessagesViewModel.swift b/Session/Conversations/Settings/ThreadDisappearingMessagesViewModel.swift index 9dbcc9765..89bb47b88 100644 --- a/Session/Conversations/Settings/ThreadDisappearingMessagesViewModel.swift +++ b/Session/Conversations/Settings/ThreadDisappearingMessagesViewModel.swift @@ -28,6 +28,8 @@ class ThreadDisappearingMessagesViewModel: SessionTableViewModel [SectionModel] in + .trackingConstantRegion { [weak self, config] db -> [SectionModel] in return [ SectionModel( model: .content, @@ -127,7 +136,7 @@ class ThreadDisappearingMessagesViewModel: SessionTableViewModel { private let storage: Storage + private let scheduler: ValueObservationScheduler // MARK: - Initialization - init(storage: Storage = Storage.shared) { + init( + storage: Storage = Storage.shared, + scheduling scheduler: ValueObservationScheduler = Storage.defaultPublisherScheduler + ) { self.storage = storage + self.scheduler = scheduler } // MARK: - Section @@ -67,7 +72,7 @@ class NotificationContentViewModel: SessionTableViewModel.NavItem, + rhs: SessionTableViewModel.NavItem + ) -> Bool { + return ( + lhs.id == rhs.id && + lhs.image == rhs.image && + lhs.style == rhs.style && + lhs.systemItem == rhs.systemItem && + lhs.accessibilityIdentifier == rhs.accessibilityIdentifier + ) + } } } diff --git a/Session/Shared/Views/SessionCell.swift b/Session/Shared/Views/SessionCell.swift index c62dcde13..cbbeffa15 100644 --- a/Session/Shared/Views/SessionCell.swift +++ b/Session/Shared/Views/SessionCell.swift @@ -92,7 +92,7 @@ public class SessionCell: UITableViewCell { private let titleLabel: UILabel = { let result: UILabel = UILabel() result.translatesAutoresizingMaskIntoConstraints = false - result.font = .boldSystemFont(ofSize: Values.mediumFontSize) + result.font = .boldSystemFont(ofSize: 15) result.themeTextColor = .textPrimary result.numberOfLines = 0 result.setCompressionResistanceHorizontalLow() @@ -104,7 +104,7 @@ public class SessionCell: UITableViewCell { private let subtitleLabel: UILabel = { let result: UILabel = UILabel() result.translatesAutoresizingMaskIntoConstraints = false - result.font = .systemFont(ofSize: Values.smallFontSize) + result.font = .systemFont(ofSize: 13) result.themeTextColor = .textPrimary result.numberOfLines = 0 result.isHidden = true diff --git a/SessionMessagingKit/Database/Models/OpenGroup.swift b/SessionMessagingKit/Database/Models/OpenGroup.swift index 48112352c..cc0439472 100644 --- a/SessionMessagingKit/Database/Models/OpenGroup.swift +++ b/SessionMessagingKit/Database/Models/OpenGroup.swift @@ -238,8 +238,8 @@ extension OpenGroup: CustomStringConvertible, CustomDebugStringConvertible { "sequenceNumber: \(sequenceNumber)", "inboxLatestMessageId: \(inboxLatestMessageId)", "outboxLatestMessageId: \(outboxLatestMessageId)", - "pollFailureCount: \(pollFailureCount))", - "permissions: \(permissions?.toString() ?? "---")" + "pollFailureCount: \(pollFailureCount)", + "permissions: \(permissions?.toString() ?? "---"))" ].joined(separator: ", ") } } diff --git a/SessionMessagingKit/Database/Models/Profile.swift b/SessionMessagingKit/Database/Models/Profile.swift index c9f33243d..38f7c4cd9 100644 --- a/SessionMessagingKit/Database/Models/Profile.swift +++ b/SessionMessagingKit/Database/Models/Profile.swift @@ -152,7 +152,7 @@ public extension Profile { func toProto() -> SNProtoDataMessage? { let dataMessageProto = SNProtoDataMessage.builder() - let profileProto = SNProtoDataMessageLokiProfile.builder() + let profileProto = SNProtoLokiProfile.builder() profileProto.setDisplayName(name) if let profileKey: OWSAES256Key = profileEncryptionKey, let profilePictureUrl: String = profilePictureUrl { diff --git a/SessionMessagingKit/Messages/Control Messages/MessageRequestResponse.swift b/SessionMessagingKit/Messages/Control Messages/MessageRequestResponse.swift index 8568b31b4..1955ecb2d 100644 --- a/SessionMessagingKit/Messages/Control Messages/MessageRequestResponse.swift +++ b/SessionMessagingKit/Messages/Control Messages/MessageRequestResponse.swift @@ -7,17 +7,21 @@ import SessionUtilitiesKit public final class MessageRequestResponse: ControlMessage { private enum CodingKeys: String, CodingKey { case isApproved + case profile } public var isApproved: Bool + public var profile: VisibleMessage.VMProfile? // MARK: - Initialization public init( isApproved: Bool, + profile: VisibleMessage.VMProfile? = nil, sentTimestampMs: UInt64? = nil ) { self.isApproved = isApproved + self.profile = profile super.init( sentTimestamp: sentTimestampMs @@ -30,6 +34,7 @@ public final class MessageRequestResponse: ControlMessage { let container: KeyedDecodingContainer = try decoder.container(keyedBy: CodingKeys.self) isApproved = try container.decode(Bool.self, forKey: .isApproved) + profile = try? container.decode(VisibleMessage.VMProfile.self, forKey: .profile) try super.init(from: decoder) } @@ -40,6 +45,7 @@ public final class MessageRequestResponse: ControlMessage { var container: KeyedEncodingContainer = encoder.container(keyedBy: CodingKeys.self) try container.encodeIfPresent(isApproved, forKey: .isApproved) + try container.encodeIfPresent(profile, forKey: .profile) } // MARK: - Proto Conversion @@ -47,11 +53,23 @@ public final class MessageRequestResponse: ControlMessage { public override class func fromProto(_ proto: SNProtoContent, sender: String) -> MessageRequestResponse? { guard let messageRequestResponseProto = proto.messageRequestResponse else { return nil } - return MessageRequestResponse(isApproved: messageRequestResponseProto.isApproved) + return MessageRequestResponse( + isApproved: messageRequestResponseProto.isApproved, + profile: VisibleMessage.VMProfile.fromProto(messageRequestResponseProto) + ) } public override func toProto(_ db: Database) -> SNProtoContent? { - let messageRequestResponseProto = SNProtoMessageRequestResponse.builder(isApproved: isApproved) + let messageRequestResponseProto: SNProtoMessageRequestResponse.SNProtoMessageRequestResponseBuilder + + // Profile + if let profile = profile, let profileProto: SNProtoMessageRequestResponse = profile.toProto(isApproved: isApproved) { + messageRequestResponseProto = profileProto.asBuilder() + } + else { + messageRequestResponseProto = SNProtoMessageRequestResponse.builder(isApproved: isApproved) + } + let contentProto = SNProtoContent.builder() do { @@ -68,7 +86,8 @@ public final class MessageRequestResponse: ControlMessage { public var description: String { """ MessageRequestResponse( - isApproved: \(isApproved) + isApproved: \(isApproved), + profile: \(profile?.description ?? "null") ) """ } diff --git a/SessionMessagingKit/Messages/Visible Messages/VisibleMessage+Profile.swift b/SessionMessagingKit/Messages/Visible Messages/VisibleMessage+Profile.swift index a45766c36..dcec57fc5 100644 --- a/SessionMessagingKit/Messages/Visible Messages/VisibleMessage+Profile.swift +++ b/SessionMessagingKit/Messages/Visible Messages/VisibleMessage+Profile.swift @@ -3,6 +3,8 @@ import Foundation import SessionUtilitiesKit +// MARK: - VisibleMessage.VMProfile + public extension VisibleMessage { struct VMProfile: Codable { public let displayName: String? @@ -40,7 +42,7 @@ public extension VisibleMessage { return nil } let dataMessageProto = SNProtoDataMessage.builder() - let profileProto = SNProtoDataMessageLokiProfile.builder() + let profileProto = SNProtoLokiProfile.builder() profileProto.setDisplayName(displayName) if let profileKey = profileKey, let profilePictureUrl = profilePictureUrl { @@ -56,6 +58,43 @@ public extension VisibleMessage { } } + public static func fromProto(_ proto: SNProtoMessageRequestResponse) -> VMProfile? { + guard + let profileProto = proto.profile, + let displayName = profileProto.displayName + else { return nil } + + return VMProfile( + displayName: displayName, + profileKey: proto.profileKey, + profilePictureUrl: profileProto.profilePicture + ) + } + + public func toProto(isApproved: Bool) -> SNProtoMessageRequestResponse? { + guard let displayName = displayName else { + SNLog("Couldn't construct profile proto from: \(self).") + return nil + } + let messageRequestResponseProto = SNProtoMessageRequestResponse.builder( + isApproved: isApproved + ) + let profileProto = SNProtoLokiProfile.builder() + profileProto.setDisplayName(displayName) + + if let profileKey = profileKey, let profilePictureUrl = profilePictureUrl { + messageRequestResponseProto.setProfileKey(profileKey) + profileProto.setProfilePicture(profilePictureUrl) + } + do { + messageRequestResponseProto.setProfile(try profileProto.build()) + return try messageRequestResponseProto.build() + } catch { + SNLog("Couldn't construct profile proto from: \(self).") + return nil + } + } + // MARK: Description public var description: String { @@ -79,3 +118,12 @@ extension VisibleMessage.VMProfile { self.profilePictureUrl = profile.profilePictureUrl } } + +// MARK: - MessageWithProfile + +public protocol MessageWithProfile { + var profile: VisibleMessage.VMProfile? { get set } +} + +extension VisibleMessage: MessageWithProfile {} +extension MessageRequestResponse: MessageWithProfile {} diff --git a/SessionMessagingKit/Messages/Visible Messages/VisibleMessage.swift b/SessionMessagingKit/Messages/Visible Messages/VisibleMessage.swift index 0e043ef87..980d90b70 100644 --- a/SessionMessagingKit/Messages/Visible Messages/VisibleMessage.swift +++ b/SessionMessagingKit/Messages/Visible Messages/VisibleMessage.swift @@ -127,7 +127,7 @@ public final class VisibleMessage: Message { let dataMessage: SNProtoDataMessage.SNProtoDataMessageBuilder // Profile - if let profile = profile, let profileProto = profile.toProto() { + if let profile = profile, let profileProto: SNProtoDataMessage = profile.toProto() { dataMessage = profileProto.asBuilder() } else { diff --git a/SessionMessagingKit/Protos/Generated/SNProto.swift b/SessionMessagingKit/Protos/Generated/SNProto.swift index a575ba288..22a8dd6b2 100644 --- a/SessionMessagingKit/Protos/Generated/SNProto.swift +++ b/SessionMessagingKit/Protos/Generated/SNProto.swift @@ -463,6 +463,12 @@ extension SNProtoUnsendRequest.SNProtoUnsendRequestBuilder { // asBuilder() constructs a builder that reflects the proto's contents. @objc public func asBuilder() -> SNProtoMessageRequestResponseBuilder { let builder = SNProtoMessageRequestResponseBuilder(isApproved: isApproved) + if let _value = profileKey { + builder.setProfileKey(_value) + } + if let _value = profile { + builder.setProfile(_value) + } return builder } @@ -482,6 +488,14 @@ extension SNProtoUnsendRequest.SNProtoUnsendRequestBuilder { proto.isApproved = valueParam } + @objc public func setProfileKey(_ valueParam: Data) { + proto.profileKey = valueParam + } + + @objc public func setProfile(_ valueParam: SNProtoLokiProfile) { + proto.profile = valueParam.proto + } + @objc public func build() throws -> SNProtoMessageRequestResponse { return try SNProtoMessageRequestResponse.parseProto(proto) } @@ -495,10 +509,24 @@ extension SNProtoUnsendRequest.SNProtoUnsendRequestBuilder { @objc public let isApproved: Bool + @objc public let profile: SNProtoLokiProfile? + + @objc public var profileKey: Data? { + guard proto.hasProfileKey else { + return nil + } + return proto.profileKey + } + @objc public var hasProfileKey: Bool { + return proto.hasProfileKey + } + private init(proto: SessionProtos_MessageRequestResponse, - isApproved: Bool) { + isApproved: Bool, + profile: SNProtoLokiProfile?) { self.proto = proto self.isApproved = isApproved + self.profile = profile } @objc @@ -517,12 +545,18 @@ extension SNProtoUnsendRequest.SNProtoUnsendRequestBuilder { } let isApproved = proto.isApproved + var profile: SNProtoLokiProfile? = nil + if proto.hasProfile { + profile = try SNProtoLokiProfile.parseProto(proto.profile) + } + // MARK: - Begin Validation Logic for SNProtoMessageRequestResponse - // MARK: - End Validation Logic for SNProtoMessageRequestResponse - let result = SNProtoMessageRequestResponse(proto: proto, - isApproved: isApproved) + isApproved: isApproved, + profile: profile) return result } @@ -1194,6 +1228,117 @@ extension SNProtoDataExtractionNotification.SNProtoDataExtractionNotificationBui #endif +// MARK: - SNProtoLokiProfile + +@objc public class SNProtoLokiProfile: NSObject { + + // MARK: - SNProtoLokiProfileBuilder + + @objc public class func builder() -> SNProtoLokiProfileBuilder { + return SNProtoLokiProfileBuilder() + } + + // asBuilder() constructs a builder that reflects the proto's contents. + @objc public func asBuilder() -> SNProtoLokiProfileBuilder { + let builder = SNProtoLokiProfileBuilder() + if let _value = displayName { + builder.setDisplayName(_value) + } + if let _value = profilePicture { + builder.setProfilePicture(_value) + } + return builder + } + + @objc public class SNProtoLokiProfileBuilder: NSObject { + + private var proto = SessionProtos_LokiProfile() + + @objc fileprivate override init() {} + + @objc public func setDisplayName(_ valueParam: String) { + proto.displayName = valueParam + } + + @objc public func setProfilePicture(_ valueParam: String) { + proto.profilePicture = valueParam + } + + @objc public func build() throws -> SNProtoLokiProfile { + return try SNProtoLokiProfile.parseProto(proto) + } + + @objc public func buildSerializedData() throws -> Data { + return try SNProtoLokiProfile.parseProto(proto).serializedData() + } + } + + fileprivate let proto: SessionProtos_LokiProfile + + @objc public var displayName: String? { + guard proto.hasDisplayName else { + return nil + } + return proto.displayName + } + @objc public var hasDisplayName: Bool { + return proto.hasDisplayName + } + + @objc public var profilePicture: String? { + guard proto.hasProfilePicture else { + return nil + } + return proto.profilePicture + } + @objc public var hasProfilePicture: Bool { + return proto.hasProfilePicture + } + + private init(proto: SessionProtos_LokiProfile) { + self.proto = proto + } + + @objc + public func serializedData() throws -> Data { + return try self.proto.serializedData() + } + + @objc public class func parseData(_ serializedData: Data) throws -> SNProtoLokiProfile { + let proto = try SessionProtos_LokiProfile(serializedData: serializedData) + return try parseProto(proto) + } + + fileprivate class func parseProto(_ proto: SessionProtos_LokiProfile) throws -> SNProtoLokiProfile { + // MARK: - Begin Validation Logic for SNProtoLokiProfile - + + // MARK: - End Validation Logic for SNProtoLokiProfile - + + let result = SNProtoLokiProfile(proto: proto) + return result + } + + @objc public override var debugDescription: String { + return "\(proto)" + } +} + +#if DEBUG + +extension SNProtoLokiProfile { + @objc public func serializedDataIgnoringErrors() -> Data? { + return try! self.serializedData() + } +} + +extension SNProtoLokiProfile.SNProtoLokiProfileBuilder { + @objc public func buildIgnoringErrors() -> SNProtoLokiProfile? { + return try! self.build() + } +} + +#endif + // MARK: - SNProtoDataMessageQuoteQuotedAttachment @objc public class SNProtoDataMessageQuoteQuotedAttachment: NSObject { @@ -1798,117 +1943,6 @@ extension SNProtoDataMessageReaction.SNProtoDataMessageReactionBuilder { #endif -// MARK: - SNProtoDataMessageLokiProfile - -@objc public class SNProtoDataMessageLokiProfile: NSObject { - - // MARK: - SNProtoDataMessageLokiProfileBuilder - - @objc public class func builder() -> SNProtoDataMessageLokiProfileBuilder { - return SNProtoDataMessageLokiProfileBuilder() - } - - // asBuilder() constructs a builder that reflects the proto's contents. - @objc public func asBuilder() -> SNProtoDataMessageLokiProfileBuilder { - let builder = SNProtoDataMessageLokiProfileBuilder() - if let _value = displayName { - builder.setDisplayName(_value) - } - if let _value = profilePicture { - builder.setProfilePicture(_value) - } - return builder - } - - @objc public class SNProtoDataMessageLokiProfileBuilder: NSObject { - - private var proto = SessionProtos_DataMessage.LokiProfile() - - @objc fileprivate override init() {} - - @objc public func setDisplayName(_ valueParam: String) { - proto.displayName = valueParam - } - - @objc public func setProfilePicture(_ valueParam: String) { - proto.profilePicture = valueParam - } - - @objc public func build() throws -> SNProtoDataMessageLokiProfile { - return try SNProtoDataMessageLokiProfile.parseProto(proto) - } - - @objc public func buildSerializedData() throws -> Data { - return try SNProtoDataMessageLokiProfile.parseProto(proto).serializedData() - } - } - - fileprivate let proto: SessionProtos_DataMessage.LokiProfile - - @objc public var displayName: String? { - guard proto.hasDisplayName else { - return nil - } - return proto.displayName - } - @objc public var hasDisplayName: Bool { - return proto.hasDisplayName - } - - @objc public var profilePicture: String? { - guard proto.hasProfilePicture else { - return nil - } - return proto.profilePicture - } - @objc public var hasProfilePicture: Bool { - return proto.hasProfilePicture - } - - private init(proto: SessionProtos_DataMessage.LokiProfile) { - self.proto = proto - } - - @objc - public func serializedData() throws -> Data { - return try self.proto.serializedData() - } - - @objc public class func parseData(_ serializedData: Data) throws -> SNProtoDataMessageLokiProfile { - let proto = try SessionProtos_DataMessage.LokiProfile(serializedData: serializedData) - return try parseProto(proto) - } - - fileprivate class func parseProto(_ proto: SessionProtos_DataMessage.LokiProfile) throws -> SNProtoDataMessageLokiProfile { - // MARK: - Begin Validation Logic for SNProtoDataMessageLokiProfile - - - // MARK: - End Validation Logic for SNProtoDataMessageLokiProfile - - - let result = SNProtoDataMessageLokiProfile(proto: proto) - return result - } - - @objc public override var debugDescription: String { - return "\(proto)" - } -} - -#if DEBUG - -extension SNProtoDataMessageLokiProfile { - @objc public func serializedDataIgnoringErrors() -> Data? { - return try! self.serializedData() - } -} - -extension SNProtoDataMessageLokiProfile.SNProtoDataMessageLokiProfileBuilder { - @objc public func buildIgnoringErrors() -> SNProtoDataMessageLokiProfile? { - return try! self.build() - } -} - -#endif - // MARK: - SNProtoDataMessageOpenGroupInvitation @objc public class SNProtoDataMessageOpenGroupInvitation: NSObject { @@ -2510,7 +2544,7 @@ extension SNProtoDataMessageClosedGroupControlMessage.SNProtoDataMessageClosedGr proto.reaction = valueParam.proto } - @objc public func setProfile(_ valueParam: SNProtoDataMessageLokiProfile) { + @objc public func setProfile(_ valueParam: SNProtoLokiProfile) { proto.profile = valueParam.proto } @@ -2547,7 +2581,7 @@ extension SNProtoDataMessageClosedGroupControlMessage.SNProtoDataMessageClosedGr @objc public let reaction: SNProtoDataMessageReaction? - @objc public let profile: SNProtoDataMessageLokiProfile? + @objc public let profile: SNProtoLokiProfile? @objc public let openGroupInvitation: SNProtoDataMessageOpenGroupInvitation? @@ -2610,7 +2644,7 @@ extension SNProtoDataMessageClosedGroupControlMessage.SNProtoDataMessageClosedGr quote: SNProtoDataMessageQuote?, preview: [SNProtoDataMessagePreview], reaction: SNProtoDataMessageReaction?, - profile: SNProtoDataMessageLokiProfile?, + profile: SNProtoLokiProfile?, openGroupInvitation: SNProtoDataMessageOpenGroupInvitation?, closedGroupControlMessage: SNProtoDataMessageClosedGroupControlMessage?) { self.proto = proto @@ -2656,9 +2690,9 @@ extension SNProtoDataMessageClosedGroupControlMessage.SNProtoDataMessageClosedGr reaction = try SNProtoDataMessageReaction.parseProto(proto.reaction) } - var profile: SNProtoDataMessageLokiProfile? = nil + var profile: SNProtoLokiProfile? = nil if proto.hasProfile { - profile = try SNProtoDataMessageLokiProfile.parseProto(proto.profile) + profile = try SNProtoLokiProfile.parseProto(proto.profile) } var openGroupInvitation: SNProtoDataMessageOpenGroupInvitation? = nil diff --git a/SessionMessagingKit/Protos/Generated/SessionProtos.pb.swift b/SessionMessagingKit/Protos/Generated/SessionProtos.pb.swift index 77cbc650f..488fc61f9 100644 --- a/SessionMessagingKit/Protos/Generated/SessionProtos.pb.swift +++ b/SessionMessagingKit/Protos/Generated/SessionProtos.pb.swift @@ -244,11 +244,31 @@ struct SessionProtos_MessageRequestResponse { /// Clears the value of `isApproved`. Subsequent reads from it will return its default value. mutating func clearIsApproved() {self._isApproved = nil} + var profileKey: Data { + get {return _profileKey ?? Data()} + set {_profileKey = newValue} + } + /// Returns true if `profileKey` has been explicitly set. + var hasProfileKey: Bool {return self._profileKey != nil} + /// Clears the value of `profileKey`. Subsequent reads from it will return its default value. + mutating func clearProfileKey() {self._profileKey = nil} + + var profile: SessionProtos_LokiProfile { + get {return _profile ?? SessionProtos_LokiProfile()} + set {_profile = newValue} + } + /// Returns true if `profile` has been explicitly set. + var hasProfile: Bool {return self._profile != nil} + /// Clears the value of `profile`. Subsequent reads from it will return its default value. + mutating func clearProfile() {self._profile = nil} + var unknownFields = SwiftProtobuf.UnknownStorage() init() {} fileprivate var _isApproved: Bool? = nil + fileprivate var _profileKey: Data? = nil + fileprivate var _profile: SessionProtos_LokiProfile? = nil } struct SessionProtos_Content { @@ -521,6 +541,37 @@ extension SessionProtos_DataExtractionNotification.TypeEnum: CaseIterable { #endif // swift(>=4.2) +struct SessionProtos_LokiProfile { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + var displayName: String { + get {return _displayName ?? String()} + set {_displayName = newValue} + } + /// Returns true if `displayName` has been explicitly set. + var hasDisplayName: Bool {return self._displayName != nil} + /// Clears the value of `displayName`. Subsequent reads from it will return its default value. + mutating func clearDisplayName() {self._displayName = nil} + + var profilePicture: String { + get {return _profilePicture ?? String()} + set {_profilePicture = newValue} + } + /// Returns true if `profilePicture` has been explicitly set. + var hasProfilePicture: Bool {return self._profilePicture != nil} + /// Clears the value of `profilePicture`. Subsequent reads from it will return its default value. + mutating func clearProfilePicture() {self._profilePicture = nil} + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} + + fileprivate var _displayName: String? = nil + fileprivate var _profilePicture: String? = nil +} + struct SessionProtos_DataMessage { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for @@ -608,8 +659,8 @@ struct SessionProtos_DataMessage { /// Clears the value of `reaction`. Subsequent reads from it will return its default value. mutating func clearReaction() {_uniqueStorage()._reaction = nil} - var profile: SessionProtos_DataMessage.LokiProfile { - get {return _storage._profile ?? SessionProtos_DataMessage.LokiProfile()} + var profile: SessionProtos_LokiProfile { + get {return _storage._profile ?? SessionProtos_LokiProfile()} set {_uniqueStorage()._profile = newValue} } /// Returns true if `profile` has been explicitly set. @@ -910,37 +961,6 @@ struct SessionProtos_DataMessage { fileprivate var _action: SessionProtos_DataMessage.Reaction.Action? = nil } - struct LokiProfile { - // SwiftProtobuf.Message conformance is added in an extension below. See the - // `Message` and `Message+*Additions` files in the SwiftProtobuf library for - // methods supported on all messages. - - var displayName: String { - get {return _displayName ?? String()} - set {_displayName = newValue} - } - /// Returns true if `displayName` has been explicitly set. - var hasDisplayName: Bool {return self._displayName != nil} - /// Clears the value of `displayName`. Subsequent reads from it will return its default value. - mutating func clearDisplayName() {self._displayName = nil} - - var profilePicture: String { - get {return _profilePicture ?? String()} - set {_profilePicture = newValue} - } - /// Returns true if `profilePicture` has been explicitly set. - var hasProfilePicture: Bool {return self._profilePicture != nil} - /// Clears the value of `profilePicture`. Subsequent reads from it will return its default value. - mutating func clearProfilePicture() {self._profilePicture = nil} - - var unknownFields = SwiftProtobuf.UnknownStorage() - - init() {} - - fileprivate var _displayName: String? = nil - fileprivate var _profilePicture: String? = nil - } - struct OpenGroupInvitation { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for @@ -1702,24 +1722,28 @@ extension SessionProtos_Envelope: SwiftProtobuf.Message, SwiftProtobuf._MessageI } func traverse(visitor: inout V) throws { - if let v = self._type { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._type { try visitor.visitSingularEnumField(value: v, fieldNumber: 1) - } - if let v = self._source { + } }() + try { if let v = self._source { try visitor.visitSingularStringField(value: v, fieldNumber: 2) - } - if let v = self._timestamp { + } }() + try { if let v = self._timestamp { try visitor.visitSingularUInt64Field(value: v, fieldNumber: 5) - } - if let v = self._sourceDevice { + } }() + try { if let v = self._sourceDevice { try visitor.visitSingularUInt32Field(value: v, fieldNumber: 7) - } - if let v = self._content { + } }() + try { if let v = self._content { try visitor.visitSingularBytesField(value: v, fieldNumber: 8) - } - if let v = self._serverTimestamp { + } }() + try { if let v = self._serverTimestamp { try visitor.visitSingularUInt64Field(value: v, fieldNumber: 10) - } + } }() try unknownFields.traverse(visitor: &visitor) } @@ -1769,12 +1793,16 @@ extension SessionProtos_TypingMessage: SwiftProtobuf.Message, SwiftProtobuf._Mes } func traverse(visitor: inout V) throws { - if let v = self._timestamp { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._timestamp { try visitor.visitSingularUInt64Field(value: v, fieldNumber: 1) - } - if let v = self._action { + } }() + try { if let v = self._action { try visitor.visitSingularEnumField(value: v, fieldNumber: 2) - } + } }() try unknownFields.traverse(visitor: &visitor) } @@ -1820,12 +1848,16 @@ extension SessionProtos_UnsendRequest: SwiftProtobuf.Message, SwiftProtobuf._Mes } func traverse(visitor: inout V) throws { - if let v = self._timestamp { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._timestamp { try visitor.visitSingularUInt64Field(value: v, fieldNumber: 1) - } - if let v = self._author { + } }() + try { if let v = self._author { try visitor.visitSingularStringField(value: v, fieldNumber: 2) - } + } }() try unknownFields.traverse(visitor: &visitor) } @@ -1841,6 +1873,8 @@ extension SessionProtos_MessageRequestResponse: SwiftProtobuf.Message, SwiftProt static let protoMessageName: String = _protobuf_package + ".MessageRequestResponse" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ 1: .same(proto: "isApproved"), + 2: .same(proto: "profileKey"), + 3: .same(proto: "profile"), ] public var isInitialized: Bool { @@ -1855,20 +1889,34 @@ extension SessionProtos_MessageRequestResponse: SwiftProtobuf.Message, SwiftProt // enabled. https://github.com/apple/swift-protobuf/issues/1034 switch fieldNumber { case 1: try { try decoder.decodeSingularBoolField(value: &self._isApproved) }() + case 2: try { try decoder.decodeSingularBytesField(value: &self._profileKey) }() + case 3: try { try decoder.decodeSingularMessageField(value: &self._profile) }() default: break } } } func traverse(visitor: inout V) throws { - if let v = self._isApproved { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._isApproved { try visitor.visitSingularBoolField(value: v, fieldNumber: 1) - } + } }() + try { if let v = self._profileKey { + try visitor.visitSingularBytesField(value: v, fieldNumber: 2) + } }() + try { if let v = self._profile { + try visitor.visitSingularMessageField(value: v, fieldNumber: 3) + } }() try unknownFields.traverse(visitor: &visitor) } static func ==(lhs: SessionProtos_MessageRequestResponse, rhs: SessionProtos_MessageRequestResponse) -> Bool { if lhs._isApproved != rhs._isApproved {return false} + if lhs._profileKey != rhs._profileKey {return false} + if lhs._profile != rhs._profile {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } @@ -1958,30 +2006,34 @@ extension SessionProtos_Content: SwiftProtobuf.Message, SwiftProtobuf._MessageIm func traverse(visitor: inout V) throws { try withExtendedLifetime(_storage) { (_storage: _StorageClass) in - if let v = _storage._dataMessage { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = _storage._dataMessage { try visitor.visitSingularMessageField(value: v, fieldNumber: 1) - } - if let v = _storage._callMessage { + } }() + try { if let v = _storage._callMessage { try visitor.visitSingularMessageField(value: v, fieldNumber: 3) - } - if let v = _storage._receiptMessage { + } }() + try { if let v = _storage._receiptMessage { try visitor.visitSingularMessageField(value: v, fieldNumber: 5) - } - if let v = _storage._typingMessage { + } }() + try { if let v = _storage._typingMessage { try visitor.visitSingularMessageField(value: v, fieldNumber: 6) - } - if let v = _storage._configurationMessage { + } }() + try { if let v = _storage._configurationMessage { try visitor.visitSingularMessageField(value: v, fieldNumber: 7) - } - if let v = _storage._dataExtractionNotification { + } }() + try { if let v = _storage._dataExtractionNotification { try visitor.visitSingularMessageField(value: v, fieldNumber: 8) - } - if let v = _storage._unsendRequest { + } }() + try { if let v = _storage._unsendRequest { try visitor.visitSingularMessageField(value: v, fieldNumber: 9) - } - if let v = _storage._messageRequestResponse { + } }() + try { if let v = _storage._messageRequestResponse { try visitor.visitSingularMessageField(value: v, fieldNumber: 10) - } + } }() } try unknownFields.traverse(visitor: &visitor) } @@ -2041,9 +2093,13 @@ extension SessionProtos_CallMessage: SwiftProtobuf.Message, SwiftProtobuf._Messa } func traverse(visitor: inout V) throws { - if let v = self._type { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._type { try visitor.visitSingularEnumField(value: v, fieldNumber: 1) - } + } }() if !self.sdps.isEmpty { try visitor.visitRepeatedStringField(value: self.sdps, fieldNumber: 2) } @@ -2053,9 +2109,9 @@ extension SessionProtos_CallMessage: SwiftProtobuf.Message, SwiftProtobuf._Messa if !self.sdpMids.isEmpty { try visitor.visitRepeatedStringField(value: self.sdpMids, fieldNumber: 4) } - if let v = self._uuid { + try { if let v = self._uuid { try visitor.visitSingularStringField(value: v, fieldNumber: 5) - } + } }() try unknownFields.traverse(visitor: &visitor) } @@ -2108,12 +2164,16 @@ extension SessionProtos_KeyPair: SwiftProtobuf.Message, SwiftProtobuf._MessageIm } func traverse(visitor: inout V) throws { - if let v = self._publicKey { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._publicKey { try visitor.visitSingularBytesField(value: v, fieldNumber: 1) - } - if let v = self._privateKey { + } }() + try { if let v = self._privateKey { try visitor.visitSingularBytesField(value: v, fieldNumber: 2) - } + } }() try unknownFields.traverse(visitor: &visitor) } @@ -2151,12 +2211,16 @@ extension SessionProtos_DataExtractionNotification: SwiftProtobuf.Message, Swift } func traverse(visitor: inout V) throws { - if let v = self._type { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._type { try visitor.visitSingularEnumField(value: v, fieldNumber: 1) - } - if let v = self._timestamp { + } }() + try { if let v = self._timestamp { try visitor.visitSingularUInt64Field(value: v, fieldNumber: 2) - } + } }() try unknownFields.traverse(visitor: &visitor) } @@ -2175,6 +2239,48 @@ extension SessionProtos_DataExtractionNotification.TypeEnum: SwiftProtobuf._Prot ] } +extension SessionProtos_LokiProfile: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = _protobuf_package + ".LokiProfile" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "displayName"), + 2: .same(proto: "profilePicture"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularStringField(value: &self._displayName) }() + case 2: try { try decoder.decodeSingularStringField(value: &self._profilePicture) }() + default: break + } + } + } + + func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._displayName { + try visitor.visitSingularStringField(value: v, fieldNumber: 1) + } }() + try { if let v = self._profilePicture { + try visitor.visitSingularStringField(value: v, fieldNumber: 2) + } }() + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: SessionProtos_LokiProfile, rhs: SessionProtos_LokiProfile) -> Bool { + if lhs._displayName != rhs._displayName {return false} + if lhs._profilePicture != rhs._profilePicture {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + extension SessionProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = _protobuf_package + ".DataMessage" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ @@ -2205,7 +2311,7 @@ extension SessionProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf._Messa var _quote: SessionProtos_DataMessage.Quote? = nil var _preview: [SessionProtos_DataMessage.Preview] = [] var _reaction: SessionProtos_DataMessage.Reaction? = nil - var _profile: SessionProtos_DataMessage.LokiProfile? = nil + var _profile: SessionProtos_LokiProfile? = nil var _openGroupInvitation: SessionProtos_DataMessage.OpenGroupInvitation? = nil var _closedGroupControlMessage: SessionProtos_DataMessage.ClosedGroupControlMessage? = nil var _syncTarget: String? = nil @@ -2282,48 +2388,52 @@ extension SessionProtos_DataMessage: SwiftProtobuf.Message, SwiftProtobuf._Messa func traverse(visitor: inout V) throws { try withExtendedLifetime(_storage) { (_storage: _StorageClass) in - if let v = _storage._body { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = _storage._body { try visitor.visitSingularStringField(value: v, fieldNumber: 1) - } + } }() if !_storage._attachments.isEmpty { try visitor.visitRepeatedMessageField(value: _storage._attachments, fieldNumber: 2) } - if let v = _storage._group { + try { if let v = _storage._group { try visitor.visitSingularMessageField(value: v, fieldNumber: 3) - } - if let v = _storage._flags { + } }() + try { if let v = _storage._flags { try visitor.visitSingularUInt32Field(value: v, fieldNumber: 4) - } - if let v = _storage._expireTimer { + } }() + try { if let v = _storage._expireTimer { try visitor.visitSingularUInt32Field(value: v, fieldNumber: 5) - } - if let v = _storage._profileKey { + } }() + try { if let v = _storage._profileKey { try visitor.visitSingularBytesField(value: v, fieldNumber: 6) - } - if let v = _storage._timestamp { + } }() + try { if let v = _storage._timestamp { try visitor.visitSingularUInt64Field(value: v, fieldNumber: 7) - } - if let v = _storage._quote { + } }() + try { if let v = _storage._quote { try visitor.visitSingularMessageField(value: v, fieldNumber: 8) - } + } }() if !_storage._preview.isEmpty { try visitor.visitRepeatedMessageField(value: _storage._preview, fieldNumber: 10) } - if let v = _storage._reaction { + try { if let v = _storage._reaction { try visitor.visitSingularMessageField(value: v, fieldNumber: 11) - } - if let v = _storage._profile { + } }() + try { if let v = _storage._profile { try visitor.visitSingularMessageField(value: v, fieldNumber: 101) - } - if let v = _storage._openGroupInvitation { + } }() + try { if let v = _storage._openGroupInvitation { try visitor.visitSingularMessageField(value: v, fieldNumber: 102) - } - if let v = _storage._closedGroupControlMessage { + } }() + try { if let v = _storage._closedGroupControlMessage { try visitor.visitSingularMessageField(value: v, fieldNumber: 104) - } - if let v = _storage._syncTarget { + } }() + try { if let v = _storage._syncTarget { try visitor.visitSingularStringField(value: v, fieldNumber: 105) - } + } }() } try unknownFields.traverse(visitor: &visitor) } @@ -2394,15 +2504,19 @@ extension SessionProtos_DataMessage.Quote: SwiftProtobuf.Message, SwiftProtobuf. } func traverse(visitor: inout V) throws { - if let v = self._id { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._id { try visitor.visitSingularUInt64Field(value: v, fieldNumber: 1) - } - if let v = self._author { + } }() + try { if let v = self._author { try visitor.visitSingularStringField(value: v, fieldNumber: 2) - } - if let v = self._text { + } }() + try { if let v = self._text { try visitor.visitSingularStringField(value: v, fieldNumber: 3) - } + } }() if !self.attachments.isEmpty { try visitor.visitRepeatedMessageField(value: self.attachments, fieldNumber: 4) } @@ -2449,18 +2563,22 @@ extension SessionProtos_DataMessage.Quote.QuotedAttachment: SwiftProtobuf.Messag } func traverse(visitor: inout V) throws { - if let v = self._contentType { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._contentType { try visitor.visitSingularStringField(value: v, fieldNumber: 1) - } - if let v = self._fileName { + } }() + try { if let v = self._fileName { try visitor.visitSingularStringField(value: v, fieldNumber: 2) - } - if let v = self._thumbnail { + } }() + try { if let v = self._thumbnail { try visitor.visitSingularMessageField(value: v, fieldNumber: 3) - } - if let v = self._flags { + } }() + try { if let v = self._flags { try visitor.visitSingularUInt32Field(value: v, fieldNumber: 4) - } + } }() try unknownFields.traverse(visitor: &visitor) } @@ -2509,15 +2627,19 @@ extension SessionProtos_DataMessage.Preview: SwiftProtobuf.Message, SwiftProtobu } func traverse(visitor: inout V) throws { - if let v = self._url { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._url { try visitor.visitSingularStringField(value: v, fieldNumber: 1) - } - if let v = self._title { + } }() + try { if let v = self._title { try visitor.visitSingularStringField(value: v, fieldNumber: 2) - } - if let v = self._image { + } }() + try { if let v = self._image { try visitor.visitSingularMessageField(value: v, fieldNumber: 3) - } + } }() try unknownFields.traverse(visitor: &visitor) } @@ -2562,18 +2684,22 @@ extension SessionProtos_DataMessage.Reaction: SwiftProtobuf.Message, SwiftProtob } func traverse(visitor: inout V) throws { - if let v = self._id { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._id { try visitor.visitSingularUInt64Field(value: v, fieldNumber: 1) - } - if let v = self._author { + } }() + try { if let v = self._author { try visitor.visitSingularStringField(value: v, fieldNumber: 2) - } - if let v = self._emoji { + } }() + try { if let v = self._emoji { try visitor.visitSingularStringField(value: v, fieldNumber: 3) - } - if let v = self._action { + } }() + try { if let v = self._action { try visitor.visitSingularEnumField(value: v, fieldNumber: 4) - } + } }() try unknownFields.traverse(visitor: &visitor) } @@ -2594,44 +2720,6 @@ extension SessionProtos_DataMessage.Reaction.Action: SwiftProtobuf._ProtoNamePro ] } -extension SessionProtos_DataMessage.LokiProfile: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { - static let protoMessageName: String = SessionProtos_DataMessage.protoMessageName + ".LokiProfile" - static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "displayName"), - 2: .same(proto: "profilePicture"), - ] - - mutating func decodeMessage(decoder: inout D) throws { - while let fieldNumber = try decoder.nextFieldNumber() { - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch fieldNumber { - case 1: try { try decoder.decodeSingularStringField(value: &self._displayName) }() - case 2: try { try decoder.decodeSingularStringField(value: &self._profilePicture) }() - default: break - } - } - } - - func traverse(visitor: inout V) throws { - if let v = self._displayName { - try visitor.visitSingularStringField(value: v, fieldNumber: 1) - } - if let v = self._profilePicture { - try visitor.visitSingularStringField(value: v, fieldNumber: 2) - } - try unknownFields.traverse(visitor: &visitor) - } - - static func ==(lhs: SessionProtos_DataMessage.LokiProfile, rhs: SessionProtos_DataMessage.LokiProfile) -> Bool { - if lhs._displayName != rhs._displayName {return false} - if lhs._profilePicture != rhs._profilePicture {return false} - if lhs.unknownFields != rhs.unknownFields {return false} - return true - } -} - extension SessionProtos_DataMessage.OpenGroupInvitation: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = SessionProtos_DataMessage.protoMessageName + ".OpenGroupInvitation" static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ @@ -2659,12 +2747,16 @@ extension SessionProtos_DataMessage.OpenGroupInvitation: SwiftProtobuf.Message, } func traverse(visitor: inout V) throws { - if let v = self._url { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._url { try visitor.visitSingularStringField(value: v, fieldNumber: 1) - } - if let v = self._name { + } }() + try { if let v = self._name { try visitor.visitSingularStringField(value: v, fieldNumber: 3) - } + } }() try unknownFields.traverse(visitor: &visitor) } @@ -2716,18 +2808,22 @@ extension SessionProtos_DataMessage.ClosedGroupControlMessage: SwiftProtobuf.Mes } func traverse(visitor: inout V) throws { - if let v = self._type { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._type { try visitor.visitSingularEnumField(value: v, fieldNumber: 1) - } - if let v = self._publicKey { + } }() + try { if let v = self._publicKey { try visitor.visitSingularBytesField(value: v, fieldNumber: 2) - } - if let v = self._name { + } }() + try { if let v = self._name { try visitor.visitSingularStringField(value: v, fieldNumber: 3) - } - if let v = self._encryptionKeyPair { + } }() + try { if let v = self._encryptionKeyPair { try visitor.visitSingularMessageField(value: v, fieldNumber: 4) - } + } }() if !self.members.isEmpty { try visitor.visitRepeatedBytesField(value: self.members, fieldNumber: 5) } @@ -2737,9 +2833,9 @@ extension SessionProtos_DataMessage.ClosedGroupControlMessage: SwiftProtobuf.Mes if !self.wrappers.isEmpty { try visitor.visitRepeatedMessageField(value: self.wrappers, fieldNumber: 7) } - if let v = self._expirationTimer { + try { if let v = self._expirationTimer { try visitor.visitSingularUInt32Field(value: v, fieldNumber: 8) - } + } }() try unknownFields.traverse(visitor: &visitor) } @@ -2796,12 +2892,16 @@ extension SessionProtos_DataMessage.ClosedGroupControlMessage.KeyPairWrapper: Sw } func traverse(visitor: inout V) throws { - if let v = self._publicKey { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._publicKey { try visitor.visitSingularBytesField(value: v, fieldNumber: 1) - } - if let v = self._encryptedKeyPair { + } }() + try { if let v = self._encryptedKeyPair { try visitor.visitSingularBytesField(value: v, fieldNumber: 2) - } + } }() try unknownFields.traverse(visitor: &visitor) } @@ -2848,21 +2948,25 @@ extension SessionProtos_ConfigurationMessage: SwiftProtobuf.Message, SwiftProtob } func traverse(visitor: inout V) throws { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 if !self.closedGroups.isEmpty { try visitor.visitRepeatedMessageField(value: self.closedGroups, fieldNumber: 1) } if !self.openGroups.isEmpty { try visitor.visitRepeatedStringField(value: self.openGroups, fieldNumber: 2) } - if let v = self._displayName { + try { if let v = self._displayName { try visitor.visitSingularStringField(value: v, fieldNumber: 3) - } - if let v = self._profilePicture { + } }() + try { if let v = self._profilePicture { try visitor.visitSingularStringField(value: v, fieldNumber: 4) - } - if let v = self._profileKey { + } }() + try { if let v = self._profileKey { try visitor.visitSingularBytesField(value: v, fieldNumber: 5) - } + } }() if !self.contacts.isEmpty { try visitor.visitRepeatedMessageField(value: self.contacts, fieldNumber: 6) } @@ -2915,24 +3019,28 @@ extension SessionProtos_ConfigurationMessage.ClosedGroup: SwiftProtobuf.Message, } func traverse(visitor: inout V) throws { - if let v = self._publicKey { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._publicKey { try visitor.visitSingularBytesField(value: v, fieldNumber: 1) - } - if let v = self._name { + } }() + try { if let v = self._name { try visitor.visitSingularStringField(value: v, fieldNumber: 2) - } - if let v = self._encryptionKeyPair { + } }() + try { if let v = self._encryptionKeyPair { try visitor.visitSingularMessageField(value: v, fieldNumber: 3) - } + } }() if !self.members.isEmpty { try visitor.visitRepeatedBytesField(value: self.members, fieldNumber: 4) } if !self.admins.isEmpty { try visitor.visitRepeatedBytesField(value: self.admins, fieldNumber: 5) } - if let v = self._expirationTimer { + try { if let v = self._expirationTimer { try visitor.visitSingularUInt32Field(value: v, fieldNumber: 6) - } + } }() try unknownFields.traverse(visitor: &visitor) } @@ -2985,27 +3093,31 @@ extension SessionProtos_ConfigurationMessage.Contact: SwiftProtobuf.Message, Swi } func traverse(visitor: inout V) throws { - if let v = self._publicKey { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._publicKey { try visitor.visitSingularBytesField(value: v, fieldNumber: 1) - } - if let v = self._name { + } }() + try { if let v = self._name { try visitor.visitSingularStringField(value: v, fieldNumber: 2) - } - if let v = self._profilePicture { + } }() + try { if let v = self._profilePicture { try visitor.visitSingularStringField(value: v, fieldNumber: 3) - } - if let v = self._profileKey { + } }() + try { if let v = self._profileKey { try visitor.visitSingularBytesField(value: v, fieldNumber: 4) - } - if let v = self._isApproved { + } }() + try { if let v = self._isApproved { try visitor.visitSingularBoolField(value: v, fieldNumber: 5) - } - if let v = self._isBlocked { + } }() + try { if let v = self._isBlocked { try visitor.visitSingularBoolField(value: v, fieldNumber: 6) - } - if let v = self._didApproveMe { + } }() + try { if let v = self._didApproveMe { try visitor.visitSingularBoolField(value: v, fieldNumber: 7) - } + } }() try unknownFields.traverse(visitor: &visitor) } @@ -3048,9 +3160,13 @@ extension SessionProtos_ReceiptMessage: SwiftProtobuf.Message, SwiftProtobuf._Me } func traverse(visitor: inout V) throws { - if let v = self._type { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._type { try visitor.visitSingularEnumField(value: v, fieldNumber: 1) - } + } }() if !self.timestamp.isEmpty { try visitor.visitRepeatedUInt64Field(value: self.timestamp, fieldNumber: 2) } @@ -3118,42 +3234,46 @@ extension SessionProtos_AttachmentPointer: SwiftProtobuf.Message, SwiftProtobuf. } func traverse(visitor: inout V) throws { - if let v = self._id { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._id { try visitor.visitSingularFixed64Field(value: v, fieldNumber: 1) - } - if let v = self._contentType { + } }() + try { if let v = self._contentType { try visitor.visitSingularStringField(value: v, fieldNumber: 2) - } - if let v = self._key { + } }() + try { if let v = self._key { try visitor.visitSingularBytesField(value: v, fieldNumber: 3) - } - if let v = self._size { + } }() + try { if let v = self._size { try visitor.visitSingularUInt32Field(value: v, fieldNumber: 4) - } - if let v = self._thumbnail { + } }() + try { if let v = self._thumbnail { try visitor.visitSingularBytesField(value: v, fieldNumber: 5) - } - if let v = self._digest { + } }() + try { if let v = self._digest { try visitor.visitSingularBytesField(value: v, fieldNumber: 6) - } - if let v = self._fileName { + } }() + try { if let v = self._fileName { try visitor.visitSingularStringField(value: v, fieldNumber: 7) - } - if let v = self._flags { + } }() + try { if let v = self._flags { try visitor.visitSingularUInt32Field(value: v, fieldNumber: 8) - } - if let v = self._width { + } }() + try { if let v = self._width { try visitor.visitSingularUInt32Field(value: v, fieldNumber: 9) - } - if let v = self._height { + } }() + try { if let v = self._height { try visitor.visitSingularUInt32Field(value: v, fieldNumber: 10) - } - if let v = self._caption { + } }() + try { if let v = self._caption { try visitor.visitSingularStringField(value: v, fieldNumber: 11) - } - if let v = self._url { + } }() + try { if let v = self._url { try visitor.visitSingularStringField(value: v, fieldNumber: 101) - } + } }() try unknownFields.traverse(visitor: &visitor) } @@ -3250,21 +3370,25 @@ extension SessionProtos_GroupContext: SwiftProtobuf.Message, SwiftProtobuf._Mess func traverse(visitor: inout V) throws { try withExtendedLifetime(_storage) { (_storage: _StorageClass) in - if let v = _storage._id { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = _storage._id { try visitor.visitSingularBytesField(value: v, fieldNumber: 1) - } - if let v = _storage._type { + } }() + try { if let v = _storage._type { try visitor.visitSingularEnumField(value: v, fieldNumber: 2) - } - if let v = _storage._name { + } }() + try { if let v = _storage._name { try visitor.visitSingularStringField(value: v, fieldNumber: 3) - } + } }() if !_storage._members.isEmpty { try visitor.visitRepeatedStringField(value: _storage._members, fieldNumber: 4) } - if let v = _storage._avatar { + try { if let v = _storage._avatar { try visitor.visitSingularMessageField(value: v, fieldNumber: 5) - } + } }() if !_storage._admins.isEmpty { try visitor.visitRepeatedStringField(value: _storage._admins, fieldNumber: 6) } diff --git a/SessionMessagingKit/Protos/Generated/WebSocketResources.pb.swift b/SessionMessagingKit/Protos/Generated/WebSocketResources.pb.swift index 4c6491fa2..737e40ce6 100644 --- a/SessionMessagingKit/Protos/Generated/WebSocketResources.pb.swift +++ b/SessionMessagingKit/Protos/Generated/WebSocketResources.pb.swift @@ -249,18 +249,22 @@ extension WebSocketProtos_WebSocketRequestMessage: SwiftProtobuf.Message, SwiftP } func traverse(visitor: inout V) throws { - if let v = self._verb { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._verb { try visitor.visitSingularStringField(value: v, fieldNumber: 1) - } - if let v = self._path { + } }() + try { if let v = self._path { try visitor.visitSingularStringField(value: v, fieldNumber: 2) - } - if let v = self._body { + } }() + try { if let v = self._body { try visitor.visitSingularBytesField(value: v, fieldNumber: 3) - } - if let v = self._requestID { + } }() + try { if let v = self._requestID { try visitor.visitSingularUInt64Field(value: v, fieldNumber: 4) - } + } }() if !self.headers.isEmpty { try visitor.visitRepeatedStringField(value: self.headers, fieldNumber: 5) } @@ -305,18 +309,22 @@ extension WebSocketProtos_WebSocketResponseMessage: SwiftProtobuf.Message, Swift } func traverse(visitor: inout V) throws { - if let v = self._requestID { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._requestID { try visitor.visitSingularUInt64Field(value: v, fieldNumber: 1) - } - if let v = self._status { + } }() + try { if let v = self._status { try visitor.visitSingularUInt32Field(value: v, fieldNumber: 2) - } - if let v = self._message { + } }() + try { if let v = self._message { try visitor.visitSingularStringField(value: v, fieldNumber: 3) - } - if let v = self._body { + } }() + try { if let v = self._body { try visitor.visitSingularBytesField(value: v, fieldNumber: 4) - } + } }() if !self.headers.isEmpty { try visitor.visitRepeatedStringField(value: self.headers, fieldNumber: 5) } @@ -357,15 +365,19 @@ extension WebSocketProtos_WebSocketMessage: SwiftProtobuf.Message, SwiftProtobuf } func traverse(visitor: inout V) throws { - if let v = self._type { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._type { try visitor.visitSingularEnumField(value: v, fieldNumber: 1) - } - if let v = self._request { + } }() + try { if let v = self._request { try visitor.visitSingularMessageField(value: v, fieldNumber: 2) - } - if let v = self._response { + } }() + try { if let v = self._response { try visitor.visitSingularMessageField(value: v, fieldNumber: 3) - } + } }() try unknownFields.traverse(visitor: &visitor) } diff --git a/SessionMessagingKit/Protos/SessionProtos.proto b/SessionMessagingKit/Protos/SessionProtos.proto index 88f1fc115..b642c5270 100644 --- a/SessionMessagingKit/Protos/SessionProtos.proto +++ b/SessionMessagingKit/Protos/SessionProtos.proto @@ -43,7 +43,9 @@ message UnsendRequest { message MessageRequestResponse { // @required - required bool isApproved = 1; // Whether the request was approved + required bool isApproved = 1; // Whether the request was approved + optional bytes profileKey = 2; + optional LokiProfile profile = 3; } message Content { @@ -98,6 +100,11 @@ message DataExtractionNotification { optional uint64 timestamp = 2; } +message LokiProfile { + optional string displayName = 1; + optional string profilePicture = 2; +} + message DataMessage { enum Flags { @@ -147,11 +154,6 @@ message DataMessage { required Action action = 4; } - message LokiProfile { - optional string displayName = 1; - optional string profilePicture = 2; - } - message OpenGroupInvitation { // @required required string url = 1; diff --git a/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+MessageRequests.swift b/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+MessageRequests.swift index 782d288a7..d16adbe95 100644 --- a/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+MessageRequests.swift +++ b/SessionMessagingKit/Sending & Receiving/Message Handling/MessageReceiver+MessageRequests.swift @@ -2,6 +2,7 @@ import Foundation import GRDB +import SignalCoreKit import SessionUtilitiesKit extension MessageReceiver { @@ -17,6 +18,24 @@ extension MessageReceiver { guard message.sender != userPublicKey else { return } guard let senderId: String = message.sender else { return } + // Update profile if needed (want to do this regardless of whether the message exists or + // not to ensure the profile info gets sync between a users devices at every chance) + if let profile = message.profile { + var contactProfileKey: OWSAES256Key? = nil + let messageSentTimestamp: TimeInterval = (TimeInterval(message.sentTimestamp ?? 0) / 1000) + + if let profileKey = profile.profileKey { contactProfileKey = OWSAES256Key(data: profileKey) } + + try MessageReceiver.updateProfileIfNeeded( + db, + publicKey: senderId, + name: profile.displayName, + profilePictureUrl: profile.profilePictureUrl, + profileKey: contactProfileKey, + sentTimestamp: messageSentTimestamp + ) + } + // Prep the unblinded thread let unblindedThread: SessionThread = try SessionThread.fetchOrCreate(db, id: senderId, variant: .contact) diff --git a/SessionMessagingKit/Sending & Receiving/MessageSender.swift b/SessionMessagingKit/Sending & Receiving/MessageSender.swift index c9b854a69..ebd8e7ff9 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageSender.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageSender.swift @@ -109,18 +109,18 @@ public final class MessageSender { } // Attach the user's profile if needed - if let message: VisibleMessage = message as? VisibleMessage { + if var messageWithProfile: MessageWithProfile = message as? MessageWithProfile { let profile: Profile = Profile.fetchOrCreateCurrentUser(db) if let profileKey: Data = profile.profileEncryptionKey?.keyData, let profilePictureUrl: String = profile.profilePictureUrl { - message.profile = VisibleMessage.VMProfile( + messageWithProfile.profile = VisibleMessage.VMProfile( displayName: profile.name, profileKey: profileKey, profilePictureUrl: profilePictureUrl ) } else { - message.profile = VisibleMessage.VMProfile(displayName: profile.name) + messageWithProfile.profile = VisibleMessage.VMProfile(displayName: profile.name) } } diff --git a/SessionMessagingKitTests/Open Groups/Models/OpenGroupSpec.swift b/SessionMessagingKitTests/Open Groups/Models/OpenGroupSpec.swift index 2b8e7c858..b6083b47a 100644 --- a/SessionMessagingKitTests/Open Groups/Models/OpenGroupSpec.swift +++ b/SessionMessagingKitTests/Open Groups/Models/OpenGroupSpec.swift @@ -76,7 +76,7 @@ class OpenGroupSpec: QuickSpec { ) expect(openGroup.debugDescription) - .to(equal("OpenGroup(server: \"server\", roomToken: \"room\", id: \"server.room\", publicKey: \"1234\", isActive: true, name: \"name\", roomDescription: null, imageId: null, userCount: 0, infoUpdates: 0, sequenceNumber: 0, inboxLatestMessageId: 0, outboxLatestMessageId: 0, pollFailureCount: 0)")) + .to(equal("OpenGroup(server: \"server\", roomToken: \"room\", id: \"server.room\", publicKey: \"1234\", isActive: true, name: \"name\", roomDescription: null, imageId: null, userCount: 0, infoUpdates: 0, sequenceNumber: 0, inboxLatestMessageId: 0, outboxLatestMessageId: 0, pollFailureCount: 0, permissions: ---)")) } } } diff --git a/SessionTests/Conversations/Settings/ThreadDisappearingMessagesViewModelSpec.swift b/SessionTests/Conversations/Settings/ThreadDisappearingMessagesViewModelSpec.swift index a8edcd510..582eb3f75 100644 --- a/SessionTests/Conversations/Settings/ThreadDisappearingMessagesViewModelSpec.swift +++ b/SessionTests/Conversations/Settings/ThreadDisappearingMessagesViewModelSpec.swift @@ -1,427 +1,253 @@ -//// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. -// -//import Combine -//import Quick -//import Nimble -// -//@testable import Session -// -//class ThreadDisappearingMessagesViewModelSpec: import Combine { -// typealias Item = ConversationDisappearingMessagesViewModel.Item -// -// var disposables: Set! -// var dataChangedCallbackTriggered: Bool = false -// var thread: TSThread! -// var config: OWSDisappearingMessagesConfiguration! -// var contact: Contact! -// var defaultItems: [ConversationDisappearingMessagesViewModel.Item]! -// var defaultItems: [Item]! -// var viewModel: ConversationDisappearingMessagesViewModel! -// -// // MARK: - Configuration -// -// override func setUpWithError() throws { -// dataChangedCallbackTriggered = false -// -// disposables = Set() -// thread = TSContactThread(uniqueId: "TestId") -// config = OWSDisappearingMessagesConfiguration(defaultWithThreadId: "TestId") -// contact = Contact(sessionID: "TestContactId") -// defaultItems = [ -// ConversationDisappearingMessagesViewModel.Item(id: 0, title: "Off", isActive: true), -// ConversationDisappearingMessagesViewModel.Item(id: 1, title: "5 seconds", isActive: false), -// ConversationDisappearingMessagesViewModel.Item(id: 2, title: "10 seconds", isActive: false), -// ConversationDisappearingMessagesViewModel.Item(id: 3, title: "30 seconds", isActive: false), -// ConversationDisappearingMessagesViewModel.Item(id: 4, title: "1 minute", isActive: false), -// ConversationDisappearingMessagesViewModel.Item(id: 5, title: "5 minutes", isActive: false), -// ConversationDisappearingMessagesViewModel.Item(id: 6, title: "30 minutes", isActive: false), -// ConversationDisappearingMessagesViewModel.Item(id: 7, title: "1 hour", isActive: false), -// ConversationDisappearingMessagesViewModel.Item(id: 8, title: "6 hours", isActive: false), -// ConversationDisappearingMessagesViewModel.Item(id: 9, title: "12 hours", isActive: false), -// ConversationDisappearingMessagesViewModel.Item(id: 10, title: "1 day", isActive: false), -// ConversationDisappearingMessagesViewModel.Item(id: 11, title: "1 week", isActive: false) -// Item(id: 0, title: "Off", isActive: true), -// Item(id: 1, title: "5 seconds", isActive: false), -// Item(id: 2, title: "10 seconds", isActive: false), -// Item(id: 3, title: "30 seconds", isActive: false), -// Item(id: 4, title: "1 minute", isActive: false), -// Item(id: 5, title: "5 minutes", isActive: false), -// Item(id: 6, title: "30 minutes", isActive: false), -// Item(id: 7, title: "1 hour", isActive: false), -// Item(id: 8, title: "6 hours", isActive: false), -// Item(id: 9, title: "12 hours", isActive: false), -// Item(id: 10, title: "1 day", isActive: false), -// Item(id: 11, title: "1 week", isActive: false) -// ] -// -// viewModel = ConversationDisappearingMessagesViewModel(thread: thread, disappearingMessagesConfiguration: config) { [weak self] in -// self?.dataChangedCallbackTriggered = true -// } -// } -// -// override func tearDownWithError() throws { -// disposables = nil -// dataChangedCallbackTriggered = false -// thread = nil -// config = nil -// contact = nil -// defaultItems = nil -// viewModel = nil -// } -// -// // MARK: - ConversationDisappearingMessagesViewModel.Item -// -// func testItDefaultsToTheExistingValuesWhenUpdatedWithNullValues() throws { -// var item: ConversationDisappearingMessagesViewModel.Item = ConversationDisappearingMessagesViewModel.Item( -// id: 1, -// title: "Test", -// isActive: true -// ) -// -// expect(item.isActive).to(beTrue()) -// -// item = item.with(isActive: nil) -// expect(item.isActive).to(beTrue()) -// -// item = item.with(isActive: false) -// expect(item.isActive).to(beFalse()) -// } -// -// // MARK: - Basic Tests -// -// func testItHasTheCorrectTitle() throws { -// expect(self.viewModel.title).to(equal("DISAPPEARING_MESSAGES_SETTINGS_TITLE".localized())) -// } -// -// func testItHasTheCorrectDescriptionForAGroup() throws { -// thread = TSGroupThread(uniqueId: "TestId1") -// config = OWSDisappearingMessagesConfiguration(defaultWithThreadId: "TestId1") -// viewModel = ConversationDisappearingMessagesViewModel(thread: thread, disappearingMessagesConfiguration: config) { [weak self] in -// self?.dataChangedCallbackTriggered = true -// } -// -// expect(self.viewModel.description) -// .to(equal( -// String(format: NSLocalizedString("When enabled, messages between you and %@ will disappear after they have been seen.", comment: ""), arguments: ["the group"]) -// )) -// } -// -// func testItHasTheCorrectDescriptionForAKnownContact() throws { -// var hasWrittenToStorage: Bool = false -// -// // TODO: Mock storage -// Storage.write { [weak self] transaction in -// guard let strongSelf = self else { return } -// -// Storage.shared.setContact(strongSelf.contact, using: transaction) -// -// // Need to do these after setting the contact to ensure it's picked up correctly -// strongSelf.thread = TSContactThread(contactSessionID: "TestContactId") -// strongSelf.config = OWSDisappearingMessagesConfiguration(defaultWithThreadId: (strongSelf.thread.uniqueId ?? "TestContactId")) -// strongSelf.viewModel = ConversationDisappearingMessagesViewModel(thread: strongSelf.thread, disappearingMessagesConfiguration: strongSelf.config) { -// self?.dataChangedCallbackTriggered = true -// } -// hasWrittenToStorage = true -// } -// -// // Note: We need this to ensure the test doesn't run before the subsequent 'expect' doesn't -// // run before the viewModel gets recreated in the 'Storage.write' -// expect(hasWrittenToStorage) -// .toEventually( -// beTrue(), -// timeout: .milliseconds(100) -// ) -// expect(self.viewModel.description) -// .toEventually( -// equal( -// String(format: NSLocalizedString("When enabled, messages between you and %@ will disappear after they have been seen.", comment: ""), arguments: ["anonymous"]) -// ), -// timeout: .milliseconds(100) -// ) -// } -// -// func testItHasTheCorrectDescriptionForAKnownContactWithADisplayName() throws { -// var hasWrittenToStorage: Bool = false -// contact.nickname = "TestName" -// -// // TODO: Mock storage -// Storage.write { [weak self] transaction in -// guard let strongSelf = self else { return } -// -// Storage.shared.setContact(strongSelf.contact, using: transaction) -// -// // Need to do these after setting the contact to ensure it's picked up correctly -// strongSelf.thread = TSContactThread(contactSessionID: "TestContactId") -// strongSelf.config = OWSDisappearingMessagesConfiguration(defaultWithThreadId: (strongSelf.thread.uniqueId ?? "TestContactId")) -// strongSelf.viewModel = ConversationDisappearingMessagesViewModel(thread: strongSelf.thread, disappearingMessagesConfiguration: strongSelf.config) { -// self?.dataChangedCallbackTriggered = true -// } -// hasWrittenToStorage = true -// } -// -// // Note: We need this to ensure the test doesn't run before the subsequent 'expect' doesn't -// // run before the viewModel gets recreated in the 'Storage.write' -// expect(hasWrittenToStorage) -// .toEventually( -// beTrue(), -// timeout: .milliseconds(100) -// ) -// expect(self.viewModel.description) -// .toEventually(equal( -// String(format: NSLocalizedString("When enabled, messages between you and %@ will disappear after they have been seen.", comment: ""), arguments: ["TestName"]) -// )) -// } -// -// func testItHasTheCorrectDescriptionForAnUnexpectedThreadType() throws { -// var hasWrittenToStorage: Bool = false -// contact.nickname = "TestName" -// -// // TODO: Mock storage -// Storage.write { [weak self] transaction in -// guard let strongSelf = self else { return } -// -// Storage.shared.setContact(strongSelf.contact, using: transaction) -// -// // Need to do these after setting the contact to ensure it's picked up correctly -// strongSelf.thread = TSThread(uniqueId: "TestId1") -// strongSelf.config = OWSDisappearingMessagesConfiguration(defaultWithThreadId: (strongSelf.thread.uniqueId ?? "TestId1")) -// strongSelf.viewModel = ConversationDisappearingMessagesViewModel(thread: strongSelf.thread, disappearingMessagesConfiguration: strongSelf.config) { -// self?.dataChangedCallbackTriggered = true -// } -// hasWrittenToStorage = true -// } -// -// // Note: We need this to ensure the test doesn't run before the subsequent 'expect' doesn't -// // run before the viewModel gets recreated in the 'Storage.write' -// expect(hasWrittenToStorage) -// .toEventually( -// beTrue(), -// timeout: .milliseconds(100) -// ) -// expect(self.viewModel.description) -// .toEventually(equal( -// String(format: NSLocalizedString("When enabled, messages between you and %@ will disappear after they have been seen.", comment: ""), arguments: ["anonymous"]) -// )) -// } -// -// func testItHasTheCorrectNumberOfItems() throws { -// expect(self.viewModel.items.value.count).to(equal(12)) -// expect(self.viewModel.items.newest) -// .toEventually( -// haveCount(12), -// timeout: .milliseconds(100) -// ) -// } -// -// func testItHasTheCorrectDefaultState() throws { -// expect(self.viewModel.items.value).to(equal(defaultItems)) -// expect(self.viewModel.items.newest) -// .toEventually( -// equal(defaultItems), -// timeout: .milliseconds(100) -// ) -// } -// -// -// func testItStartsWithTheCorrectItemActiveIfNotDefault() throws { -// config = OWSDisappearingMessagesConfiguration(defaultWithThreadId: "TestId1") -// config.isEnabled = true -// config.durationSeconds = 30 -// viewModel = ConversationDisappearingMessagesViewModel(thread: thread, disappearingMessagesConfiguration: config) { [weak self] in -// self?.dataChangedCallbackTriggered = true -// } -// -// var nonDefaultItems: [ConversationDisappearingMessagesViewModel.Item] = defaultItems -// nonDefaultItems[0] = nonDefaultItems[0].with(isActive: false) -// nonDefaultItems[3] = nonDefaultItems[3].with(isActive: true) -// expect(self.viewModel.items.value).to(equal(nonDefaultItems)) -// } -// -// // MARK: - Interactions -// -// func testItProvidesTheThreadAndGivenDataWhenAnInteractionOccurs() throws { -// var interactionThread: TSThread? = nil -// -// self.viewModel.interaction.on(0) { thread in -// interactionThread = thread -// } -// -// self.viewModel.interaction.tap(0) -// -// expect(interactionThread).to(equal(self.thread)) -// } -// -// func testItRefreshesTheDataCorrectly() throws { -// expect(self.viewModel.items.value.count).to(beGreaterThan(3)) -// expect(self.viewModel.items.value[3].id).to(equal(3)) -// expect(self.viewModel.items.value[3].isActive).to(beFalse()) -// -// config.isEnabled = true -// config.durationSeconds = 30 -// -// viewModel.tryRefreshData(for: 3) -// -// expect(self.viewModel.items.value[3].id).to(equal(3)) -// expect(self.viewModel.items.value[3].isActive).to(beTrue()) -// } -// -// func testItDoesNotSetAnItemToActiveIfTheConfigIsNotEnabled() throws { -// expect(self.viewModel.items.value.count).to(beGreaterThan(3)) -// expect(self.viewModel.items.value[3].id).to(equal(3)) -// expect(self.viewModel.items.value[3].isActive).to(beFalse()) -// var nonDefaultItems: [Item] = defaultItems -// nonDefaultItems[0] = Item(id: nonDefaultItems[0].id, title: nonDefaultItems[0].title, isActive: false) -// nonDefaultItems[3] = Item(id: nonDefaultItems[3].id, title: nonDefaultItems[3].title, isActive: true) -// -// config.durationSeconds = 30 -// -// viewModel.tryRefreshData(for: 3) -// -// expect(self.viewModel.items.value[3].id).to(equal(3)) -// expect(self.viewModel.items.value[3].isActive).to(beFalse()) -// expect(self.viewModel.items.newest) -// .toEventually( -// equal(nonDefaultItems), -// timeout: .milliseconds(100) -// ) -// } -// -// func testItUpdatesToADifferentValue() throws { -// expect(self.viewModel.items.value.count).to(beGreaterThan(3)) -// expect(self.viewModel.items.value[0].id).to(equal(0)) -// expect(self.viewModel.items.value[0].isActive).to(beTrue()) -// -// viewModel.interaction.tap(3) -// // MARK: - Interactions -// -// expect(self.viewModel.items.value[0].id) -// func testItSelectsTheItemCorrectly() throws { -// expect(self.viewModel.items.newest) -// .toEventually( -// equal(0), -// satisfyAllOf( -// haveCountGreaterThan(3), -// valueFor(\.id, at: 3, to: equal(3)), -// valueFor(\.isActive, at: 3, to: beFalse()) -// ), -// timeout: .milliseconds(100) -// ) -// expect(self.viewModel.items.value[0].isActive) -// -// viewModel.itemSelected.send(3) -// -// expect(self.viewModel.items.newest) -// .toEventually( -// beFalse(), -// satisfyAllOf( -// valueFor(\.id, at: 3, to: equal(3)), -// valueFor(\.isActive, at: 3, to: beTrue()) -// ), -// timeout: .milliseconds(100) -// ) -// expect(self.viewModel.items.value[3].id) -// } -// -// func testItUpdatesToADifferentValue() throws { -// expect(self.viewModel.items.newest) -// .toEventually( -// equal(3), -// satisfyAllOf( -// haveCountGreaterThan(3), -// valueFor(\.id, at: 0, to: equal(0)), -// valueFor(\.isActive, at: 0, to: beTrue()) -// ), -// timeout: .milliseconds(100) -// ) -// expect(self.viewModel.items.value[3].isActive) -// -// viewModel.itemSelected.send(3) -// -// expect(self.viewModel.items.newest) -// .toEventually( -// beTrue(), -// satisfyAllOf( -// valueFor(\.id, at: 0, to: equal(0)), -// valueFor(\.isActive, at: 0, to: beFalse()), -// valueFor(\.id, at: 3, to: equal(3)), -// valueFor(\.isActive, at: 3, to: beTrue()) -// ), -// timeout: .milliseconds(100) -// ) -// } -// -// -// func testItUpdatesTheConfigWhenChangingValue() throws { -// // Note: Default for 'durationSectionds' is OWSDisappearingMessagesConfigurationDefaultExpirationDuration -// // currently set to 86400 -// expect(self.config.isEnabled).to(beFalse()) -// expect(self.config.durationSeconds).to(equal(86400)) -// -// viewModel.interaction.tap(3) -// -// viewModel.items.sink(receiveValue: { _ in }).store(in: &disposables) -// viewModel.itemSelected.send(3) -// -// expect(self.config.isEnabled) -// .toEventually( -// beTrue(), -// timeout: .milliseconds(100) -// ) -// expect(self.config.durationSeconds) -// .toEventually( -// equal(30), -// timeout: .milliseconds(100) -// ) -// } -// -// -// func testItDisablesTheConfigWhenSetToZero() throws { -// config.isEnabled = true -// -// viewModel.interaction.tap(0) -// -// viewModel.items.sink(receiveValue: { _ in }).store(in: &disposables) -// viewModel.itemSelected.send(0) -// -// expect(self.config.isEnabled) -// .toEventually( -// beFalse(), -// timeout: .milliseconds(100) -// ) -// expect(self.config.durationSeconds) -// .toEventually( -// equal(0), -// timeout: .milliseconds(100) -// ) -// } -// -// -// func testItDoesNotSaveChangesIfTheConfigHasNotChangedFromItsDefaultState() { -// viewModel.trySaveChanges() -// -// -// // TODO: Mock out Storage.write -// expect(self.dataChangedCallbackTriggered) -// .toEventually( -// beFalse(), -// timeout: .milliseconds(100) -// ) -// } -// -// -// func testItDoesSaveChangesIfTheConfigHasChanged() { -// config.isEnabled = true -// config.durationSeconds = 30 -// -// -// viewModel.trySaveChanges() -// -// -// // TODO: Mock out Storage.write -// expect(self.dataChangedCallbackTriggered) -// .toEventually( -// beTrue(), -// timeout: .milliseconds(100) -// ) -// } -//} +// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. + +import Combine +import GRDB +import Quick +import Nimble + +@testable import Session + +class ThreadDisappearingMessagesViewModelSpec: QuickSpec { + typealias ParentType = SessionTableViewModel + + // MARK: - Spec + + override func spec() { + var mockStorage: Storage! + var dataChangeCancellable: AnyCancellable? + var otherCancellables: [AnyCancellable] = [] + var viewModel: ThreadDisappearingMessagesViewModel! + + describe("a ThreadDisappearingMessagesViewModel") { + // MARK: - Configuration + + beforeEach { + mockStorage = Storage( + customWriter: DatabaseQueue(), + customMigrations: [ + SNUtilitiesKit.migrations(), + SNSnodeKit.migrations(), + SNMessagingKit.migrations(), + SNUIKit.migrations() + ] + ) + mockStorage.write { db in + try SessionThread( + id: "TestId", + variant: .contact + ).insert(db) + } + viewModel = ThreadDisappearingMessagesViewModel( + storage: mockStorage, + scheduling: .immediate, + threadId: "TestId", + config: DisappearingMessagesConfiguration.defaultWith("TestId") + ) + dataChangeCancellable = viewModel.observableSettingsData + .receiveOnMain(immediately: true) + .sink( + receiveCompletion: { _ in }, + receiveValue: { viewModel.updateSettings($0) } + ) + } + + afterEach { + dataChangeCancellable?.cancel() + otherCancellables.forEach { $0.cancel() } + + mockStorage = nil + dataChangeCancellable = nil + otherCancellables = [] + viewModel = nil + } + + // MARK: - Basic Tests + + it("has the correct title") { + expect(viewModel.title).to(equal("DISAPPEARING_MESSAGES".localized())) + } + + it("has the correct number of items") { + expect(viewModel.settingsData.count) + .to(equal(1)) + expect(viewModel.settingsData.first?.elements.count) + .to(equal(12)) + } + + it("has the correct default state") { + expect(viewModel.settingsData.first?.elements.first) + .to( + equal( + SessionCell.Info( + id: ThreadDisappearingMessagesViewModel.Item( + title: "DISAPPEARING_MESSAGES_OFF".localized() + ), + title: "DISAPPEARING_MESSAGES_OFF".localized(), + rightAccessory: .radio( + isSelected: { true } + ) + ) + ) + ) + + let title: String = NSString.formatDurationSeconds( + UInt32(DisappearingMessagesConfiguration.validDurationsSeconds.last ?? -1), + useShortFormat: false + ) + expect(viewModel.settingsData.first?.elements.last) + .to( + equal( + SessionCell.Info( + id: ThreadDisappearingMessagesViewModel.Item(title: title), + title: title, + rightAccessory: .radio( + isSelected: { false } + ) + ) + ) + ) + } + + it("starts with the correct item active if not default") { + let config: DisappearingMessagesConfiguration = DisappearingMessagesConfiguration + .defaultWith("TestId") + .with( + isEnabled: true, + durationSeconds: DisappearingMessagesConfiguration.validDurationsSeconds.last + ) + mockStorage.write { db in + _ = try config.saved(db) + } + viewModel = ThreadDisappearingMessagesViewModel( + storage: mockStorage, + scheduling: .immediate, + threadId: "TestId", + config: config + ) + dataChangeCancellable = viewModel.observableSettingsData + .receiveOnMain(immediately: true) + .sink( + receiveCompletion: { _ in }, + receiveValue: { viewModel.updateSettings($0) } + ) + + expect(viewModel.settingsData.first?.elements.first) + .to( + equal( + SessionCell.Info( + id: ThreadDisappearingMessagesViewModel.Item( + title: "DISAPPEARING_MESSAGES_OFF".localized() + ), + title: "DISAPPEARING_MESSAGES_OFF".localized(), + rightAccessory: .radio( + isSelected: { false } + ) + ) + ) + ) + + let title: String = NSString.formatDurationSeconds( + UInt32(DisappearingMessagesConfiguration.validDurationsSeconds.last ?? -1), + useShortFormat: false + ) + expect(viewModel.settingsData.first?.elements.last) + .to( + equal( + SessionCell.Info( + id: ThreadDisappearingMessagesViewModel.Item(title: title), + title: title, + rightAccessory: .radio( + isSelected: { true } + ) + ) + ) + ) + } + + it("has no right bar button") { + var items: [ParentType.NavItem]? + + otherCancellables.append( + viewModel.rightNavItems + .receiveOnMain(immediately: true) + .sink( + receiveCompletion: { _ in }, + receiveValue: { navItems in items = navItems } + ) + ) + + expect(items).to(equal([])) + } + + context("when changed from the previous setting") { + var items: [ParentType.NavItem]? + + beforeEach { + otherCancellables.append( + viewModel.rightNavItems + .receiveOnMain(immediately: true) + .sink( + receiveCompletion: { _ in }, + receiveValue: { navItems in items = navItems } + ) + ) + + viewModel.settingsData.first?.elements.last?.onTap?(nil) + } + + it("shows the save button") { + expect(items) + .to(equal([ + ParentType.NavItem( + id: .save, + systemItem: .save, + accessibilityIdentifier: "Save button" + ) + ])) + } + + context("and saving") { + it("dismisses the screen") { + var didDismissScreen: Bool = false + + otherCancellables.append( + viewModel.dismissScreen + .receiveOnMain(immediately: true) + .sink( + receiveCompletion: { _ in }, + receiveValue: { _ in didDismissScreen = true } + ) + ) + + items?.first?.action?() + + expect(didDismissScreen) + .toEventually( + beTrue(), + timeout: .milliseconds(100) + ) + } + + it("saves the updated config") { + items?.first?.action?() + + let updatedConfig: DisappearingMessagesConfiguration? = mockStorage.read { db in + try DisappearingMessagesConfiguration.fetchOne(db, id: "TestId") + } + + expect(updatedConfig?.isEnabled) + .toEventually( + beTrue(), + timeout: .milliseconds(100) + ) + expect(updatedConfig?.durationSeconds) + .toEventually( + equal(DisappearingMessagesConfiguration.validDurationsSeconds.last ?? -1), + timeout: .milliseconds(100) + ) + } + } + } + } + } +} diff --git a/SessionTests/SessionTests.swift b/SessionTests/SessionTests.swift deleted file mode 100644 index 3c22a0ce3..000000000 --- a/SessionTests/SessionTests.swift +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. - -import XCTest - -class SessionTests: XCTestCase { - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() throws { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - // Any test you write for XCTest can be annotated as throws and async. - // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. - // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. - } - - func testPerformanceExample() throws { - // This is an example of a performance test case. - measure { - // Put the code you want to measure the time of here. - } - } - -} diff --git a/SessionTests/Settings/NotificationContentViewModelSpec.swift b/SessionTests/Settings/NotificationContentViewModelSpec.swift index e0979189e..bc6954881 100644 --- a/SessionTests/Settings/NotificationContentViewModelSpec.swift +++ b/SessionTests/Settings/NotificationContentViewModelSpec.swift @@ -13,6 +13,7 @@ class NotificationContentViewModelSpec: QuickSpec { override func spec() { var mockStorage: Storage! var dataChangeCancellable: AnyCancellable? + var dismissCancellable: AnyCancellable? var viewModel: NotificationContentViewModel! describe("a NotificationContentViewModel") { @@ -28,7 +29,7 @@ class NotificationContentViewModelSpec: QuickSpec { SNUIKit.migrations() ] ) - viewModel = NotificationContentViewModel(storage: mockStorage) + viewModel = NotificationContentViewModel(storage: mockStorage, scheduling: .immediate) dataChangeCancellable = viewModel.observableSettingsData .receiveOnMain(immediately: true) .sink( @@ -39,9 +40,11 @@ class NotificationContentViewModelSpec: QuickSpec { afterEach { dataChangeCancellable?.cancel() + dismissCancellable?.cancel() mockStorage = nil dataChangeCancellable = nil + dismissCancellable = nil viewModel = nil } @@ -53,53 +56,37 @@ class NotificationContentViewModelSpec: QuickSpec { it("has the correct number of items") { expect(viewModel.settingsData.count) - .toEventually( - equal(1), - timeout: .milliseconds(10) - ) + .to(equal(1)) expect(viewModel.settingsData.first?.elements.count) - .toEventually( - equal(3), - timeout: .milliseconds(10) - ) + .to(equal(3)) } it("has the correct default state") { - expect(viewModel.settingsData.first?.elements ) - .toEventually( + expect(viewModel.settingsData.first?.elements) + .to( equal([ - SettingInfo( + SessionCell.Info( id: Preferences.NotificationPreviewType.nameAndPreview, - title: "NOTIFICATIONS_SENDER_AND_MESSAGE".localized(), - action: .listSelection( - isSelected: { true }, - storedSelection: true, - shouldAutoSave: true, - selectValue: {} + title: "NOTIFICATIONS_STYLE_CONTENT_OPTION_NAME_AND_CONTENT".localized(), + rightAccessory: .radio( + isSelected: { true } ) ), - SettingInfo( + SessionCell.Info( id: Preferences.NotificationPreviewType.nameNoPreview, - title: "NOTIFICATIONS_SENDER_ONLY".localized(), - action: .listSelection( - isSelected: { false }, - storedSelection: false, - shouldAutoSave: true, - selectValue: {} + title: "NOTIFICATIONS_STYLE_CONTENT_OPTION_NAME_ONLY".localized(), + rightAccessory: .radio( + isSelected: { false } ) ), - SettingInfo( + SessionCell.Info( id: Preferences.NotificationPreviewType.noNameNoPreview, - title: "NOTIFICATIONS_NONE".localized(), - action: .listSelection( - isSelected: { false }, - storedSelection: false, - shouldAutoSave: true, - selectValue: {} + title: "NOTIFICATIONS_STYLE_CONTENT_OPTION_NO_NAME_OR_CONTENT".localized(), + rightAccessory: .radio( + isSelected: { false } ) ) - ]), - timeout: .milliseconds(10) + ]) ) } @@ -107,7 +94,7 @@ class NotificationContentViewModelSpec: QuickSpec { mockStorage.write { db in db[.preferencesNotificationPreviewType] = Preferences.NotificationPreviewType.nameNoPreview } - viewModel = NotificationContentViewModel(storage: mockStorage) + viewModel = NotificationContentViewModel(storage: mockStorage, scheduling: .immediate) dataChangeCancellable = viewModel.observableSettingsData .receiveOnMain(immediately: true) .sink( @@ -115,43 +102,56 @@ class NotificationContentViewModelSpec: QuickSpec { receiveValue: { viewModel.updateSettings($0) } ) - expect(viewModel.settingsData.first?.elements ) - .toEventually( + expect(viewModel.settingsData.first?.elements) + .to( equal([ - SettingInfo( + SessionCell.Info( id: Preferences.NotificationPreviewType.nameAndPreview, - title: "NOTIFICATIONS_SENDER_AND_MESSAGE".localized(), - action: .listSelection( - isSelected: { false }, - storedSelection: false, - shouldAutoSave: true, - selectValue: {} + title: "NOTIFICATIONS_STYLE_CONTENT_OPTION_NAME_AND_CONTENT".localized(), + rightAccessory: .radio( + isSelected: { false } ) ), - SettingInfo( + SessionCell.Info( id: Preferences.NotificationPreviewType.nameNoPreview, - title: "NOTIFICATIONS_SENDER_ONLY".localized(), - action: .listSelection( - isSelected: { true }, - storedSelection: true, - shouldAutoSave: true, - selectValue: {} + title: "NOTIFICATIONS_STYLE_CONTENT_OPTION_NAME_ONLY".localized(), + rightAccessory: .radio( + isSelected: { true } ) ), - SettingInfo( + SessionCell.Info( id: Preferences.NotificationPreviewType.noNameNoPreview, - title: "NOTIFICATIONS_NONE".localized(), - action: .listSelection( - isSelected: { false }, - storedSelection: false, - shouldAutoSave: true, - selectValue: {} + title: "NOTIFICATIONS_STYLE_CONTENT_OPTION_NO_NAME_OR_CONTENT".localized(), + rightAccessory: .radio( + isSelected: { false } ) ) - ]), - timeout: .milliseconds(10) + ]) ) } + + context("when tapping an item") { + it("updates the saved preference") { + viewModel.settingsData.first?.elements.last?.onTap?(nil) + + expect(mockStorage[.preferencesNotificationPreviewType]) + .to(equal(Preferences.NotificationPreviewType.noNameNoPreview)) + } + + it("dismisses the screen") { + var didDismissScreen: Bool = false + + dismissCancellable = viewModel.dismissScreen + .receiveOnMain(immediately: true) + .sink( + receiveCompletion: { _ in }, + receiveValue: { _ in didDismissScreen = true } + ) + viewModel.settingsData.first?.elements.last?.onTap?(nil) + + expect(didDismissScreen).to(beTrue()) + } + } } } } diff --git a/SessionUtilitiesKit/Database/Storage.swift b/SessionUtilitiesKit/Database/Storage.swift index ad4941d98..280b33a4b 100644 --- a/SessionUtilitiesKit/Database/Storage.swift +++ b/SessionUtilitiesKit/Database/Storage.swift @@ -26,6 +26,7 @@ public final class Storage { public static let shared: Storage = Storage() public private(set) var isValid: Bool = false public private(set) var hasCompletedMigrations: Bool = false + public static let defaultPublisherScheduler: ValueObservationScheduler = .async(onQueue: .main) fileprivate var dbWriter: DatabaseWriter? private var migrator: DatabaseMigrator? @@ -429,12 +430,15 @@ public extension Storage { // MARK: - Combine Extensions public extension ValueObservation { - func publisher(in storage: Storage) -> AnyPublisher { + func publisher( + in storage: Storage, + scheduling scheduler: ValueObservationScheduler = Storage.defaultPublisherScheduler + ) -> AnyPublisher { guard storage.isValid, let dbWriter: DatabaseWriter = storage.dbWriter else { return Fail(error: StorageError.databaseInvalid).eraseToAnyPublisher() } - - return self.publisher(in: dbWriter) + + return self.publisher(in: dbWriter, scheduling: scheduler) .eraseToAnyPublisher() } }