Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
6941f7e
Updated code to use libSession for message encoding
mpretty-cyro Oct 15, 2025
8d9cb6a
Renamed message Origin/Destination to use "community" and "group"
mpretty-cyro Oct 16, 2025
f00a2e3
Added in initial logic for message decoding via libSession
mpretty-cyro Oct 16, 2025
1c2f794
Added original handling for community inbox messages
mpretty-cyro Oct 16, 2025
71f303c
Consolidated MessageReceiverError and MessageSenderError
mpretty-cyro Oct 16, 2025
60f4fef
Cleaned up libSession message decoding logic
mpretty-cyro Oct 16, 2025
0aa4233
Added support for group decoding, updated with latest naming changes
mpretty-cyro Oct 16, 2025
c43c27c
Added wrappers and code to test the add_pro_payment endpoint
mpretty-cyro Oct 22, 2025
767ef8b
Added some more Pro API call wrappers, fixed issue with initial tests
mpretty-cyro Oct 28, 2025
0cc3736
Merge branch 'feature/profile-pic-reupload-changes' into feature/libs…
mpretty-cyro Oct 28, 2025
ca1697e
Merge remote-tracking branch 'upstream/dev' into feature/libsession-m…
mpretty-cyro Oct 31, 2025
af63853
Updated libSession integration, plugged in some pro status observations
mpretty-cyro Oct 31, 2025
aef37ee
Started plugging in some logic to use proper pro status and proofs
mpretty-cyro Oct 31, 2025
36bdbe7
Updated pro integration for breaking change
mpretty-cyro Nov 3, 2025
22bab8b
Wired up the pro proof check to the truncation logic
mpretty-cyro Nov 5, 2025
0467625
Merge remote-tracking branch 'upstream/dev' into feature/libsession-m…
mpretty-cyro Nov 5, 2025
f32d8a7
Populate and update the pro state from the UserProfile config
mpretty-cyro Nov 5, 2025
0c889e7
Merge remote-tracking branch 'RyanFork/session-pro-badge' into featur…
mpretty-cyro Nov 5, 2025
6bcd3f4
Refactoring to move pro state management to VMs instead of views
mpretty-cyro Nov 13, 2025
402e577
Started adding pro variables to profile info and wiring it through th…
mpretty-cyro Nov 13, 2025
aa203de
Updated the remaining Pro Badge cases
mpretty-cyro Nov 13, 2025
4ead1e8
Updates to dev settings and global search fix
mpretty-cyro Nov 13, 2025
58329d2
Resolved a bunch of TODOs and cleaned up some of the interface
mpretty-cyro Nov 14, 2025
9191307
Updates for breaking API changes
mpretty-cyro Nov 14, 2025
bdda549
Merge remote-tracking branch 'upstream/dev' into feature/libsession-m…
mpretty-cyro Nov 17, 2025
270f1f3
Added a bunch of Pro handling
mpretty-cyro Nov 18, 2025
5b3c37d
Added the pro revocations request (not used yet)
mpretty-cyro Nov 18, 2025
02786a9
Setup the "animatedAvatar" pro feature flag, fixed a couple of bugs
mpretty-cyro Nov 18, 2025
21e514b
Updated the conversation screen to correctly update title pro badge
mpretty-cyro Nov 19, 2025
1817fbb
Merge branch 'session-pro-settings' into feature/libsession-message-w…
mpretty-cyro Nov 21, 2025
ceb6405
Further progress wiring up Session Pro to Pro Settings
mpretty-cyro Nov 25, 2025
92dc30f
Bunch of process on pro integration
mpretty-cyro Nov 26, 2025
1730256
Fixed a few issues, updated for latest libSession changes
mpretty-cyro Nov 27, 2025
30210dc
Merge branch 'session-pro-settings' into feature/libsession-message-w…
mpretty-cyro Dec 1, 2025
c4dce92
Added a couple more mocks
mpretty-cyro Dec 1, 2025
53a7b1b
Fixed a bunch of build errors from the merge
mpretty-cyro Dec 1, 2025
1b8d6ea
Added in initial AppStore purchase logic, cleaned up some code
mpretty-cyro Dec 2, 2025
57305ed
Updated with latest libSession changes
mpretty-cyro Dec 2, 2025
ac9816b
Massive refactors of the SessionThreadViewModel and MessageViewModel
mpretty-cyro Dec 11, 2025
69c242f
Bunch of bug fixes
mpretty-cyro Dec 12, 2025
fa6efe5
Updated to the latest libSession
mpretty-cyro Dec 12, 2025
24157d8
Fixed a few more bugs
mpretty-cyro Dec 12, 2025
6aedc4b
Fixed an issue where the QueryRunner would never go out of scope
mpretty-cyro Dec 15, 2025
ce773c1
Added in a couple of TODOs and simple cancellation logic
mpretty-cyro Dec 15, 2025
7dfa2a7
Merge remote-tracking branch 'upstream/dev' into feature/libsession-m…
mpretty-cyro Dec 16, 2025
03df067
Fixed some build errors, resolved some TODOs, fixed a couple of bugs
mpretty-cyro Dec 17, 2025
089eccd
Added some commented out code as reference
mpretty-cyro Dec 17, 2025
55ba2aa
Merge remote-tracking branch 'upstream/dev' into feature/libsession-m…
mpretty-cyro Dec 17, 2025
179e91b
Fixed unit test build errors
mpretty-cyro Dec 17, 2025
6194833
Fixed a few more unit tests
mpretty-cyro Dec 17, 2025
6d8b2de
Fixed a couple of bugs found while fixing the unit tests
mpretty-cyro Dec 17, 2025
965a778
Fixed some more tests
mpretty-cyro Dec 17, 2025
508448c
Fixed a bug where default db values weren't being inserted during mig…
mpretty-cyro Dec 18, 2025
5e53101
Fixed a bug where default communities would use auth to retrieve disp…
mpretty-cyro Dec 18, 2025
e9cb273
Fixed a bug where the CommunityManager wasn't updating it's syncState
mpretty-cyro Dec 18, 2025
778a5b5
Fixed a bug where communities wouldn't identify mods/admins correctly…
mpretty-cyro Dec 18, 2025
0a3f7b4
Fixed a bunch more unit tests
mpretty-cyro Dec 18, 2025
83e4a74
Attempt to fix CI build issue
mpretty-cyro Dec 18, 2025
32a22b5
CI build error
mpretty-cyro Dec 18, 2025
282701f
More CI errors, added code to update the `accessExpiryTsMs` in the co…
mpretty-cyro Dec 18, 2025
b1ed813
Update libSession, some pro revocation list logic, fix warnings
mpretty-cyro Dec 18, 2025
03a9767
CI build errors -_-
mpretty-cyro Dec 18, 2025
8c525d6
Fixed bugs found during regression tests
mpretty-cyro Dec 19, 2025
2622fae
Fixed a bunch of bugs found during regression testing
mpretty-cyro Dec 19, 2025
60af69d
Fixed a few more issues from regression tests
mpretty-cyro Dec 19, 2025
6a63a07
Fixed a few more regression test issues
mpretty-cyro Dec 19, 2025
62c841c
Fixed another regression test issue
mpretty-cyro Dec 19, 2025
6e675fb
Fixed a few more regression issues
mpretty-cyro Dec 23, 2025
0b7d8b3
Fixed broken read receipts
mpretty-cyro Dec 23, 2025
4729f4c
Fixed broken community invitations and group description updates
mpretty-cyro Jan 1, 2026
4b56fbd
Fixed an issue with syncing deleted/kicked groups to linked devices
mpretty-cyro Jan 2, 2026
e7fdf3c
Fixed an issue where accepted message requests may not update the UI …
mpretty-cyro Jan 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
495 changes: 372 additions & 123 deletions Session.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@
buildConfiguration = "Debug_Compile_LibSession"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
enableAddressSanitizer = "YES"
enableASanStackUseAfterReturn = "YES"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
Expand Down
6 changes: 4 additions & 2 deletions Session/Calls/Call Management/SessionCall.swift
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,9 @@ public final class SessionCall: CurrentCallProtocol, WebRTCSessionDelegate {

let webRTCSession: WebRTCSession = self.webRTCSession
let timestampMs: Int64 = dependencies[cache: .snodeAPI].currentOffsetTimestampMs()
let disappearingMessagesConfiguration = try? thread.disappearingMessagesConfiguration.fetchOne(db)?.forcedWithDisappearAfterReadIfNeeded()
let disappearingMessagesConfiguration = try? DisappearingMessagesConfiguration
.fetchOne(db, id: thread.id)?
.forcedWithDisappearAfterReadIfNeeded()
let message: CallMessage = CallMessage(
uuid: self.uuid,
kind: .preOffer,
Expand Down Expand Up @@ -248,7 +250,7 @@ public final class SessionCall: CurrentCallProtocol, WebRTCSessionDelegate {
message: message,
threadId: thread.id,
interactionId: interaction?.id,
authMethod: try Authentication.with(db, swarmPublicKey: thread.id, using: dependencies)
authMethod: try Authentication.with(swarmPublicKey: thread.id, using: dependencies)
)
.retry(5)
// Start the timeout timer for the call
Expand Down
8 changes: 2 additions & 6 deletions Session/Calls/Call Management/SessionCallManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -209,11 +209,7 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
let call: SessionCall = dependencies[singleton: .storage].read({ [dependencies] db in
SessionCall(
for: caller,
contactName: Profile.displayName(
db,
id: caller,
threadVariant: .contact
),
contactName: Profile.displayName(db, id: caller),
uuid: uuid,
mode: mode,
using: dependencies
Expand All @@ -235,7 +231,7 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {

if
let conversationVC: ConversationVC = currentFrontMostViewController as? ConversationVC,
conversationVC.viewModel.threadData.threadId == call.sessionId
conversationVC.viewModel.state.threadId == call.sessionId
{
let callVC = CallVC(for: call, using: dependencies)
currentFrontMostViewController.present(callVC, animated: true, completion: nil)
Expand Down
56 changes: 28 additions & 28 deletions Session/Calls/WebRTC/WebRTCSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -181,14 +181,11 @@ public final class WebRTCSession: NSObject, RTCPeerConnectionDelegate {
}

dependencies[singleton: .storage]
.writePublisher { db -> (AuthenticationMethod, DisappearingMessagesConfiguration?) in
(
try Authentication.with(db, swarmPublicKey: thread.id, using: dependencies),
try DisappearingMessagesConfiguration.fetchOne(db, id: thread.id)
)
.writePublisher { db -> DisappearingMessagesConfiguration? in
try DisappearingMessagesConfiguration.fetchOne(db, id: thread.id)
}
.subscribe(on: DispatchQueue.global(qos: .userInitiated))
.tryFlatMap { authMethod, disappearingMessagesConfiguration in
.tryFlatMap { disappearingMessagesConfiguration in
try MessageSender.preparedSend(
message: CallMessage(
uuid: uuid,
Expand All @@ -201,7 +198,10 @@ public final class WebRTCSession: NSObject, RTCPeerConnectionDelegate {
namespace: .default,
interactionId: nil,
attachments: nil,
authMethod: authMethod,
authMethod: try Authentication.with(
swarmPublicKey: thread.id,
using: dependencies
),
onEvent: MessageSender.standardEventHandling(using: dependencies),
using: dependencies
).send(using: dependencies)
Expand All @@ -226,7 +226,7 @@ public final class WebRTCSession: NSObject, RTCPeerConnectionDelegate {
let mediaConstraints: RTCMediaConstraints = mediaConstraints(false)

return dependencies[singleton: .storage]
.readPublisher { [dependencies] db -> (AuthenticationMethod, DisappearingMessagesConfiguration?) in
.readPublisher { db -> DisappearingMessagesConfiguration? in
/// Ensure a thread exists for the `sessionId` and that it's a `contact` thread
guard
SessionThread
Expand All @@ -235,12 +235,9 @@ public final class WebRTCSession: NSObject, RTCPeerConnectionDelegate {
.isNotEmpty(db)
else { throw WebRTCSessionError.noThread }

return (
try Authentication.with(db, swarmPublicKey: sessionId, using: dependencies),
try DisappearingMessagesConfiguration.fetchOne(db, id: sessionId)
)
return try DisappearingMessagesConfiguration.fetchOne(db, id: sessionId)
}
.flatMap { [weak self, dependencies] authMethod, disappearingMessagesConfiguration in
.flatMap { [weak self, dependencies] disappearingMessagesConfiguration in
Future<Void, Error> { resolver in
self?.peerConnection?.answer(for: mediaConstraints) { [weak self] sdp, error in
if let error = error {
Expand Down Expand Up @@ -271,7 +268,10 @@ public final class WebRTCSession: NSObject, RTCPeerConnectionDelegate {
namespace: .default,
interactionId: nil,
attachments: nil,
authMethod: authMethod,
authMethod: try Authentication.with(
swarmPublicKey: sessionId,
using: dependencies
),
onEvent: MessageSender.standardEventHandling(using: dependencies),
using: dependencies
)
Expand Down Expand Up @@ -313,7 +313,7 @@ public final class WebRTCSession: NSObject, RTCPeerConnectionDelegate {
self.queuedICECandidates.removeAll()

return dependencies[singleton: .storage]
.readPublisher { [dependencies] db -> (AuthenticationMethod, DisappearingMessagesConfiguration?) in
.readPublisher { db -> DisappearingMessagesConfiguration? in
/// Ensure a thread exists for the `sessionId` and that it's a `contact` thread
guard
SessionThread
Expand All @@ -322,13 +322,10 @@ public final class WebRTCSession: NSObject, RTCPeerConnectionDelegate {
.isNotEmpty(db)
else { throw WebRTCSessionError.noThread }

return (
try Authentication.with(db, swarmPublicKey: contactSessionId, using: dependencies),
try DisappearingMessagesConfiguration.fetchOne(db, id: contactSessionId)
)
return try DisappearingMessagesConfiguration.fetchOne(db, id: contactSessionId)
}
.subscribe(on: DispatchQueue.global(qos: .userInitiated))
.tryFlatMap { [dependencies] authMethod, disappearingMessagesConfiguration in
.tryFlatMap { [dependencies] disappearingMessagesConfiguration in
Log.info(.calls, "Batch sending \(candidates.count) ICE candidates.")

return try MessageSender
Expand All @@ -346,7 +343,10 @@ public final class WebRTCSession: NSObject, RTCPeerConnectionDelegate {
namespace: .default,
interactionId: nil,
attachments: nil,
authMethod: authMethod,
authMethod: try Authentication.with(
swarmPublicKey: contactSessionId,
using: dependencies
),
onEvent: MessageSender.standardEventHandling(using: dependencies),
using: dependencies
)
Expand All @@ -368,7 +368,7 @@ public final class WebRTCSession: NSObject, RTCPeerConnectionDelegate {

public func endCall(with sessionId: String) {
return dependencies[singleton: .storage]
.readPublisher { [dependencies] db -> (AuthenticationMethod, DisappearingMessagesConfiguration?) in
.readPublisher { db -> DisappearingMessagesConfiguration? in
/// Ensure a thread exists for the `sessionId` and that it's a `contact` thread
guard
SessionThread
Expand All @@ -377,13 +377,10 @@ public final class WebRTCSession: NSObject, RTCPeerConnectionDelegate {
.isNotEmpty(db)
else { throw WebRTCSessionError.noThread }

return (
try Authentication.with(db, swarmPublicKey: sessionId, using: dependencies),
try DisappearingMessagesConfiguration.fetchOne(db, id: sessionId)
)
return try DisappearingMessagesConfiguration.fetchOne(db, id: sessionId)
}
.subscribe(on: DispatchQueue.global(qos: .userInitiated))
.tryFlatMap { [dependencies, uuid] authMethod, disappearingMessagesConfiguration in
.tryFlatMap { [dependencies, uuid] disappearingMessagesConfiguration in
Log.info(.calls, "Sending end call message.")

return try MessageSender
Expand All @@ -398,7 +395,10 @@ public final class WebRTCSession: NSObject, RTCPeerConnectionDelegate {
namespace: .default,
interactionId: nil,
attachments: nil,
authMethod: authMethod,
authMethod: try Authentication.with(
swarmPublicKey: sessionId,
using: dependencies
),
onEvent: MessageSender.standardEventHandling(using: dependencies),
using: dependencies
)
Expand Down
4 changes: 3 additions & 1 deletion Session/Closed Groups/EditGroupViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,9 @@ class EditGroupViewModel: SessionTableViewModel, NavigatableStateHolder, Observa
identifier: "Contact"
),
trailingImage: {
guard (dependencies.mutate(cache: .libSession) { $0.validateProProof(for: memberInfo.profile) }) else { return nil }
guard memberInfo.profile?.proFeatures.contains(.proBadge) == true else {
return nil
}

return SessionProBadge.trailingImage(
size: .small,
Expand Down
16 changes: 7 additions & 9 deletions Session/Closed Groups/NewClosedGroupVC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -439,15 +439,13 @@ final class NewClosedGroupVC: BaseVC, UITableViewDataSource, UITableViewDelegate
/// When this is triggered via the "Recreate Group" action for Legacy Groups the screen will have been
/// pushed instead of presented and, as a result, we need to dismiss the `activityIndicatorViewController`
/// and want the transition to be animated in order to behave nicely
await MainActor.run { [weak self, dependencies] in
dependencies[singleton: .app].presentConversationCreatingIfNeeded(
for: thread.id,
variant: thread.variant,
action: .none,
dismissing: (self?.presentingViewController ?? indicator),
animated: (self?.presentingViewController == nil)
)
}
await dependencies[singleton: .app].presentConversationCreatingIfNeeded(
for: thread.id,
variant: thread.variant,
action: .none,
dismissing: (self.presentingViewController ?? indicator),
animated: (self.presentingViewController == nil)
)
}
catch {
await MainActor.run { [weak self] in
Expand Down
58 changes: 22 additions & 36 deletions Session/Conversations/Context Menu/ContextMenuVC+Action.swift
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,11 @@ extension ContextMenuVC {

static func actions(
for cellViewModel: MessageViewModel,
in threadViewModel: SessionThreadViewModel,
threadInfo: ConversationInfoViewModel,
authMethod: AuthenticationMethod,
reactionsSupported: Bool,
recentReactionEmoji: [String],
isUserModeratorOrAdmin: Bool,
forMessageInfoScreen: Bool,
delegate: ContextMenuActionDelegate?,
using dependencies: Dependencies
Expand Down Expand Up @@ -221,18 +225,18 @@ extension ContextMenuVC {
cellViewModel.cellType == .genericAttachment ||
cellViewModel.cellType == .mediaMessage
) &&
(cellViewModel.attachments ?? []).count == 1 &&
(cellViewModel.attachments ?? []).first?.isVisualMedia == true &&
(cellViewModel.attachments ?? []).first?.isValid == true && (
(cellViewModel.attachments ?? []).first?.state == .downloaded ||
(cellViewModel.attachments ?? []).first?.state == .uploaded
cellViewModel.attachments.count == 1 &&
cellViewModel.attachments.first?.isVisualMedia == true &&
cellViewModel.attachments.first?.isValid == true && (
cellViewModel.attachments.first?.state == .downloaded ||
cellViewModel.attachments.first?.state == .uploaded
)
)
)
let canSave: Bool = {
switch cellViewModel.cellType {
case .mediaMessage:
return (cellViewModel.attachments ?? [])
return cellViewModel.attachments
.filter { attachment in
attachment.isValid &&
attachment.isVisualMedia && (
Expand All @@ -242,7 +246,7 @@ extension ContextMenuVC {
}.isEmpty == false

case .audio, .genericAttachment:
return (cellViewModel.attachments ?? [])
return cellViewModel.attachments
.filter { attachment in
attachment.isValid && (
attachment.state == .downloaded ||
Expand All @@ -258,40 +262,22 @@ extension ContextMenuVC {
cellViewModel.threadVariant != .community &&
!forMessageInfoScreen
)
let canDelete: Bool = (MessageViewModel.DeletionBehaviours.deletionActions(
let canDelete: Bool = ((try? MessageViewModel.DeletionBehaviours.deletionActions(
for: [cellViewModel],
with: threadViewModel,
threadInfo: threadInfo,
authMethod: authMethod,
isUserModeratorOrAdmin: isUserModeratorOrAdmin,
using: dependencies
) != nil)
)) != nil)
let canBan: Bool = (
cellViewModel.threadVariant == .community &&
dependencies[singleton: .openGroupManager].isUserModeratorOrAdmin(
publicKey: threadViewModel.currentUserSessionId,
for: threadViewModel.openGroupRoomToken,
on: threadViewModel.openGroupServer,
currentUserSessionIds: (threadViewModel.currentUserSessionIds ?? [])
)
isUserModeratorOrAdmin
)
let shouldShowEmojiActions: Bool = {
guard cellViewModel.threadVariant != .legacyGroup else { return false }

if cellViewModel.threadVariant == .community {
return (
!forMessageInfoScreen &&
dependencies[singleton: .openGroupManager].doesOpenGroupSupport(
capability: .reactions,
on: cellViewModel.threadOpenGroupServer
)
)
}
return (threadViewModel.threadIsMessageRequest != true && !forMessageInfoScreen)
}()

let recentEmojis: [EmojiWithSkinTones] = {
guard shouldShowEmojiActions else { return [] }
guard reactionsSupported else { return [] }

return (threadViewModel.recentReactionEmoji ?? [])
.compactMap { EmojiWithSkinTones(rawValue: $0) }
return recentReactionEmoji.compactMap { EmojiWithSkinTones(rawValue: $0) }
}()
let generatedActions: [Action] = [
(canRetry ? Action.retry(cellViewModel, delegate) : nil),
Expand All @@ -305,7 +291,7 @@ extension ContextMenuVC {
(forMessageInfoScreen ? nil : Action.info(cellViewModel, delegate)),
]
.appending(
contentsOf: (shouldShowEmojiActions ? recentEmojis : [])
contentsOf: (reactionsSupported ? recentEmojis : [])
.map { Action.react(cellViewModel, $0, delegate) }
)
.appending(forMessageInfoScreen ? nil : Action.emojiPlusButton(cellViewModel, delegate))
Expand All @@ -322,7 +308,7 @@ extension ContextMenuVC {
protocol ContextMenuActionDelegate {
func info(_ cellViewModel: MessageViewModel)
@MainActor func retry(_ cellViewModel: MessageViewModel, completion: (@MainActor () -> Void)?)
func reply(_ cellViewModel: MessageViewModel, completion: (() -> Void)?)
@MainActor func reply(_ cellViewModel: MessageViewModel, completion: (() -> Void)?)
func copy(_ cellViewModel: MessageViewModel, completion: (() -> Void)?)
func copySessionID(_ cellViewModel: MessageViewModel, completion: (() -> Void)?)
func delete(_ cellViewModel: MessageViewModel, completion: (() -> Void)?)
Expand Down
2 changes: 1 addition & 1 deletion Session/Conversations/ConversationSearch.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ extension ConversationSearchController: UISearchResultsUpdating {
.readPublisher { db -> [Interaction.TimestampInfo] in
try Interaction.idsForTermWithin(
threadId: threadId,
pattern: try SessionThreadViewModel.pattern(db, searchTerm: searchText)
pattern: try GlobalSearch.pattern(db, searchTerm: searchText)
)
.fetchAll(db)
}
Expand Down
Loading