diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 868955165..0c4e4b32a 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -4147,7 +4147,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 124; + CURRENT_PROJECT_VERSION = 125; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = SUQ8J2PCT7; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -4209,7 +4209,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 124; + CURRENT_PROJECT_VERSION = 125; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = SUQ8J2PCT7; ENABLE_NS_ASSERTIONS = NO; @@ -4263,7 +4263,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 124; + CURRENT_PROJECT_VERSION = 125; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = SUQ8J2PCT7; @@ -4333,7 +4333,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 124; + CURRENT_PROJECT_VERSION = 125; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = SUQ8J2PCT7; @@ -4395,7 +4395,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 124; + CURRENT_PROJECT_VERSION = 125; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = SUQ8J2PCT7; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -4458,7 +4458,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 124; + CURRENT_PROJECT_VERSION = 125; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = SUQ8J2PCT7; ENABLE_NS_ASSERTIONS = NO; @@ -4659,7 +4659,7 @@ CODE_SIGN_ENTITLEMENTS = Signal/Signal.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 124; + CURRENT_PROJECT_VERSION = 125; DEVELOPMENT_TEAM = SUQ8J2PCT7; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -4726,7 +4726,7 @@ CODE_SIGN_ENTITLEMENTS = Signal/Signal.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 124; + CURRENT_PROJECT_VERSION = 125; DEVELOPMENT_TEAM = SUQ8J2PCT7; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", diff --git a/Signal/Images.xcassets/Loki V2/Mute.imageset/Contents.json b/Signal/Images.xcassets/Loki V2/Mute.imageset/Contents.json new file mode 100644 index 000000000..1c49e26bd --- /dev/null +++ b/Signal/Images.xcassets/Loki V2/Mute.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Mute.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Signal/Images.xcassets/Loki V2/Mute.imageset/Mute.pdf b/Signal/Images.xcassets/Loki V2/Mute.imageset/Mute.pdf new file mode 100644 index 000000000..771eb961b --- /dev/null +++ b/Signal/Images.xcassets/Loki V2/Mute.imageset/Mute.pdf @@ -0,0 +1,93 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 2.000000 1.933483 cm +0.000000 0.000000 0.000000 scn +15.313575 19.991774 m +15.556440 19.877506 15.713589 19.620409 15.713589 19.349028 c +15.713589 7.379672 l +8.227607 14.864090 l +14.556405 19.906073 l +14.770699 20.077473 15.056423 20.120321 15.313575 19.991774 c +h +1.218972 19.854357 m +19.791065 1.286150 l +20.069647 1.006199 20.069647 0.554850 19.791065 0.274897 c +19.651060 0.136351 19.468197 0.066362 19.285332 0.066362 c +19.102468 0.066362 18.919605 0.136351 18.779600 0.276327 c +15.713777 3.341509 l +15.713777 0.780523 l +15.713777 0.509142 15.556629 0.252045 15.313763 0.137779 c +15.213759 0.094929 15.099469 0.066362 14.999466 0.066362 c +14.842317 0.066362 14.685169 0.123497 14.556593 0.223478 c +7.599201 5.779656 l +4.284797 5.779656 l +3.499054 5.779656 2.856174 6.422402 2.856174 7.207980 c +2.856174 12.921274 l +2.856174 13.264072 2.984750 13.592587 3.199043 13.835402 c +3.456195 14.149633 3.841923 14.349598 4.284797 14.349598 c +4.703383 14.349598 l +0.208936 18.844532 l +-0.069645 19.123055 -0.069645 19.575834 0.208936 19.854357 c +0.487517 20.132879 0.940391 20.132879 1.218972 19.854357 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 1175 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 24.000000 24.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Type /Catalog + /Pages 5 0 R + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000001265 00000 n +0000001288 00000 n +0000001461 00000 n +0000001535 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +1594 +%%EOF \ No newline at end of file diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 9ef040390..7a80995d0 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -653,8 +653,6 @@ typedef enum : NSUInteger { [self createContents]; - [self registerCellClasses]; - [self createConversationScrollButtons]; [self createHeaderViews]; @@ -760,6 +758,8 @@ typedef enum : NSUInteger { [self.loadMoreHeader autoSetDimension:ALDimensionHeight toSize:kLoadMoreHeaderHeight]; SET_SUBVIEW_ACCESSIBILITY_IDENTIFIER(self, _loadMoreHeader); + [self registerCellClasses]; + [self updateShowLoadMoreHeader]; } @@ -986,7 +986,7 @@ typedef enum : NSUInteger { self.lastReloadDate = [NSDate new]; [self.conversationViewModel viewDidResetContentAndLayout]; [self.collectionView.collectionViewLayout invalidateLayout]; - [self.collectionView reloadData]; + [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]]; if (self.viewHasEverAppeared) { // Try to update the lastKnownDistanceFromBottom; the content size may have changed. diff --git a/Signal/src/ViewControllers/MessageDetailViewController.swift b/Signal/src/ViewControllers/MessageDetailViewController.swift index 293ba46ba..168bac7b2 100644 --- a/Signal/src/ViewControllers/MessageDetailViewController.swift +++ b/Signal/src/ViewControllers/MessageDetailViewController.swift @@ -235,84 +235,32 @@ class MessageDetailViewController: OWSViewController, MediaGalleryDataSourceDele // Recipient(s) if let outgoingMessage = message as? TSOutgoingMessage { - let isGroupThread = thread.isGroupThread() - - let recipientStatusGroups: [MessageReceiptStatus] = [ - .read, - .uploading, - .delivered, - .sent, - .sending, - .failed, - .skipped - ] - for recipientStatusGroup in recipientStatusGroups { - var groupRows = [UIView]() - - // TODO: It'd be nice to inset these dividers from the edge of the screen. - let addDivider = { - let divider = UIView() - divider.backgroundColor = Theme.hairlineColor - divider.autoSetDimension(.height, toSize: CGHairlineWidth()) - groupRows.append(divider) - } - - let messageRecipientIds = outgoingMessage.recipientIds() - - for recipientId in messageRecipientIds { - guard let recipientState = outgoingMessage.recipientState(forRecipientId: recipientId) else { - owsFailDebug("no message status for recipient: \(recipientId).") - continue - } - - // We use the "short" status message to avoid being redundant with the section title. - let (recipientStatus, shortStatusMessage, _) = MessageRecipientStatusUtils.recipientStatusAndStatusMessage(outgoingMessage: outgoingMessage, recipientState: recipientState) - - guard recipientStatus == recipientStatusGroup else { - continue - } - - if groupRows.count < 1 { - if isGroupThread { - groupRows.append(valueRow(name: string(for: recipientStatusGroup), - value: "")) - } - - addDivider() - } - - // We use ContactCellView, not ContactTableViewCell. - // Table view cells don't layout properly outside the - // context of a table view. - let cellView = ContactCellView() - if self.shouldShowUD, recipientState.wasSentByUD { - let udAccessoryView = self.buildUDAccessoryView(text: shortStatusMessage) - cellView.setAccessory(udAccessoryView) - } else { - cellView.accessoryMessage = shortStatusMessage - } - cellView.configure(withRecipientId: recipientId) - - let wrapper = UIView() - wrapper.layoutMargins = UIEdgeInsets(top: 8, left: 20, bottom: 8, right: 20) - wrapper.addSubview(cellView) - cellView.autoPinEdgesToSuperviewMargins() - groupRows.append(wrapper) - } + func getSeparator() -> UIView { + let result = UIView() + result.set(.height, to: Values.separatorThickness) + result.backgroundColor = Colors.separator + return result + } - if groupRows.count > 0 { - addDivider() + if !outgoingMessage.recipientIds().isEmpty { + rows += [ getSeparator() ] + } - let spacer = UIView() - spacer.autoSetDimension(.height, toSize: 10) - groupRows.append(spacer) - } + rows += outgoingMessage.recipientIds().flatMap { publicKey -> [UIView] in + // We use ContactCellView, not ContactTableViewCell. + // Table view cells don't layout properly outside the + // context of a table view. + let cellView = ContactCellView() + cellView.configure(withRecipientId: publicKey) + let wrapper = UIView() + wrapper.layoutMargins = UIEdgeInsets(top: 8, left: 20, bottom: 8, right: 20) + wrapper.addSubview(cellView) + cellView.autoPinEdgesToSuperviewMargins() + return [ wrapper, getSeparator() ] + } - Logger.verbose("\(groupRows.count) rows for \(recipientStatusGroup)") - guard groupRows.count > 0 else { - continue - } - rows += groupRows + if !outgoingMessage.recipientIds().isEmpty { + rows += [ UIView.vSpacer(10) ] } } diff --git a/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m b/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m index 68335302c..df26facd6 100644 --- a/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m +++ b/Signal/src/ViewControllers/ThreadSettings/OWSConversationSettingsViewController.m @@ -769,7 +769,7 @@ const CGFloat kIconViewLength = 24; cell.preservesSuperviewLayoutMargins = YES; cell.contentView.preservesSuperviewLayoutMargins = YES; - UIImageView *iconView = [strongSelf viewForIconWithName:@"table_ic_mute_thread"]; + UIImageView *iconView = [strongSelf viewForIconWithName:@"Mute"]; UILabel *rowLabel = [UILabel new]; rowLabel.text = NSLocalizedString(@"CONVERSATION_SETTINGS_MUTE_LABEL", @@ -832,24 +832,17 @@ const CGFloat kIconViewLength = 24; // Block Conversation section. if (!isNoteToSelf && [self.thread isKindOfClass:TSContactThread.class]) { - [mainSection addItem: - [OWSTableItem itemWithCustomCellBlock:^{ - NSString *title = @"Reset Secure Session"; - UIView *iconViewContainer = [UIView new]; - UIImageView *iconView = [[UIImageView alloc] initWithImage:[[UIImage imageNamed:@"system_message_security"] asTintedImageWithColor:LKColors.text]]; - [iconViewContainer addSubview:iconView]; - [iconViewContainer autoSetDimension:ALDimensionWidth toSize:kIconViewLength]; - [iconViewContainer autoSetDimension:ALDimensionHeight toSize:kIconViewLength]; - [iconView autoPinEdgeToSuperviewEdge:ALEdgeLeading]; - [iconView autoPinEdgeToSuperviewEdge:ALEdgeTop withInset:1]; - [iconView autoPinEdgeToSuperviewEdge:ALEdgeTrailing withInset:2]; - [iconView autoPinEdgeToSuperviewEdge:ALEdgeBottom withInset:1]; - [iconView autoSetDimension:ALDimensionWidth toSize:kIconViewLength - 2]; - [iconView autoSetDimension:ALDimensionHeight toSize:kIconViewLength - 2]; - return [weakSelf cellWithName:title iconView:iconViewContainer]; - } - actionBlock:^{ [weakSelf resetSecureSession]; }] - ]; + [mainSection addItem:[OWSTableItem + itemWithCustomCellBlock:^{ + return [weakSelf + disclosureCellWithName:@"Reset Secure Session" + iconName:@"system_message_security" + accessibilityIdentifier:ACCESSIBILITY_IDENTIFIER_WITH_NAME( + OWSConversationSettingsViewController, @"reset_secure_ession")]; + } + actionBlock:^{ + [weakSelf resetSecureSession]; + }]]; mainSection.footerTitle = NSLocalizedString( @"BLOCK_USER_BEHAVIOR_EXPLANATION", @"An explanation of the consequences of blocking another user."); diff --git a/SignalMessaging/Loki/Redesign/Components/ProfilePictureView.swift b/SignalMessaging/Loki/Redesign/Components/ProfilePictureView.swift index 1192e05fb..91d3596c9 100644 --- a/SignalMessaging/Loki/Redesign/Components/ProfilePictureView.swift +++ b/SignalMessaging/Loki/Redesign/Components/ProfilePictureView.swift @@ -45,6 +45,15 @@ public final class ProfilePictureView : UIView { } // MARK: Updating + @objc(updateForContact:) + public func update(for publicKey: String) { + openGroupProfilePicture = nil + hexEncodedPublicKey = publicKey + additionalHexEncodedPublicKey = nil + isRSSFeed = false + update() + } + @objc(updateForThread:) public func update(for thread: TSThread) { openGroupProfilePicture = nil @@ -71,23 +80,21 @@ public final class ProfilePictureView : UIView { additionalHexEncodedPublicKey = randomUsers.count >= 2 ? randomUsers[1] : "" isRSSFeed = false } + update() } else { // A one-to-one chat - hexEncodedPublicKey = thread.contactIdentifier()! - additionalHexEncodedPublicKey = nil - isRSSFeed = false + update(for: thread.contactIdentifier()!) } - update() } @objc public func update() { AssertIsOnMainThread() - func getProfilePicture(of size: CGFloat, for hexEncodedPublicKey: String) -> UIImage? { - guard !hexEncodedPublicKey.isEmpty else { return nil } - if let profilePicture = OWSProfileManager.shared().profileAvatar(forRecipientId: hexEncodedPublicKey) { + func getProfilePicture(of size: CGFloat, for publicKey: String) -> UIImage? { + guard !publicKey.isEmpty else { return nil } + if let profilePicture = OWSProfileManager.shared().profileAvatar(forRecipientId: publicKey) { return profilePicture } else { - let displayName = OWSProfileManager.shared().profileNameForRecipient(withID: hexEncodedPublicKey) ?? hexEncodedPublicKey - return Identicon.generatePlaceholderIcon(seed: hexEncodedPublicKey, text: displayName, size: size) + let displayName = OWSProfileManager.shared().profileNameForRecipient(withID: publicKey) ?? publicKey + return Identicon.generatePlaceholderIcon(seed: publicKey, text: displayName, size: size) } } let size: CGFloat diff --git a/SignalMessaging/Views/ContactCellView.m b/SignalMessaging/Views/ContactCellView.m index f3d04abec..179d7a079 100644 --- a/SignalMessaging/Views/ContactCellView.m +++ b/SignalMessaging/Views/ContactCellView.m @@ -211,7 +211,11 @@ const CGFloat kContactCellAvatarTextMargin = 12; - (void)updateAvatar { - [self.profilePictureView updateForThread:self.thread]; + if (self.thread != nil) { + [self.profilePictureView updateForThread:self.thread]; + } else { + [self.profilePictureView updateForContact:self.recipientId]; + } } - (void)updateProfileName diff --git a/SignalServiceKit/src/Loki/Protocol/Closed Groups/SharedSenderKeysImplementation.swift b/SignalServiceKit/src/Loki/Protocol/Closed Groups/SharedSenderKeysImplementation.swift index 59e9d8cb5..b53b4725e 100644 --- a/SignalServiceKit/src/Loki/Protocol/Closed Groups/SharedSenderKeysImplementation.swift +++ b/SignalServiceKit/src/Loki/Protocol/Closed Groups/SharedSenderKeysImplementation.swift @@ -109,16 +109,19 @@ public final class SharedSenderKeysImplementation : NSObject { return ratchet } else { var currentKeyIndex = ratchet.keyIndex - var result = ratchet + var current = ratchet + var messageKeys: [String] = [] while currentKeyIndex < targetKeyIndex { do { - result = try step(result) - currentKeyIndex = result.keyIndex + current = try step(current) + messageKeys += current.messageKeys + currentKeyIndex = current.keyIndex } catch { print("[Loki] Couldn't step ratchet due to error: \(error).") throw error } } + let result = ClosedGroupRatchet(chainKey: current.chainKey, keyIndex: current.keyIndex, messageKeys: messageKeys) // Includes any skipped message keys Storage.setClosedGroupRatchet(for: groupPublicKey, senderPublicKey: senderPublicKey, ratchet: result, using: transaction) return result } diff --git a/SignalServiceKit/src/Loki/Push Notifications/LokiPushNotificationManager.swift b/SignalServiceKit/src/Loki/Push Notifications/LokiPushNotificationManager.swift index 2497225a4..faa163b52 100644 --- a/SignalServiceKit/src/Loki/Push Notifications/LokiPushNotificationManager.swift +++ b/SignalServiceKit/src/Loki/Push Notifications/LokiPushNotificationManager.swift @@ -29,7 +29,7 @@ public final class LokiPushNotificationManager : NSObject { let request = TSRequest(url: url, method: "POST", parameters: parameters) request.allHTTPHeaderFields = [ "Content-Type" : "application/json" ] let promise: Promise = OnionRequestAPI.sendOnionRequest(request, to: server, using: pnServerPublicKey).map2 { response in - guard let json = response as? JSON else { + guard let json = response["body"] as? JSON else { return print("[Loki] Couldn't unregister from push notifications.") } guard json["code"] as? Int != 0 else { @@ -69,7 +69,7 @@ public final class LokiPushNotificationManager : NSObject { let request = TSRequest(url: url, method: "POST", parameters: parameters) request.allHTTPHeaderFields = [ "Content-Type" : "application/json" ] let promise: Promise = OnionRequestAPI.sendOnionRequest(request, to: server, using: pnServerPublicKey).map2 { response in - guard let json = response as? JSON else { + guard let json = response["body"] as? JSON else { return print("[Loki] Couldn't register device token.") } guard json["code"] as? Int != 0 else { @@ -104,7 +104,7 @@ public final class LokiPushNotificationManager : NSObject { let request = TSRequest(url: url, method: "POST", parameters: parameters) request.allHTTPHeaderFields = [ "Content-Type" : "application/json" ] let promise = OnionRequestAPI.sendOnionRequest(request, to: server, using: pnServerPublicKey).map2 { response in - guard let json = response as? JSON else { + guard let json = response["body"] as? JSON else { return print("[Loki] Couldn't subscribe/unsubscribe closed group: \(closedGroupPublicKey).") } guard json["code"] as? Int != 0 else { @@ -125,7 +125,7 @@ public final class LokiPushNotificationManager : NSObject { let request = TSRequest(url: url, method: "POST", parameters: parameters) request.allHTTPHeaderFields = [ "Content-Type" : "application/json" ] let promise = OnionRequestAPI.sendOnionRequest(request, to: server, using: pnServerPublicKey).map2 { response in - guard let json = response as? JSON else { + guard let json = response["body"] as? JSON else { return print("[Loki] Couldn't notify PN server.") } guard json["code"] as? Int != 0 else { diff --git a/SignalServiceKit/src/Messages/TSGroupModel.m b/SignalServiceKit/src/Messages/TSGroupModel.m index 6157d55b4..f8bbdba35 100644 --- a/SignalServiceKit/src/Messages/TSGroupModel.m +++ b/SignalServiceKit/src/Messages/TSGroupModel.m @@ -117,9 +117,6 @@ const int32_t kGroupIdLength = 16; updatedGroupInfoString = [updatedGroupInfoString stringByAppendingString:NSLocalizedString(@"GROUP_AVATAR_CHANGED", @"")]; } - if ([updatedGroupInfoString length] == 0) { - updatedGroupInfoString = NSLocalizedString(@"GROUP_UPDATED", @""); - } NSSet *oldMembers = [NSSet setWithArray:_groupMemberIds]; NSSet *newMembers = [NSSet setWithArray:newModel.groupMemberIds]; @@ -169,7 +166,9 @@ const int32_t kGroupIdLength = 16; } } } - + if ([updatedGroupInfoString length] == 0) { + updatedGroupInfoString = NSLocalizedString(@"GROUP_UPDATED", @""); + } return updatedGroupInfoString; }