diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index e70d113f3..eaa0f30e1 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -6365,7 +6365,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 401; + CURRENT_PROJECT_VERSION = 404; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = SUQ8J2PCT7; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -6389,7 +6389,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.2.12; + MARKETING_VERSION = 2.2.13; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.ShareExtension"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -6437,7 +6437,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 401; + CURRENT_PROJECT_VERSION = 404; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = SUQ8J2PCT7; ENABLE_NS_ASSERTIONS = NO; @@ -6466,7 +6466,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.2.12; + MARKETING_VERSION = 2.2.13; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.ShareExtension"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -6502,7 +6502,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 401; + CURRENT_PROJECT_VERSION = 404; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = SUQ8J2PCT7; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; @@ -6525,7 +6525,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.2.12; + MARKETING_VERSION = 2.2.13; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.NotificationServiceExtension"; @@ -6576,7 +6576,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 401; + CURRENT_PROJECT_VERSION = 404; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = SUQ8J2PCT7; ENABLE_NS_ASSERTIONS = NO; @@ -6604,7 +6604,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 2.2.12; + MARKETING_VERSION = 2.2.13; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger.NotificationServiceExtension"; @@ -7484,7 +7484,7 @@ CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 401; + CURRENT_PROJECT_VERSION = 404; DEVELOPMENT_TEAM = SUQ8J2PCT7; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -7522,7 +7522,7 @@ "$(SRCROOT)", ); LLVM_LTO = NO; - MARKETING_VERSION = 2.2.12; + MARKETING_VERSION = 2.2.13; OTHER_LDFLAGS = "$(inherited)"; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger"; @@ -7555,7 +7555,7 @@ CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 401; + CURRENT_PROJECT_VERSION = 404; DEVELOPMENT_TEAM = SUQ8J2PCT7; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -7593,7 +7593,7 @@ "$(SRCROOT)", ); LLVM_LTO = NO; - MARKETING_VERSION = 2.2.12; + MARKETING_VERSION = 2.2.13; OTHER_LDFLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = "com.loki-project.loki-messenger"; PRODUCT_NAME = Session; diff --git a/Session/Conversations/Settings/ThreadDisappearingMessagesSettingsViewModel.swift b/Session/Conversations/Settings/ThreadDisappearingMessagesSettingsViewModel.swift index cce21bf56..26bb6e0e3 100644 --- a/Session/Conversations/Settings/ThreadDisappearingMessagesSettingsViewModel.swift +++ b/Session/Conversations/Settings/ThreadDisappearingMessagesSettingsViewModel.swift @@ -97,8 +97,13 @@ class ThreadDisappearingMessagesSettingsViewModel: SessionTableViewModel [SectionModel] in + private lazy var _observableSettingsData: ObservableData = ValueObservation + .trackingConstantRegion { [weak self, config, dependencies, threadId = self.threadId] db -> [SectionModel] in + let userPublicKey: String = getUserHexEncodedPublicKey(db, dependencies: dependencies) + let maybeThreadViewModel: SessionThreadViewModel? = try SessionThreadViewModel + .conversationSettingsQuery(threadId: threadId, userPublicKey: userPublicKey) + .fetchOne(db) + return [ SectionModel( model: .content, @@ -109,6 +114,10 @@ class ThreadDisappearingMessagesSettingsViewModel: SessionTableViewModel NSAppleMusicUsageDescription - Signal needs to use Apple Music to play media attachments. + Session needs to use Apple Music to play media attachments. NSCameraUsageDescription Session needs camera access to take pictures and scan QR codes. - NSContactsUsageDescription - Signal uses your contacts to find users you know. We do not store your contacts on the server. NSFaceIDUsageDescription Session's Screen Lock feature uses Face ID. NSHumanReadableCopyright com.loki-project.loki-messenger NSMicrophoneUsageDescription - Session needs access to your microphone to record media. + Session needs access to your microphone for calls and to send to audio messages. NSPhotoLibraryAddUsageDescription Session needs access to your library to save photos. NSPhotoLibraryUsageDescription - Session needs access to your library to send photos. + Session needs access to your library to update your avatar and send photos. PHPhotoLibraryPreventAutomaticLimitedAccessAlert UIAppFonts diff --git a/Session/Meta/Signal.entitlements b/Session/Meta/Signal.entitlements index 7be77767f..84526edd9 100644 --- a/Session/Meta/Signal.entitlements +++ b/Session/Meta/Signal.entitlements @@ -4,19 +4,6 @@ aps-environment production - com.apple.developer.icloud-container-identifiers - - iCloud.$(CFBundleIdentifier) - - com.apple.developer.icloud-services - - CloudDocuments - CloudKit - - com.apple.developer.ubiquity-container-identifiers - - iCloud.$(CFBundleIdentifier) - com.apple.security.application-groups group.com.loki-project.loki-messenger diff --git a/Session/Shared/Views/SessionCell+AccessoryView.swift b/Session/Shared/Views/SessionCell+AccessoryView.swift index 0aac489bd..fa73ab7f7 100644 --- a/Session/Shared/Views/SessionCell+AccessoryView.swift +++ b/Session/Shared/Views/SessionCell+AccessoryView.swift @@ -408,20 +408,34 @@ extension SessionCell { let wasOldSelection: Bool = (!isSelected && storedSelection) radioBorderView.isHidden = false - radioBorderView.themeBorderColor = (isSelected ? - .radioButton_selectedBorder : - .radioButton_unselectedBorder - ) + radioBorderView.themeBorderColor = { + guard isEnabled else { return .radioButton_disabledBorder } + + return (isSelected ? + .radioButton_selectedBorder : + .radioButton_unselectedBorder + ) + }() + radioBorderView.layer.cornerRadius = (size.borderSize / 2) radioView.accessibilityIdentifier = accessibility?.identifier radioView.accessibilityLabel = accessibility?.label radioView.alpha = (wasOldSelection ? 0.3 : 1) radioView.isHidden = (!isSelected && !storedSelection) - radioView.themeBackgroundColor = (isSelected || wasOldSelection ? - .radioButton_selectedBackground : - .radioButton_unselectedBackground - ) + radioView.themeBackgroundColor = { + guard isEnabled else { + return (isSelected || wasOldSelection ? + .radioButton_disabledSelectedBackground : + .radioButton_disabledUnselectedBackground + ) + } + + return (isSelected || wasOldSelection ? + .radioButton_selectedBackground : + .radioButton_unselectedBackground + ) + }() radioView.layer.cornerRadius = (size.selectionSize / 2) radioViewWidthConstraint.constant = size.selectionSize diff --git a/SessionMessagingKit/Shared Models/SessionThreadViewModel.swift b/SessionMessagingKit/Shared Models/SessionThreadViewModel.swift index f2585e182..817ff6582 100644 --- a/SessionMessagingKit/Shared Models/SessionThreadViewModel.swift +++ b/SessionMessagingKit/Shared Models/SessionThreadViewModel.swift @@ -639,11 +639,11 @@ public extension SessionThreadViewModel { WHERE ( \(recipientState[.interactionId]) = \(interaction[.id]) AND -- Ignore 'skipped' states - \(SQL("\(recipientState[.state]) = \(RecipientState.State.sending)")) + \(SQL("\(recipientState[.state]) != \(RecipientState.State.skipped)")) ) LIMIT 1 - ), 0) AS \(ViewModel.interactionStateKey), - + ), \(SQL("\(RecipientState.State.sending)"))) AS \(ViewModel.interactionStateKey), + (\(readReceiptTableLiteral).\(readReceiptReadTimestampMsColumnLiteral) IS NOT NULL) AS \(ViewModel.interactionHasAtLeastOneReadReceiptKey), (\(linkPreview[.url]) IS NOT NULL) AS \(ViewModel.interactionIsOpenGroupInvitationKey), @@ -967,10 +967,6 @@ public extension SessionThreadViewModel { let profileIdColumnLiteral: SQL = SQL(stringLiteral: Profile.Columns.id.name) - let groupMemberProfileIdColumnLiteral: SQL = SQL(stringLiteral: GroupMember.Columns.profileId.name) - let groupMemberRoleColumnLiteral: SQL = SQL(stringLiteral: GroupMember.Columns.role.name) - let groupMemberGroupIdColumnLiteral: SQL = SQL(stringLiteral: GroupMember.Columns.groupId.name) - /// **Note:** The `numColumnsBeforeProfiles` value **MUST** match the number of fields before /// the `ViewModel.contactProfileKey` entry below otherwise the query will fail to /// parse and might throw @@ -997,8 +993,27 @@ public extension SessionThreadViewModel { \(ViewModel.closedGroupProfileBackFallbackKey).*, \(closedGroup[.name]) AS \(ViewModel.closedGroupNameKey), - (\(ViewModel.currentUserIsClosedGroupMemberKey).profileId IS NOT NULL) AS \(ViewModel.currentUserIsClosedGroupMemberKey), - (\(ViewModel.currentUserIsClosedGroupAdminKey).profileId IS NOT NULL) AS \(ViewModel.currentUserIsClosedGroupAdminKey), + + EXISTS ( + SELECT 1 + FROM \(GroupMember.self) + WHERE ( + \(groupMember[.groupId]) = \(closedGroup[.threadId]) AND + \(SQL("\(groupMember[.role]) != \(GroupMember.Role.zombie)")) AND + \(SQL("\(groupMember[.profileId]) = \(userPublicKey)")) + ) + ) AS \(ViewModel.currentUserIsClosedGroupMemberKey), + + EXISTS ( + SELECT 1 + FROM \(GroupMember.self) + WHERE ( + \(groupMember[.groupId]) = \(closedGroup[.threadId]) AND + \(SQL("\(groupMember[.role]) = \(GroupMember.Role.admin)")) AND + \(SQL("\(groupMember[.profileId]) = \(userPublicKey)")) + ) + ) AS \(ViewModel.currentUserIsClosedGroupAdminKey), + \(openGroup[.name]) AS \(ViewModel.openGroupNameKey), \(openGroup[.server]) AS \(ViewModel.openGroupServerKey), \(openGroup[.roomToken]) AS \(ViewModel.openGroupRoomTokenKey), @@ -1012,16 +1027,6 @@ public extension SessionThreadViewModel { LEFT JOIN \(Profile.self) AS \(ViewModel.contactProfileKey) ON \(ViewModel.contactProfileKey).\(profileIdColumnLiteral) = \(thread[.id]) LEFT JOIN \(OpenGroup.self) ON \(openGroup[.threadId]) = \(thread[.id]) LEFT JOIN \(ClosedGroup.self) ON \(closedGroup[.threadId]) = \(thread[.id]) - LEFT JOIN \(GroupMember.self) AS \(ViewModel.currentUserIsClosedGroupMemberKey) ON ( - \(SQL("\(ViewModel.currentUserIsClosedGroupMemberKey).\(groupMemberRoleColumnLiteral) != \(GroupMember.Role.zombie)")) AND - \(ViewModel.currentUserIsClosedGroupMemberKey).\(groupMemberGroupIdColumnLiteral) = \(closedGroup[.threadId]) AND - \(SQL("\(ViewModel.currentUserIsClosedGroupMemberKey).\(groupMemberProfileIdColumnLiteral) = \(userPublicKey)")) - ) - LEFT JOIN \(GroupMember.self) AS \(ViewModel.currentUserIsClosedGroupAdminKey) ON ( - \(SQL("\(ViewModel.currentUserIsClosedGroupAdminKey).\(groupMemberRoleColumnLiteral) = \(GroupMember.Role.admin)")) AND - \(ViewModel.currentUserIsClosedGroupAdminKey).\(groupMemberGroupIdColumnLiteral) = \(closedGroup[.threadId]) AND - \(SQL("\(ViewModel.currentUserIsClosedGroupAdminKey).\(groupMemberProfileIdColumnLiteral) = \(userPublicKey)")) - ) LEFT JOIN \(Profile.self) AS \(ViewModel.closedGroupProfileFrontKey) ON ( \(ViewModel.closedGroupProfileFrontKey).\(profileIdColumnLiteral) = ( @@ -1029,8 +1034,8 @@ public extension SessionThreadViewModel { FROM \(GroupMember.self) JOIN \(Profile.self) ON \(profile[.id]) = \(groupMember[.profileId]) WHERE ( - \(SQL("\(groupMember[.role]) = \(GroupMember.Role.standard)")) AND \(groupMember[.groupId]) = \(closedGroup[.threadId]) AND + \(SQL("\(groupMember[.role]) = \(GroupMember.Role.standard)")) AND \(SQL("\(groupMember[.profileId]) != \(userPublicKey)")) ) ) @@ -1042,8 +1047,8 @@ public extension SessionThreadViewModel { FROM \(GroupMember.self) JOIN \(Profile.self) ON \(profile[.id]) = \(groupMember[.profileId]) WHERE ( - \(SQL("\(groupMember[.role]) = \(GroupMember.Role.standard)")) AND \(groupMember[.groupId]) = \(closedGroup[.threadId]) AND + \(SQL("\(groupMember[.role]) = \(GroupMember.Role.standard)")) AND \(SQL("\(groupMember[.profileId]) != \(userPublicKey)")) ) ) @@ -1683,6 +1688,7 @@ public extension SessionThreadViewModel { let profile: TypedTableAlias = TypedTableAlias() let interaction: TypedTableAlias = TypedTableAlias() + let aggregateInteractionLiteral: SQL = SQL(stringLiteral: "aggregateInteraction") let profileIdColumnLiteral: SQL = SQL(stringLiteral: Profile.Columns.id.name) /// **Note:** The `numColumnsBeforeProfiles` value **MUST** match the number of fields before @@ -1716,11 +1722,21 @@ public extension SessionThreadViewModel { FROM \(SessionThread.self) LEFT JOIN \(Contact.self) ON \(contact[.id]) = \(thread[.id]) + LEFT JOIN ( - SELECT \(interaction[.threadId]), MAX(\(interaction[.timestampMs])) + SELECT + \(interaction[.id]) AS \(ViewModel.interactionIdKey), + \(interaction[.threadId]) AS \(ViewModel.threadIdKey), + MAX(\(interaction[.timestampMs])) FROM \(Interaction.self) + WHERE \(SQL("\(interaction[.variant]) != \(Interaction.Variant.standardIncomingDeleted)")) GROUP BY \(interaction[.threadId]) - ) AS \(Interaction.self) ON \(interaction[.threadId]) = \(thread[.id]) + ) AS \(aggregateInteractionLiteral) ON \(aggregateInteractionLiteral).\(ViewModel.threadIdKey) = \(thread[.id]) + LEFT JOIN \(Interaction.self) ON ( + \(interaction[.threadId]) = \(thread[.id]) AND + \(interaction[.id]) = \(aggregateInteractionLiteral).\(ViewModel.interactionIdKey) + ) + LEFT JOIN \(Profile.self) AS \(ViewModel.contactProfileKey) ON \(ViewModel.contactProfileKey).\(profileIdColumnLiteral) = \(thread[.id]) LEFT JOIN \(ClosedGroup.self) ON \(closedGroup[.threadId]) = \(thread[.id]) LEFT JOIN \(OpenGroup.self) ON \(openGroup[.threadId]) = \(thread[.id]) diff --git a/SessionUIKit/Style Guide/Themes/Theme+ClassicDark.swift b/SessionUIKit/Style Guide/Themes/Theme+ClassicDark.swift index 34ca02df0..b903ca4bb 100644 --- a/SessionUIKit/Style Guide/Themes/Theme+ClassicDark.swift +++ b/SessionUIKit/Style Guide/Themes/Theme+ClassicDark.swift @@ -48,6 +48,9 @@ internal enum Theme_ClassicDark: ThemeColors { .radioButton_unselectedBackground: .clear, .radioButton_selectedBorder: .classicDark6, .radioButton_unselectedBorder: .classicDark6, + .radioButton_disabledSelectedBackground: .disabledDark, + .radioButton_disabledUnselectedBackground: .clear, + .radioButton_disabledBorder: .disabledDark, // SessionButton .sessionButton_text: .primary, diff --git a/SessionUIKit/Style Guide/Themes/Theme+ClassicLight.swift b/SessionUIKit/Style Guide/Themes/Theme+ClassicLight.swift index 317554827..a659d2905 100644 --- a/SessionUIKit/Style Guide/Themes/Theme+ClassicLight.swift +++ b/SessionUIKit/Style Guide/Themes/Theme+ClassicLight.swift @@ -48,6 +48,9 @@ internal enum Theme_ClassicLight: ThemeColors { .radioButton_unselectedBackground: .clear, .radioButton_selectedBorder: .classicLight0, .radioButton_unselectedBorder: .classicLight0, + .radioButton_disabledSelectedBackground: .disabledLight, + .radioButton_disabledUnselectedBackground: .clear, + .radioButton_disabledBorder: .disabledLight, // OutlineButton .sessionButton_text: .classicLight0, diff --git a/SessionUIKit/Style Guide/Themes/Theme+Colors.swift b/SessionUIKit/Style Guide/Themes/Theme+Colors.swift index 057157bfd..625a96eb2 100644 --- a/SessionUIKit/Style Guide/Themes/Theme+Colors.swift +++ b/SessionUIKit/Style Guide/Themes/Theme+Colors.swift @@ -44,8 +44,8 @@ internal extension UIColor { static let warning: UIColor = #colorLiteral(red: 0.9882352941, green: 0.6941176471, blue: 0.3490196078, alpha: 1) // #FCB159 static let dangerDark: UIColor = #colorLiteral(red: 1, green: 0.2274509804, blue: 0.2274509804, alpha: 1) // #FF3A3A static let dangerLight: UIColor = #colorLiteral(red: 0.8823529412, green: 0.1764705882, blue: 0.09803921569, alpha: 1) // #E12D19 - static let disabledDark: UIColor = #colorLiteral(red: 0.631372549, green: 0.6352941176, blue: 0.631372549, alpha: 1) // #A1A2A1 - static let disabledLight: UIColor = #colorLiteral(red: 0.4274509804, green: 0.4274509804, blue: 0.4274509804, alpha: 1) // #6D6D6D + static let disabledDark: UIColor = #colorLiteral(red: 0.4274509804, green: 0.4274509804, blue: 0.4274509804, alpha: 1 ) // #6D6D6D + static let disabledLight: UIColor = #colorLiteral(red: 0.631372549, green: 0.6352941176, blue: 0.631372549, alpha: 1) // #A1A2A1 static let black_06: UIColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 0.06) // #000000 static let pathConnected: UIColor = #colorLiteral(red: 0.1921568627, green: 0.9450980392, blue: 0.5882352941, alpha: 1) // #31F196 diff --git a/SessionUIKit/Style Guide/Themes/Theme+OceanDark.swift b/SessionUIKit/Style Guide/Themes/Theme+OceanDark.swift index 59cf2edea..9113250f5 100644 --- a/SessionUIKit/Style Guide/Themes/Theme+OceanDark.swift +++ b/SessionUIKit/Style Guide/Themes/Theme+OceanDark.swift @@ -48,6 +48,9 @@ internal enum Theme_OceanDark: ThemeColors { .radioButton_unselectedBackground: .clear, .radioButton_selectedBorder: .oceanDark7, .radioButton_unselectedBorder: .oceanDark7, + .radioButton_disabledSelectedBackground: .disabledDark, + .radioButton_disabledUnselectedBackground: .clear, + .radioButton_disabledBorder: .disabledDark, // SessionButton .sessionButton_text: .primary, diff --git a/SessionUIKit/Style Guide/Themes/Theme+OceanLight.swift b/SessionUIKit/Style Guide/Themes/Theme+OceanLight.swift index fbf9e1e9c..0210cc01f 100644 --- a/SessionUIKit/Style Guide/Themes/Theme+OceanLight.swift +++ b/SessionUIKit/Style Guide/Themes/Theme+OceanLight.swift @@ -48,6 +48,9 @@ internal enum Theme_OceanLight: ThemeColors { .radioButton_unselectedBackground: .clear, .radioButton_selectedBorder: .oceanLight1, .radioButton_unselectedBorder: .oceanLight3, + .radioButton_disabledSelectedBackground: .disabledLight, + .radioButton_disabledUnselectedBackground: .clear, + .radioButton_disabledBorder: .disabledLight, // SessionButton .sessionButton_text: .oceanLight1, diff --git a/SessionUIKit/Style Guide/Themes/Theme.swift b/SessionUIKit/Style Guide/Themes/Theme.swift index 84a28174d..e5c17e43a 100644 --- a/SessionUIKit/Style Guide/Themes/Theme.swift +++ b/SessionUIKit/Style Guide/Themes/Theme.swift @@ -136,6 +136,9 @@ public indirect enum ThemeValue: Hashable { case radioButton_unselectedBackground case radioButton_selectedBorder case radioButton_unselectedBorder + case radioButton_disabledSelectedBackground + case radioButton_disabledUnselectedBackground + case radioButton_disabledBorder // SessionButton case sessionButton_text