Merge branch 'master' into f/directory-search

Co-authored-by: Claude <noreply@anthropic.com>

# Conflicts:
#	apps/simplex-directory-service/src/Directory/Events.hs
#	apps/simplex-directory-service/src/Directory/Service.hs
#	src/Simplex/Chat/Core.hs
#	src/Simplex/Chat/Library/Internal.hs
#	tests/Bots/DirectoryTests.hs
This commit is contained in:
spaced4ndy committed 2026-10-12 17:34:32 +00:00
commit 0e06ded808
996 files changed
+48336 -3431

No files matched your search

+57 -15
View File
@@ -1376,8 +1376,12 @@ func apiUpdateProfile(profile: Profile) async throws -> (Profile, [Contact])? {
switch r {
case .result(.userProfileNoChange): return (profile, [])
case let .result(.userProfileUpdated(_, _, toProfile, updateSummary)): return (toProfile, updateSummary.changedContacts)
case .error(.errorStore(.duplicateName)): return nil;
default: throw r.unexpected
case .error(.errorStore(.duplicateName)), .error(.error(.userExists)): return nil;
default:
await MainActor.run {
showErrorAlert(r.unexpected, NSLocalizedString("Error saving profile", comment: "alert title"))
}
throw r.unexpected
}
}
@@ -2378,6 +2382,10 @@ func startChat(refreshInvitations: Bool = true, onboarding: Bool = false) throws
ChatReceiver.shared.start()
m.chatRunning = true
chatLastStartGroupDefault.set(Date.now)
if shouldDeleteDatabaseBackupsDefault.get() {
deleteDatabaseBackups()
shouldDeleteDatabaseBackupsDefault.set(false)
}
}
func startChatWithTemporaryDatabase(ctrl: chat_ctrl) throws -> User? {
@@ -2714,7 +2722,8 @@ func processReceivedMsg(_ res: ChatEvent) async {
}
case let .groupChatItemsDeleted(user, groupInfo, chatItemIDs, _, member_):
await groupChatItemsDeleted(user, groupInfo, chatItemIDs, member_)
case let .receivedGroupInvitation(user, groupInfo, _, _):
case let .receivedGroupInvitation(user, groupInfo, _, _),
let .receivedGroupInvitationViaGroup(user, groupInfo, _, _, _):
if active(user) {
await MainActor.run {
m.updateGroup(groupInfo) // update so that repeat group invitations are not duplicated
@@ -2927,23 +2936,19 @@ func processReceivedMsg(_ res: ChatEvent) async {
m.callInvitations[invitation.contact.id] = invitation
}
activateCall(invitation)
case let .callOffer(_, contact, callType, offer, sharedKey, _):
case let .callOffer(_, contact, callType, offer, sharedKey, askConfirmation):
await withCall(contact) { call in
await MainActor.run {
call.callState = .offerReceived
call.sharedKey = sharedKey
}
let useRelay = UserDefaults.standard.bool(forKey: DEFAULT_WEBRTC_POLICY_RELAY)
let iceServers = getIceServers()
logger.debug(".callOffer useRelay \(useRelay)")
logger.debug(".callOffer iceServers \(String(describing: iceServers))")
await m.callCommand.processCommand(.offer(
offer: offer.rtcSession,
iceCandidates: offer.rtcIceCandidates,
media: callType.media, aesKey: sharedKey,
iceServers: iceServers,
relay: useRelay
))
if askConfirmation {
showUnencryptedCallAlert(call) {
Task { await processCallOffer(callType, offer, sharedKey) }
}
} else {
await processCallOffer(callType, offer, sharedKey)
}
}
case let .callAnswer(_, contact, answer):
await withCall(contact) { call in
@@ -3080,6 +3085,43 @@ func processReceivedMsg(_ res: ChatEvent) async {
logger.debug("processReceivedMsg: ignoring \(res.responseType), not in call with the contact \(contact.id)")
}
}
func processCallOffer(_ callType: CallType, _ offer: WebRTCSession, _ sharedKey: String?) async {
let useRelay = UserDefaults.standard.bool(forKey: DEFAULT_WEBRTC_POLICY_RELAY)
let iceServers = getIceServers()
logger.debug(".callOffer useRelay \(useRelay)")
logger.debug(".callOffer iceServers \(String(describing: iceServers))")
await m.callCommand.processCommand(.offer(
offer: offer.rtcSession,
iceCandidates: offer.rtcIceCandidates,
media: callType.media, aesKey: sharedKey,
iceServers: iceServers,
relay: useRelay
))
}
func showUnencryptedCallAlert(_ call: Call, onContinue: @escaping () -> Void) {
DispatchQueue.main.async {
showAlert(
NSLocalizedString("Call is not encrypted", comment: "alert title"),
message: String.localizedStringWithFormat(NSLocalizedString("%@ accepted the call without end-to-end encryption.", comment: "alert message"), call.contact.displayName),
actions: {[
UIAlertAction(title: NSLocalizedString("End call", comment: "alert action"), style: .destructive) { _ in
// the call may have ended, or a new one started with the same contact, while the alert was shown
guard m.activeCall === call else { return }
if let uuid = call.callUUID {
CallController.shared.endCall(callUUID: uuid)
} else {
CallController.shared.endCall(call: call) {}
}
},
UIAlertAction(title: NSLocalizedString("Continue", comment: "alert action"), style: .default) { _ in
if m.activeCall === call { onContinue() }
}
]}
)
}
}
}
func switchToLocalSession() {