Fixed the background crash issues

• Updated GRDB and SQLCipher
• Shifted the ThreadSettingsViewModel code into a separate function to fix a compilation issue
pull/960/head
Morgan Pretty 1 year ago
parent 9491b4a97b
commit eccaa29c4a

@ -1 +1 @@
Subproject commit 6c86cc0d374bf0b3372512c31ab6d0d5727be643 Subproject commit c7c68fb6b344d431f6a5b7652eab0fd8f7be8286

@ -12,7 +12,7 @@ abstract_target 'GlobalDependencies' do
pod 'GRDB.swift/SQLCipher' pod 'GRDB.swift/SQLCipher'
# FIXME: Would be nice to migrate from CocoaPods to SwiftPackageManager (should allow us to speed up build time), haven't gone through all of the dependencies but currently unfortunately SQLCipher doesn't support SPM (for more info see: https://github.com/sqlcipher/sqlcipher/issues/371) # FIXME: Would be nice to migrate from CocoaPods to SwiftPackageManager (should allow us to speed up build time), haven't gone through all of the dependencies but currently unfortunately SQLCipher doesn't support SPM (for more info see: https://github.com/sqlcipher/sqlcipher/issues/371)
pod 'SQLCipher', '~> 4.5.3' pod 'SQLCipher', '~> 4.5.7'
pod 'WebRTC-lib' pod 'WebRTC-lib'
target 'Session' do target 'Session' do

@ -11,7 +11,7 @@ PODS:
- DifferenceKit/Core (1.3.0) - DifferenceKit/Core (1.3.0)
- DifferenceKit/UIKitExtension (1.3.0): - DifferenceKit/UIKitExtension (1.3.0):
- DifferenceKit/Core - DifferenceKit/Core
- GRDB.swift/SQLCipher (6.13.0): - GRDB.swift/SQLCipher (6.24.1):
- SQLCipher (>= 3.4.2) - SQLCipher (>= 3.4.2)
- libwebp (1.3.2): - libwebp (1.3.2):
- libwebp/demux (= 1.3.2) - libwebp/demux (= 1.3.2)
@ -37,10 +37,10 @@ PODS:
- CocoaLumberjack - CocoaLumberjack
- OpenSSL-Universal - OpenSSL-Universal
- Sodium (0.9.1) - Sodium (0.9.1)
- SQLCipher (4.5.3): - SQLCipher (4.5.7):
- SQLCipher/standard (= 4.5.3) - SQLCipher/standard (= 4.5.7)
- SQLCipher/common (4.5.3) - SQLCipher/common (4.5.7)
- SQLCipher/standard (4.5.3): - SQLCipher/standard (4.5.7):
- SQLCipher/common - SQLCipher/common
- SwiftProtobuf (1.5.0) - SwiftProtobuf (1.5.0)
- WebRTC-lib (114.0.0) - WebRTC-lib (114.0.0)
@ -60,7 +60,7 @@ DEPENDENCIES:
- SAMKeychain - SAMKeychain
- SignalCoreKit (from `https://github.com/oxen-io/session-ios-core-kit`, commit `3acbfe5`) - SignalCoreKit (from `https://github.com/oxen-io/session-ios-core-kit`, commit `3acbfe5`)
- Sodium (from `https://github.com/oxen-io/session-ios-swift-sodium.git`, commit `310c343`) - Sodium (from `https://github.com/oxen-io/session-ios-swift-sodium.git`, commit `310c343`)
- SQLCipher (~> 4.5.3) - SQLCipher (~> 4.5.7)
- SwiftProtobuf (~> 1.5.0) - SwiftProtobuf (~> 1.5.0)
- WebRTC-lib - WebRTC-lib
- YYImage/libwebp (from `https://github.com/signalapp/YYImage`) - YYImage/libwebp (from `https://github.com/signalapp/YYImage`)
@ -112,7 +112,7 @@ SPEC CHECKSUMS:
CocoaLumberjack: 78abfb691154e2a9df8ded4350d504ee19d90732 CocoaLumberjack: 78abfb691154e2a9df8ded4350d504ee19d90732
Curve25519Kit: e63f9859ede02438ae3defc5e1a87e09d1ec7ee6 Curve25519Kit: e63f9859ede02438ae3defc5e1a87e09d1ec7ee6
DifferenceKit: ab185c4d7f9cef8af3fcf593e5b387fb81e999ca DifferenceKit: ab185c4d7f9cef8af3fcf593e5b387fb81e999ca
GRDB.swift: fe420b1af49ec519c7e96e07887ee44f5dfa2b78 GRDB.swift: 136dcb5d8dddca50aae3ba7d77475f79e7232cd8
libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009 libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009
Nimble: f8a8219d16f176429b951e8f7e72df5c23ceddc0 Nimble: f8a8219d16f176429b951e8f7e72df5c23ceddc0
NVActivityIndicatorView: 1f6c5687f1171810aa27a3296814dc2d7dec3667 NVActivityIndicatorView: 1f6c5687f1171810aa27a3296814dc2d7dec3667
@ -122,11 +122,11 @@ SPEC CHECKSUMS:
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
SignalCoreKit: 1fbd8732163ef76de16cd1107d1fa3684b607e5d SignalCoreKit: 1fbd8732163ef76de16cd1107d1fa3684b607e5d
Sodium: a7d42cb46e789d2630fa552d35870b416ed055ae Sodium: a7d42cb46e789d2630fa552d35870b416ed055ae
SQLCipher: 57fa9f863fa4a3ed9dd3c90ace52315db8c0fdca SQLCipher: 5e6bfb47323635c8b657b1b27d25c5f1baf63bf5
SwiftProtobuf: 241400280f912735c1e1b9fe675fdd2c6c4d42e2 SwiftProtobuf: 241400280f912735c1e1b9fe675fdd2c6c4d42e2
WebRTC-lib: d83df8976fa608b980f1d85796b3de66d60a1953 WebRTC-lib: d83df8976fa608b980f1d85796b3de66d60a1953
YYImage: f1ddd15ac032a58b78bbed1e012b50302d318331 YYImage: f1ddd15ac032a58b78bbed1e012b50302d318331
PODFILE CHECKSUM: 92bc475070c02411caf98ca5e543dbcb188098e2 PODFILE CHECKSUM: 6d85dee189f35e1e9a49cf8e95799a7087cfbdd5
COCOAPODS: 1.15.2 COCOAPODS: 1.15.2

@ -7977,7 +7977,7 @@
CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES; CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_IDENTITY = "iPhone Developer";
CURRENT_PROJECT_VERSION = 441; CURRENT_PROJECT_VERSION = 442;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES; ENABLE_TESTABILITY = YES;
@ -8055,7 +8055,7 @@
CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES; CLANG_WARN__ARC_BRIDGE_CAST_NONARC = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_IDENTITY = "iPhone Distribution";
CURRENT_PROJECT_VERSION = 441; CURRENT_PROJECT_VERSION = 442;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;

@ -198,6 +198,7 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
// Stop all jobs except for message sending and when completed suspend the database // Stop all jobs except for message sending and when completed suspend the database
JobRunner.stopAndClearPendingJobs(exceptForVariant: .messageSend, using: dependencies) { JobRunner.stopAndClearPendingJobs(exceptForVariant: .messageSend, using: dependencies) {
Storage.suspendDatabaseAccess() Storage.suspendDatabaseAccess()
LibSession.closeNetworkConnections()
} }
} }
} }

@ -216,521 +216,523 @@ class ThreadSettingsViewModel: SessionTableViewModel, NavigationItemSource, Navi
disappearingMessagesConfig: disappearingMessagesConfig disappearingMessagesConfig: disappearingMessagesConfig
) )
} }
.mapWithPrevious { [weak self, dependencies] previous, current -> [SectionModel] in .compactMapWithPrevious { [weak self] prev, current -> [SectionModel]? in self?.content(prev, current) }
// If we don't get a `SessionThreadViewModel` then it means the thread was probably deleted
// so dismiss the screen private func content(_ previous: State?, _ current: State) -> [SectionModel] {
guard let threadViewModel: SessionThreadViewModel = current.threadViewModel else { // If we don't get a `SessionThreadViewModel` then it means the thread was probably deleted
self?.dismissScreen(type: .popToRoot) // so dismiss the screen
return [] guard let threadViewModel: SessionThreadViewModel = current.threadViewModel else {
} self.dismissScreen(type: .popToRoot)
return []
let currentUserIsClosedGroupMember: Bool = ( }
(
threadViewModel.threadVariant == .legacyGroup || let currentUserIsClosedGroupMember: Bool = (
threadViewModel.threadVariant == .group (
) && threadViewModel.threadVariant == .legacyGroup ||
threadViewModel.currentUserIsClosedGroupMember == true threadViewModel.threadVariant == .group
) ) &&
let currentUserIsClosedGroupAdmin: Bool = ( threadViewModel.currentUserIsClosedGroupMember == true
( )
threadViewModel.threadVariant == .legacyGroup || let currentUserIsClosedGroupAdmin: Bool = (
threadViewModel.threadVariant == .group (
) && threadViewModel.threadVariant == .legacyGroup ||
threadViewModel.currentUserIsClosedGroupAdmin == true threadViewModel.threadVariant == .group
) ) &&
let editIcon: UIImage? = UIImage(named: "icon_edit") threadViewModel.currentUserIsClosedGroupAdmin == true
)
return [ let editIcon: UIImage? = UIImage(named: "icon_edit")
SectionModel(
model: .conversationInfo, return [
elements: [ SectionModel(
SessionCell.Info( model: .conversationInfo,
id: .avatar, elements: [
accessory: .profile( SessionCell.Info(
id: threadViewModel.id, id: .avatar,
size: .hero, accessory: .profile(
threadVariant: threadViewModel.threadVariant, id: threadViewModel.id,
customImageData: threadViewModel.openGroupProfilePictureData, size: .hero,
profile: threadViewModel.profile, threadVariant: threadViewModel.threadVariant,
profileIcon: .none, customImageData: threadViewModel.openGroupProfilePictureData,
additionalProfile: threadViewModel.additionalProfile, profile: threadViewModel.profile,
additionalProfileIcon: .none, profileIcon: .none,
accessibility: nil additionalProfile: threadViewModel.additionalProfile,
), additionalProfileIcon: .none,
styling: SessionCell.StyleInfo( accessibility: nil
alignment: .centerHugging, ),
customPadding: SessionCell.Padding(bottom: Values.smallSpacing), styling: SessionCell.StyleInfo(
backgroundStyle: .noBackground alignment: .centerHugging,
customPadding: SessionCell.Padding(bottom: Values.smallSpacing),
backgroundStyle: .noBackground
),
onTap: { [weak self] in self?.viewProfilePicture(threadViewModel: threadViewModel) }
),
SessionCell.Info(
id: .nickname,
leftAccessory: (threadViewModel.threadVariant != .contact ? nil :
.icon(
editIcon?.withRenderingMode(.alwaysTemplate),
size: .fit,
customTint: .textSecondary
)
),
title: SessionCell.TextInfo(
threadViewModel.displayName,
font: .titleLarge,
alignment: .center,
editingPlaceholder: "CONTACT_NICKNAME_PLACEHOLDER".localized(),
interaction: (threadViewModel.threadVariant == .contact ? .editable : .none)
),
styling: SessionCell.StyleInfo(
alignment: .centerHugging,
customPadding: SessionCell.Padding(
top: Values.smallSpacing,
trailing: (threadViewModel.threadVariant != .contact ?
nil :
-(((editIcon?.size.width ?? 0) + (Values.smallSpacing * 2)) / 2)
),
bottom: (threadViewModel.threadVariant != .contact ?
nil :
Values.smallSpacing
),
interItem: 0
), ),
onTap: { self?.viewProfilePicture(threadViewModel: threadViewModel) } backgroundStyle: .noBackground
), ),
accessibility: Accessibility(
identifier: "Username",
label: threadViewModel.displayName
),
onTap: { [weak self] in
self?.textChanged(self?.oldDisplayName, for: .nickname)
self?.setIsEditing(true)
}
),
(threadViewModel.threadVariant != .contact ? nil :
SessionCell.Info( SessionCell.Info(
id: .nickname, id: .sessionId,
leftAccessory: (threadViewModel.threadVariant != .contact ? nil : subtitle: SessionCell.TextInfo(
.icon( threadViewModel.id,
editIcon?.withRenderingMode(.alwaysTemplate), font: .monoSmall,
size: .fit,
customTint: .textSecondary
)
),
title: SessionCell.TextInfo(
threadViewModel.displayName,
font: .titleLarge,
alignment: .center, alignment: .center,
editingPlaceholder: "CONTACT_NICKNAME_PLACEHOLDER".localized(), interaction: .copy
interaction: (threadViewModel.threadVariant == .contact ? .editable : .none)
), ),
styling: SessionCell.StyleInfo( styling: SessionCell.StyleInfo(
alignment: .centerHugging,
customPadding: SessionCell.Padding( customPadding: SessionCell.Padding(
top: Values.smallSpacing, top: Values.smallSpacing,
trailing: (threadViewModel.threadVariant != .contact ? bottom: Values.largeSpacing
nil :
-(((editIcon?.size.width ?? 0) + (Values.smallSpacing * 2)) / 2)
),
bottom: (threadViewModel.threadVariant != .contact ?
nil :
Values.smallSpacing
),
interItem: 0
), ),
backgroundStyle: .noBackground backgroundStyle: .noBackground
), ),
accessibility: Accessibility( accessibility: Accessibility(
identifier: "Username", identifier: "Session ID",
label: threadViewModel.displayName label: threadViewModel.id
)
)
)
].compactMap { $0 }
),
SectionModel(
model: .content,
elements: [
(threadViewModel.threadVariant == .legacyGroup || threadViewModel.threadVariant == .group ? nil :
SessionCell.Info(
id: .copyThreadId,
leftAccessory: .icon(
UIImage(named: "ic_copy")?
.withRenderingMode(.alwaysTemplate)
), ),
onTap: { title: (threadViewModel.threadVariant == .community ?
self?.textChanged(self?.oldDisplayName, for: .nickname) "COPY_GROUP_URL".localized() :
self?.setIsEditing(true) "vc_conversation_settings_copy_session_id_button_title".localized()
),
accessibility: Accessibility(
identifier: "\(ThreadSettingsViewModel.self).copy_thread_id",
label: "Copy Session ID"
),
onTap: { [weak self] in
switch threadViewModel.threadVariant {
case .contact, .legacyGroup, .group:
UIPasteboard.general.string = threadViewModel.threadId
case .community:
guard
let server: String = threadViewModel.openGroupServer,
let roomToken: String = threadViewModel.openGroupRoomToken,
let publicKey: String = threadViewModel.openGroupPublicKey
else { return }
UIPasteboard.general.string = LibSession.communityUrlFor(
server: server,
roomToken: roomToken,
publicKey: publicKey
)
}
self?.showToast(
text: "copied".localized(),
backgroundColor: .backgroundSecondary
)
} }
), )
),
(threadViewModel.threadVariant != .contact ? nil : SessionCell.Info(
SessionCell.Info( id: .allMedia,
id: .sessionId, leftAccessory: .icon(
subtitle: SessionCell.TextInfo( UIImage(named: "actionsheet_camera_roll_black")?
threadViewModel.id, .withRenderingMode(.alwaysTemplate)
font: .monoSmall, ),
alignment: .center, title: MediaStrings.allMedia,
interaction: .copy accessibility: Accessibility(
), identifier: "\(ThreadSettingsViewModel.self).all_media",
styling: SessionCell.StyleInfo( label: "All media"
customPadding: SessionCell.Padding( ),
top: Values.smallSpacing, onTap: { [weak self] in
bottom: Values.largeSpacing self?.transitionToScreen(
), MediaGalleryViewModel.createAllMediaViewController(
backgroundStyle: .noBackground threadId: threadViewModel.threadId,
), threadVariant: threadViewModel.threadVariant,
accessibility: Accessibility( focusedAttachmentId: nil
identifier: "Session ID",
label: threadViewModel.id
) )
) )
) }
].compactMap { $0 } ),
),
SectionModel(
model: .content,
elements: [
(threadViewModel.threadVariant == .legacyGroup || threadViewModel.threadVariant == .group ? nil :
SessionCell.Info(
id: .copyThreadId,
leftAccessory: .icon(
UIImage(named: "ic_copy")?
.withRenderingMode(.alwaysTemplate)
),
title: (threadViewModel.threadVariant == .community ?
"COPY_GROUP_URL".localized() :
"vc_conversation_settings_copy_session_id_button_title".localized()
),
accessibility: Accessibility(
identifier: "\(ThreadSettingsViewModel.self).copy_thread_id",
label: "Copy Session ID"
),
onTap: {
switch threadViewModel.threadVariant {
case .contact, .legacyGroup, .group:
UIPasteboard.general.string = threadViewModel.threadId
case .community: SessionCell.Info(
guard id: .searchConversation,
let server: String = threadViewModel.openGroupServer, leftAccessory: .icon(
let roomToken: String = threadViewModel.openGroupRoomToken, UIImage(named: "conversation_settings_search")?
let publicKey: String = threadViewModel.openGroupPublicKey .withRenderingMode(.alwaysTemplate)
else { return } ),
title: "CONVERSATION_SETTINGS_SEARCH".localized(),
accessibility: Accessibility(
identifier: "\(ThreadSettingsViewModel.self).search",
label: "Search"
),
onTap: { [weak self] in
self?.didTriggerSearch()
}
),
UIPasteboard.general.string = LibSession.communityUrlFor( (threadViewModel.threadVariant != .community ? nil :
server: server, SessionCell.Info(
roomToken: roomToken, id: .addToOpenGroup,
publicKey: publicKey leftAccessory: .icon(
) UIImage(named: "ic_plus_24")?
.withRenderingMode(.alwaysTemplate)
),
title: "vc_conversation_settings_invite_button_title".localized(),
accessibility: Accessibility(
identifier: "\(ThreadSettingsViewModel.self).add_to_open_group"
),
onTap: { [weak self] in
self?.transitionToScreen(
UserSelectionVC(
with: "vc_conversation_settings_invite_button_title".localized(),
excluding: Set()
) { [weak self] selectedUsers in
self?.addUsersToOpenGoup(
threadViewModel: threadViewModel,
selectedUsers: selectedUsers
)
} }
)
}
)
),
self?.showToast( (threadViewModel.threadVariant == .community || threadViewModel.threadIsBlocked == true ? nil :
text: "copied".localized(), SessionCell.Info(
backgroundColor: .backgroundSecondary id: .disappearingMessages,
leftAccessory: .icon(
UIImage(systemName: "timer")?
.withRenderingMode(.alwaysTemplate)
),
title: "DISAPPEARING_MESSAGES".localized(),
subtitle: {
guard current.disappearingMessagesConfig.isEnabled else {
return "DISAPPEARING_MESSAGES_SUBTITLE_OFF".localized()
}
guard Features.useNewDisappearingMessagesConfig else {
return String(
format: "DISAPPEARING_MESSAGES_SUBTITLE_DISAPPEAR_AFTER_LEGACY".localized(),
current.disappearingMessagesConfig.durationString
) )
} }
)
), return String(
format: (current.disappearingMessagesConfig.type == .disappearAfterRead ?
"DISAPPEARING_MESSAGES_SUBTITLE_DISAPPEAR_AFTER_READ".localized() :
"DISAPPEARING_MESSAGES_SUBTITLE_DISAPPEAR_AFTER_SEND".localized()
),
current.disappearingMessagesConfig.durationString
)
}(),
accessibility: Accessibility(
identifier: "Disappearing messages",
label: "\(ThreadSettingsViewModel.self).disappearing_messages"
),
onTap: { [weak self] in
self?.transitionToScreen(
SessionTableViewController(
viewModel: ThreadDisappearingMessagesSettingsViewModel(
threadId: threadViewModel.threadId,
threadVariant: threadViewModel.threadVariant,
currentUserIsClosedGroupMember: threadViewModel.currentUserIsClosedGroupMember,
currentUserIsClosedGroupAdmin: threadViewModel.currentUserIsClosedGroupAdmin,
config: current.disappearingMessagesConfig
)
)
)
}
)
),
(!currentUserIsClosedGroupMember ? nil :
SessionCell.Info( SessionCell.Info(
id: .allMedia, id: .editGroup,
leftAccessory: .icon( leftAccessory: .icon(
UIImage(named: "actionsheet_camera_roll_black")? UIImage(named: "table_ic_group_edit")?
.withRenderingMode(.alwaysTemplate) .withRenderingMode(.alwaysTemplate)
), ),
title: MediaStrings.allMedia, title: "EDIT_GROUP_ACTION".localized(),
accessibility: Accessibility( accessibility: Accessibility(
identifier: "\(ThreadSettingsViewModel.self).all_media", identifier: "Edit group",
label: "All media" label: "Edit group"
), ),
onTap: { [weak self] in onTap: { [weak self] in
self?.transitionToScreen( self?.transitionToScreen(
MediaGalleryViewModel.createAllMediaViewController( EditClosedGroupVC(
threadId: threadViewModel.threadId, threadId: threadViewModel.threadId,
threadVariant: threadViewModel.threadVariant, threadVariant: threadViewModel.threadVariant
focusedAttachmentId: nil
) )
) )
} }
), )
),
(!currentUserIsClosedGroupMember ? nil :
SessionCell.Info( SessionCell.Info(
id: .searchConversation, id: .leaveGroup,
leftAccessory: .icon( leftAccessory: .icon(
UIImage(named: "conversation_settings_search")? UIImage(named: "table_ic_group_leave")?
.withRenderingMode(.alwaysTemplate) .withRenderingMode(.alwaysTemplate)
), ),
title: "CONVERSATION_SETTINGS_SEARCH".localized(), title: "LEAVE_GROUP_ACTION".localized(),
accessibility: Accessibility( accessibility: Accessibility(
identifier: "\(ThreadSettingsViewModel.self).search", identifier: "Leave group",
label: "Search" label: "Leave group"
), ),
onTap: { [weak self] in confirmationInfo: ConfirmationModal.Info(
self?.didTriggerSearch() title: "leave_group_confirmation_alert_title".localized(),
} body: .attributedText({
), if currentUserIsClosedGroupAdmin {
return NSAttributedString(string: "admin_group_leave_warning".localized())
(threadViewModel.threadVariant != .community ? nil :
SessionCell.Info(
id: .addToOpenGroup,
leftAccessory: .icon(
UIImage(named: "ic_plus_24")?
.withRenderingMode(.alwaysTemplate)
),
title: "vc_conversation_settings_invite_button_title".localized(),
accessibility: Accessibility(
identifier: "\(ThreadSettingsViewModel.self).add_to_open_group"
),
onTap: { [weak self] in
self?.transitionToScreen(
UserSelectionVC(
with: "vc_conversation_settings_invite_button_title".localized(),
excluding: Set()
) { [weak self] selectedUsers in
self?.addUsersToOpenGoup(
threadViewModel: threadViewModel,
selectedUsers: selectedUsers
)
}
)
}
)
),
(threadViewModel.threadVariant == .community || threadViewModel.threadIsBlocked == true ? nil :
SessionCell.Info(
id: .disappearingMessages,
leftAccessory: .icon(
UIImage(systemName: "timer")?
.withRenderingMode(.alwaysTemplate)
),
title: "DISAPPEARING_MESSAGES".localized(),
subtitle: {
guard current.disappearingMessagesConfig.isEnabled else {
return "DISAPPEARING_MESSAGES_SUBTITLE_OFF".localized()
}
guard Features.useNewDisappearingMessagesConfig else {
return String(
format: "DISAPPEARING_MESSAGES_SUBTITLE_DISAPPEAR_AFTER_LEGACY".localized(),
current.disappearingMessagesConfig.durationString
)
} }
return String( let mutableAttributedString = NSMutableAttributedString(
format: (current.disappearingMessagesConfig.type == .disappearAfterRead ? string: String(
"DISAPPEARING_MESSAGES_SUBTITLE_DISAPPEAR_AFTER_READ".localized() : format: "leave_community_confirmation_alert_message".localized(),
"DISAPPEARING_MESSAGES_SUBTITLE_DISAPPEAR_AFTER_SEND".localized() threadViewModel.displayName
),
current.disappearingMessagesConfig.durationString
)
}(),
accessibility: Accessibility(
identifier: "Disappearing messages",
label: "\(ThreadSettingsViewModel.self).disappearing_messages"
),
onTap: { [weak self] in
self?.transitionToScreen(
SessionTableViewController(
viewModel: ThreadDisappearingMessagesSettingsViewModel(
threadId: threadViewModel.threadId,
threadVariant: threadViewModel.threadVariant,
currentUserIsClosedGroupMember: threadViewModel.currentUserIsClosedGroupMember,
currentUserIsClosedGroupAdmin: threadViewModel.currentUserIsClosedGroupAdmin,
config: current.disappearingMessagesConfig
)
) )
) )
} mutableAttributedString.addAttribute(
) .font,
), value: UIFont.boldSystemFont(ofSize: Values.smallFontSize),
range: (mutableAttributedString.string as NSString).range(of: threadViewModel.displayName)
(!currentUserIsClosedGroupMember ? nil : )
SessionCell.Info( return mutableAttributedString
id: .editGroup, }()),
leftAccessory: .icon( confirmTitle: "LEAVE_BUTTON_TITLE".localized(),
UIImage(named: "table_ic_group_edit")? confirmStyle: .danger,
.withRenderingMode(.alwaysTemplate) cancelStyle: .alert_text
), ),
title: "EDIT_GROUP_ACTION".localized(), onTap: { [dependencies] in
accessibility: Accessibility( dependencies.storage.write { db in
identifier: "Edit group", try SessionThread.deleteOrLeave(
label: "Edit group" db,
), threadId: threadViewModel.threadId,
onTap: { [weak self] in threadVariant: threadViewModel.threadVariant,
self?.transitionToScreen( groupLeaveType: .standard,
EditClosedGroupVC( calledFromConfigHandling: false
threadId: threadViewModel.threadId,
threadVariant: threadViewModel.threadVariant
)
) )
} }
) }
), )
),
(!currentUserIsClosedGroupMember ? nil :
SessionCell.Info( (threadViewModel.threadIsNoteToSelf ? nil :
id: .leaveGroup, SessionCell.Info(
leftAccessory: .icon( id: .notificationSound,
UIImage(named: "table_ic_group_leave")? leftAccessory: .icon(
.withRenderingMode(.alwaysTemplate) UIImage(named: "table_ic_notification_sound")?
), .withRenderingMode(.alwaysTemplate)
title: "LEAVE_GROUP_ACTION".localized(), ),
accessibility: Accessibility( title: "SETTINGS_ITEM_NOTIFICATION_SOUND".localized(),
identifier: "Leave group", rightAccessory: .dropDown(
label: "Leave group" .dynamicString { current.notificationSound.displayName }
), ),
confirmationInfo: ConfirmationModal.Info( onTap: { [weak self] in
title: "leave_group_confirmation_alert_title".localized(), self?.transitionToScreen(
body: .attributedText({ SessionTableViewController(
if currentUserIsClosedGroupAdmin { viewModel: NotificationSoundViewModel(threadId: threadViewModel.threadId)
return NSAttributedString(string: "admin_group_leave_warning".localized()) )
} )
}
let mutableAttributedString = NSMutableAttributedString( )
string: String( ),
format: "leave_community_confirmation_alert_message".localized(),
threadViewModel.displayName (threadViewModel.threadVariant == .contact ? nil :
) SessionCell.Info(
) id: .notificationMentionsOnly,
mutableAttributedString.addAttribute( leftAccessory: .icon(
.font, UIImage(named: "NotifyMentions")?
value: UIFont.boldSystemFont(ofSize: Values.smallFontSize), .withRenderingMode(.alwaysTemplate)
range: (mutableAttributedString.string as NSString).range(of: threadViewModel.displayName) ),
) title: "vc_conversation_settings_notify_for_mentions_only_title".localized(),
return mutableAttributedString subtitle: "vc_conversation_settings_notify_for_mentions_only_explanation".localized(),
}()), rightAccessory: .toggle(
confirmTitle: "LEAVE_BUTTON_TITLE".localized(), .boolValue(
confirmStyle: .danger, threadViewModel.threadOnlyNotifyForMentions == true,
cancelStyle: .alert_text oldValue: ((previous?.threadViewModel ?? threadViewModel).threadOnlyNotifyForMentions == true)
), )
onTap: { [weak self] in ),
dependencies.storage.write { db in isEnabled: (
try SessionThread.deleteOrLeave( (
threadViewModel.threadVariant != .legacyGroup &&
threadViewModel.threadVariant != .group
) ||
currentUserIsClosedGroupMember
),
accessibility: Accessibility(
identifier: "Mentions only notification setting",
label: "Mentions only"
),
onTap: { [dependencies] in
let newValue: Bool = !(threadViewModel.threadOnlyNotifyForMentions == true)
dependencies.storage.writeAsync { db in
try SessionThread
.filter(id: threadViewModel.threadId)
.updateAll(
db, db,
threadId: threadViewModel.threadId, SessionThread.Columns.onlyNotifyForMentions
threadVariant: threadViewModel.threadVariant, .set(to: newValue)
groupLeaveType: .standard,
calledFromConfigHandling: false
) )
}
}
)
),
(threadViewModel.threadIsNoteToSelf ? nil :
SessionCell.Info(
id: .notificationSound,
leftAccessory: .icon(
UIImage(named: "table_ic_notification_sound")?
.withRenderingMode(.alwaysTemplate)
),
title: "SETTINGS_ITEM_NOTIFICATION_SOUND".localized(),
rightAccessory: .dropDown(
.dynamicString { current.notificationSound.displayName }
),
onTap: { [weak self] in
self?.transitionToScreen(
SessionTableViewController(
viewModel: NotificationSoundViewModel(threadId: threadViewModel.threadId)
)
)
} }
) }
), )
),
(threadViewModel.threadVariant == .contact ? nil :
SessionCell.Info( (threadViewModel.threadIsNoteToSelf ? nil :
id: .notificationMentionsOnly, SessionCell.Info(
leftAccessory: .icon( id: .notificationMute,
UIImage(named: "NotifyMentions")? leftAccessory: .icon(
.withRenderingMode(.alwaysTemplate) UIImage(named: "Mute")?
), .withRenderingMode(.alwaysTemplate)
title: "vc_conversation_settings_notify_for_mentions_only_title".localized(), ),
subtitle: "vc_conversation_settings_notify_for_mentions_only_explanation".localized(), title: "CONVERSATION_SETTINGS_MUTE_LABEL".localized(),
rightAccessory: .toggle( rightAccessory: .toggle(
.boolValue( .boolValue(
threadViewModel.threadOnlyNotifyForMentions == true, threadViewModel.threadMutedUntilTimestamp != nil,
oldValue: ((previous?.threadViewModel ?? threadViewModel).threadOnlyNotifyForMentions == true) oldValue: ((previous?.threadViewModel ?? threadViewModel).threadMutedUntilTimestamp != nil)
) )
), ),
isEnabled: ( isEnabled: (
( (
threadViewModel.threadVariant != .legacyGroup && threadViewModel.threadVariant != .legacyGroup &&
threadViewModel.threadVariant != .group threadViewModel.threadVariant != .group
) || ) ||
currentUserIsClosedGroupMember currentUserIsClosedGroupMember
), ),
accessibility: Accessibility( accessibility: Accessibility(
identifier: "Mentions only notification setting", identifier: "\(ThreadSettingsViewModel.self).mute",
label: "Mentions only" label: "Mute notifications"
), ),
onTap: { onTap: { [dependencies] in
let newValue: Bool = !(threadViewModel.threadOnlyNotifyForMentions == true) dependencies.storage.writeAsync { db in
let currentValue: TimeInterval? = try SessionThread
.filter(id: threadViewModel.threadId)
.select(.mutedUntilTimestamp)
.asRequest(of: TimeInterval.self)
.fetchOne(db)
dependencies.storage.writeAsync { db in try SessionThread
try SessionThread .filter(id: threadViewModel.threadId)
.filter(id: threadViewModel.threadId) .updateAll(
.updateAll( db,
db, SessionThread.Columns.mutedUntilTimestamp.set(
SessionThread.Columns.onlyNotifyForMentions to: (currentValue == nil ?
.set(to: newValue) Date.distantFuture.timeIntervalSince1970 :
) nil
}
}
)
),
(threadViewModel.threadIsNoteToSelf ? nil :
SessionCell.Info(
id: .notificationMute,
leftAccessory: .icon(
UIImage(named: "Mute")?
.withRenderingMode(.alwaysTemplate)
),
title: "CONVERSATION_SETTINGS_MUTE_LABEL".localized(),
rightAccessory: .toggle(
.boolValue(
threadViewModel.threadMutedUntilTimestamp != nil,
oldValue: ((previous?.threadViewModel ?? threadViewModel).threadMutedUntilTimestamp != nil)
)
),
isEnabled: (
(
threadViewModel.threadVariant != .legacyGroup &&
threadViewModel.threadVariant != .group
) ||
currentUserIsClosedGroupMember
),
accessibility: Accessibility(
identifier: "\(ThreadSettingsViewModel.self).mute",
label: "Mute notifications"
),
onTap: {
dependencies.storage.writeAsync { db in
let currentValue: TimeInterval? = try SessionThread
.filter(id: threadViewModel.threadId)
.select(.mutedUntilTimestamp)
.asRequest(of: TimeInterval.self)
.fetchOne(db)
try SessionThread
.filter(id: threadViewModel.threadId)
.updateAll(
db,
SessionThread.Columns.mutedUntilTimestamp.set(
to: (currentValue == nil ?
Date.distantFuture.timeIntervalSince1970 :
nil
)
) )
) )
} )
} }
) }
), )
),
(threadViewModel.threadIsNoteToSelf || threadViewModel.threadVariant != .contact ? nil :
SessionCell.Info( (threadViewModel.threadIsNoteToSelf || threadViewModel.threadVariant != .contact ? nil :
id: .blockUser, SessionCell.Info(
leftAccessory: .icon( id: .blockUser,
UIImage(named: "table_ic_block")? leftAccessory: .icon(
.withRenderingMode(.alwaysTemplate) UIImage(named: "table_ic_block")?
), .withRenderingMode(.alwaysTemplate)
title: "CONVERSATION_SETTINGS_BLOCK_THIS_USER".localized(), ),
rightAccessory: .toggle( title: "CONVERSATION_SETTINGS_BLOCK_THIS_USER".localized(),
.boolValue( rightAccessory: .toggle(
threadViewModel.threadIsBlocked == true, .boolValue(
oldValue: ((previous?.threadViewModel ?? threadViewModel).threadIsBlocked == true) threadViewModel.threadIsBlocked == true,
) oldValue: ((previous?.threadViewModel ?? threadViewModel).threadIsBlocked == true)
), )
accessibility: Accessibility( ),
identifier: "\(ThreadSettingsViewModel.self).block", accessibility: Accessibility(
label: "Block" identifier: "\(ThreadSettingsViewModel.self).block",
), label: "Block"
confirmationInfo: ConfirmationModal.Info( ),
title: { confirmationInfo: ConfirmationModal.Info(
guard threadViewModel.threadIsBlocked == true else { title: {
return String( guard threadViewModel.threadIsBlocked == true else {
format: "BLOCK_LIST_BLOCK_USER_TITLE_FORMAT".localized(),
threadViewModel.displayName
)
}
return String( return String(
format: "BLOCK_LIST_UNBLOCK_TITLE_FORMAT".localized(), format: "BLOCK_LIST_BLOCK_USER_TITLE_FORMAT".localized(),
threadViewModel.displayName threadViewModel.displayName
) )
}(), }
body: (threadViewModel.threadIsBlocked == true ? .none :
.text("BLOCK_USER_BEHAVIOR_EXPLANATION".localized())
),
confirmTitle: (threadViewModel.threadIsBlocked == true ?
"BLOCK_LIST_UNBLOCK_BUTTON".localized() :
"BLOCK_LIST_BLOCK_BUTTON".localized()
),
confirmAccessibility: Accessibility(identifier: "Confirm block"),
confirmStyle: .danger,
cancelStyle: .alert_text
),
onTap: {
let isBlocked: Bool = (threadViewModel.threadIsBlocked == true)
self?.updateBlockedState( return String(
from: isBlocked, format: "BLOCK_LIST_UNBLOCK_TITLE_FORMAT".localized(),
isBlocked: !isBlocked, threadViewModel.displayName
threadId: threadViewModel.threadId,
displayName: threadViewModel.displayName
) )
} }(),
) body: (threadViewModel.threadIsBlocked == true ? .none :
.text("BLOCK_USER_BEHAVIOR_EXPLANATION".localized())
),
confirmTitle: (threadViewModel.threadIsBlocked == true ?
"BLOCK_LIST_UNBLOCK_BUTTON".localized() :
"BLOCK_LIST_BLOCK_BUTTON".localized()
),
confirmAccessibility: Accessibility(identifier: "Confirm block"),
confirmStyle: .danger,
cancelStyle: .alert_text
),
onTap: { [weak self] in
let isBlocked: Bool = (threadViewModel.threadIsBlocked == true)
self?.updateBlockedState(
from: isBlocked,
isBlocked: !isBlocked,
threadId: threadViewModel.threadId,
displayName: threadViewModel.displayName
)
}
) )
].compactMap { $0 } )
) ].compactMap { $0 }
] )
} ]
}
// MARK: - Functions // MARK: - Functions

@ -204,6 +204,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
JobRunner.stopAndClearPendingJobs(exceptForVariant: .messageSend, using: dependencies) { JobRunner.stopAndClearPendingJobs(exceptForVariant: .messageSend, using: dependencies) {
if !self.hasCallOngoing() { if !self.hasCallOngoing() {
Storage.suspendDatabaseAccess() Storage.suspendDatabaseAccess()
LibSession.closeNetworkConnections()
} }
} }
} }
@ -284,6 +285,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
if Singleton.hasAppContext && Singleton.appContext.isInBackground { if Singleton.hasAppContext && Singleton.appContext.isInBackground {
Storage.suspendDatabaseAccess() Storage.suspendDatabaseAccess()
LibSession.closeNetworkConnections()
} }
SNLog("Background poll failed due to manual timeout") SNLog("Background poll failed due to manual timeout")
@ -310,6 +312,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
if Singleton.hasAppContext && Singleton.appContext.isInBackground { if Singleton.hasAppContext && Singleton.appContext.isInBackground {
Storage.suspendDatabaseAccess() Storage.suspendDatabaseAccess()
LibSession.closeNetworkConnections()
} }
cancelTimer.invalidate() cancelTimer.invalidate()

@ -216,6 +216,15 @@ public extension TableObservation {
.eraseToAnyPublisher() .eraseToAnyPublisher()
} }
} }
func compactMapWithPrevious<R>(transform: @escaping (T?, T) -> R?) -> TableObservation<R> {
return TableObservation<R> { viewModel, dependencies in
self.generatePublisher(viewModel, dependencies)
.withPrevious()
.compactMap(transform)
.eraseToAnyPublisher()
}
}
} }
public extension Array { public extension Array {

@ -112,8 +112,8 @@ public extension Profile {
// If we have both a `profileKey` and a `profilePicture` then the key MUST be valid // If we have both a `profileKey` and a `profilePicture` then the key MUST be valid
if if
let profileKeyData: Data = try? container.decode(Data.self, forKey: .profileEncryptionKey), let profileKeyData: Data = try? container.decode(Data?.self, forKey: .profileEncryptionKey),
let profilePictureUrlValue: String = try? container.decode(String.self, forKey: .profilePictureUrl) let profilePictureUrlValue: String = try? container.decode(String?.self, forKey: .profilePictureUrl)
{ {
profileKey = profileKeyData profileKey = profileKeyData
profilePictureUrl = profilePictureUrlValue profilePictureUrl = profilePictureUrlValue
@ -122,14 +122,14 @@ public extension Profile {
self = Profile( self = Profile(
id: try container.decode(String.self, forKey: .id), id: try container.decode(String.self, forKey: .id),
name: try container.decode(String.self, forKey: .name), name: try container.decode(String.self, forKey: .name),
lastNameUpdate: try? container.decode(TimeInterval.self, forKey: .lastNameUpdate), lastNameUpdate: try? container.decode(TimeInterval?.self, forKey: .lastNameUpdate),
nickname: try? container.decode(String.self, forKey: .nickname), nickname: try? container.decode(String?.self, forKey: .nickname),
profilePictureUrl: profilePictureUrl, profilePictureUrl: profilePictureUrl,
profilePictureFileName: try? container.decode(String.self, forKey: .profilePictureFileName), profilePictureFileName: try? container.decode(String?.self, forKey: .profilePictureFileName),
profileEncryptionKey: profileKey, profileEncryptionKey: profileKey,
lastProfilePictureUpdate: try? container.decode(TimeInterval.self, forKey: .lastProfilePictureUpdate), lastProfilePictureUpdate: try? container.decode(TimeInterval?.self, forKey: .lastProfilePictureUpdate),
blocksCommunityMessageRequests: try? container.decode(Bool.self, forKey: .blocksCommunityMessageRequests), blocksCommunityMessageRequests: try? container.decode(Bool?.self, forKey: .blocksCommunityMessageRequests),
lastBlocksCommunityMessageRequests: try? container.decode(TimeInterval.self, forKey: .lastBlocksCommunityMessageRequests) lastBlocksCommunityMessageRequests: try? container.decode(TimeInterval?.self, forKey: .lastBlocksCommunityMessageRequests)
) )
} }

@ -557,7 +557,7 @@ public extension MessageViewModel {
struct ReactionInfo: FetchableRecordWithRowId, Decodable, Identifiable, Equatable, Comparable, Hashable, Differentiable, ColumnExpressible { struct ReactionInfo: FetchableRecordWithRowId, Decodable, Identifiable, Equatable, Comparable, Hashable, Differentiable, ColumnExpressible {
public typealias Columns = CodingKeys public typealias Columns = CodingKeys
public enum CodingKeys: String, CodingKey, ColumnExpression, CaseIterable { public enum CodingKeys: String, CodingKey, ColumnExpression, CaseIterable {
case rowId case rowId = "rowid"
case reaction case reaction
case profile case profile
} }

@ -319,6 +319,7 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
.map { NSNumber(value: $0) } .map { NSNumber(value: $0) }
.defaulting(to: NSNumber(value: 0)) .defaulting(to: NSNumber(value: 0))
Storage.suspendDatabaseAccess() Storage.suspendDatabaseAccess()
LibSession.closeNetworkConnections()
self.contentHandler!(silentContent) self.contentHandler!(silentContent)
} }
@ -385,6 +386,7 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension
SNLog("[NotificationServiceExtension] Show generic failure message due to error: \(error)", forceNSLog: true) SNLog("[NotificationServiceExtension] Show generic failure message due to error: \(error)", forceNSLog: true)
DDLog.flushLog() DDLog.flushLog()
Storage.suspendDatabaseAccess() Storage.suspendDatabaseAccess()
LibSession.closeNetworkConnections()
content.title = "Session" content.title = "Session"
content.body = "APN_Message".localized() content.body = "APN_Message".localized()

@ -310,6 +310,7 @@ final class ThreadPickerVC: UIViewController, UITableViewDataSource, UITableView
receiveCompletion: { [weak self] result in receiveCompletion: { [weak self] result in
DDLog.flushLog() DDLog.flushLog()
Storage.suspendDatabaseAccess() Storage.suspendDatabaseAccess()
LibSession.closeNetworkConnections()
activityIndicator.dismiss { } activityIndicator.dismiss { }
switch result { switch result {

@ -112,6 +112,12 @@ public extension LibSession {
}) })
} }
static func closeNetworkConnections() {
guard let network: UnsafeMutablePointer<network_object> = networkCache.wrappedValue else { return }
network_close_connections(network)
}
static func clearSnodeCache() { static func clearSnodeCache() {
guard let network: UnsafeMutablePointer<network_object> = networkCache.wrappedValue else { return } guard let network: UnsafeMutablePointer<network_object> = networkCache.wrappedValue else { return }
@ -388,8 +394,16 @@ public extension LibSession {
} }
return nodes return nodes
} }
// Need to free the nodes within the path as we are the owner
cPaths.forEach { cPath in
cPath.nodes.deallocate()
}
} }
// Need to free the cPathsPtr as we are the owner
cPathsPtr?.deallocate()
lastPaths.mutate { lastPaths in lastPaths.mutate { lastPaths in
lastPaths = paths lastPaths = paths

@ -139,8 +139,9 @@ public extension Array where Element == String {
count: Int? count: Int?
) { ) {
guard guard
let pointee: UnsafeMutablePointer<CChar> = pointer?.pointee, let count: Int = count,
let count: Int = count count > 0,
let pointee: UnsafeMutablePointer<CChar> = pointer?.pointee
else { return nil } else { return nil }
self = (0..<count) self = (0..<count)

Loading…
Cancel
Save