diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index ced67d6371..8ba6dccf32 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -2515,6 +2515,7 @@ D9A85DC22BE1719C003F7045 /* MessageBackupGroupCallArchiver.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9A85DC12BE1719C003F7045 /* MessageBackupGroupCallArchiver.swift */; }; D9AA37A02A86E0910088EFFB /* OutgoingCallEventSyncMessageTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9AA379F2A86E0910088EFFB /* OutgoingCallEventSyncMessageTest.swift */; }; D9AA37A42A8A9A910088EFFB /* OutgoingGroupCallUpdateMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9AA37A32A8A9A910088EFFB /* OutgoingGroupCallUpdateMessage.swift */; }; + D9AA7D6E2D11F08A0014137C /* OutgoingDeviceNameChangeSyncMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9AA7D6D2D11F0750014137C /* OutgoingDeviceNameChangeSyncMessage.swift */; }; D9AD1D9528B9955C00B42E6F /* TSInfoMessage+GroupUpdateType+NSAttributedStringTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9AD1D9428B9955C00B42E6F /* TSInfoMessage+GroupUpdateType+NSAttributedStringTest.swift */; }; D9AE0ACF29186D7F0063488B /* IncomingContactSyncJobRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9AE0ACE29186D7F0063488B /* IncomingContactSyncJobRecord.swift */; }; D9AE0AD32918715E0063488B /* DonationReceiptCredentialRedemptionJobRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9AE0AD22918715E0063488B /* DonationReceiptCredentialRedemptionJobRecord.swift */; }; @@ -6280,6 +6281,7 @@ D9A85DC12BE1719C003F7045 /* MessageBackupGroupCallArchiver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageBackupGroupCallArchiver.swift; sourceTree = ""; }; D9AA379F2A86E0910088EFFB /* OutgoingCallEventSyncMessageTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutgoingCallEventSyncMessageTest.swift; sourceTree = ""; }; D9AA37A32A8A9A910088EFFB /* OutgoingGroupCallUpdateMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutgoingGroupCallUpdateMessage.swift; sourceTree = ""; }; + D9AA7D6D2D11F0750014137C /* OutgoingDeviceNameChangeSyncMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutgoingDeviceNameChangeSyncMessage.swift; sourceTree = ""; }; D9AD1D9428B9955C00B42E6F /* TSInfoMessage+GroupUpdateType+NSAttributedStringTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSInfoMessage+GroupUpdateType+NSAttributedStringTest.swift"; sourceTree = ""; }; D9AE0ACE29186D7F0063488B /* IncomingContactSyncJobRecord.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncomingContactSyncJobRecord.swift; sourceTree = ""; }; D9AE0AD22918715E0063488B /* DonationReceiptCredentialRedemptionJobRecord.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DonationReceiptCredentialRedemptionJobRecord.swift; sourceTree = ""; }; @@ -13443,6 +13445,7 @@ 6619A1BB2B2A8132004B38FE /* SentMessageTranscriptReceiver */, D9C0AE642BD7103100FCB05E /* InactiveLinkedDeviceFinder.swift */, 669941A02CC976B000DC99A1 /* LinkAndSyncManager.swift */, + D9AA7D6D2D11F0750014137C /* OutgoingDeviceNameChangeSyncMessage.swift */, F9C5CA21289453B100548EEE /* OWSBlockedPhoneNumbersMessage.h */, F9C5CA29289453B100548EEE /* OWSBlockedPhoneNumbersMessage.m */, F9C5CA1A289453B100548EEE /* OWSDevice.swift */, @@ -17323,6 +17326,7 @@ D979CC292AD3933B006AAC49 /* OutgoingCallEventSyncMessageManager.swift in Sources */, 5060EBBA2C7D211A00DF77AD /* OutgoingCallLinkUpdateMessage.swift in Sources */, D9DB37F32B71B037007B16C8 /* OutgoingCallLogEventSyncMessage.swift in Sources */, + D9AA7D6E2D11F08A0014137C /* OutgoingDeviceNameChangeSyncMessage.swift in Sources */, C13B9BB62A1819C7007F74C4 /* OutgoingEditMessage.swift in Sources */, C1EAECDF2A1EFC21008A3D58 /* OutgoingEditMessageSyncTranscript.swift in Sources */, D9AA37A42A8A9A910088EFFB /* OutgoingGroupCallUpdateMessage.swift in Sources */, diff --git a/Signal/src/ViewControllers/AppSettings/Linked Devices/LinkedDevicesView.swift b/Signal/src/ViewControllers/AppSettings/Linked Devices/LinkedDevicesView.swift index 2feacbd268..79d3ce1f06 100644 --- a/Signal/src/ViewControllers/AppSettings/Linked Devices/LinkedDevicesView.swift +++ b/Signal/src/ViewControllers/AppSettings/Linked Devices/LinkedDevicesView.swift @@ -55,8 +55,8 @@ class LinkedDevicesViewModel: ObservableObject { private let db: any DB private let deviceService: OWSDeviceService private let deviceStore: OWSDeviceStore - private let messageBackupErrorPresenter: MessageBackupErrorPresenter private let identityManager: OWSIdentityManager + private let messageBackupErrorPresenter: MessageBackupErrorPresenter #if DEBUG private let isPreview: Bool @@ -70,8 +70,8 @@ class LinkedDevicesViewModel: ObservableObject { db = DependenciesBridge.shared.db deviceService = DependenciesBridge.shared.deviceService deviceStore = DependenciesBridge.shared.deviceStore - messageBackupErrorPresenter = DependenciesBridge.shared.messageBackupErrorPresenter identityManager = DependenciesBridge.shared.identityManager + messageBackupErrorPresenter = DependenciesBridge.shared.messageBackupErrorPresenter databaseChangeObserver.appendDatabaseChangeDelegate(self) } @@ -214,14 +214,6 @@ class LinkedDevicesViewModel: ObservableObject { device: displayableDevice.device, toEncryptedName: encryptedName ) - - await db.awaitableWrite { tx in - deviceStore.setEncryptedName( - encryptedName, - for: displayableDevice.device, - tx: tx - ) - } } // MARK: DisplayableDevice diff --git a/SignalServiceKit/Devices/OWSDeviceService.swift b/SignalServiceKit/Devices/OWSDeviceService.swift index 7a9f73f4a8..edb8a1f8c8 100644 --- a/SignalServiceKit/Devices/OWSDeviceService.swift +++ b/SignalServiceKit/Devices/OWSDeviceService.swift @@ -35,6 +35,7 @@ public enum DeviceRenameError: Error { struct OWSDeviceServiceImpl: OWSDeviceService { private let db: any DB + private let deviceNameChangeSyncMessageSender: DeviceNameChangeSyncMessageSender private let deviceManager: OWSDeviceManager private let deviceStore: OWSDeviceStore private let networkManager: NetworkManager @@ -43,9 +44,15 @@ struct OWSDeviceServiceImpl: OWSDeviceService { db: any DB, deviceManager: OWSDeviceManager, deviceStore: OWSDeviceStore, - networkManager: NetworkManager + messageSenderJobQueue: MessageSenderJobQueue, + networkManager: NetworkManager, + threadStore: ThreadStore ) { self.db = db + self.deviceNameChangeSyncMessageSender = DeviceNameChangeSyncMessageSender( + messageSenderJobQueue: messageSenderJobQueue, + threadStore: threadStore + ) self.deviceManager = deviceManager self.deviceStore = deviceStore self.networkManager = networkManager @@ -141,6 +148,59 @@ struct OWSDeviceServiceImpl: OWSDeviceService { guard response.responseStatusCode == 204 else { throw DeviceRenameError.unspecified } + + await db.awaitableWrite { tx in + deviceStore.setEncryptedName( + encryptedName, + for: device, + tx: tx + ) + + guard let deviceId = UInt32(exactly: device.deviceId) else { + owsFailDebug("Failed to coerce device ID into UInt32!") + return + } + + deviceNameChangeSyncMessageSender.enqueueDeviceNameChangeSyncMessage( + forDeviceId: deviceId, + tx: tx + ) + } + } +} + +// MARK: - + +private struct DeviceNameChangeSyncMessageSender { + private let messageSenderJobQueue: MessageSenderJobQueue + private let threadStore: ThreadStore + + init(messageSenderJobQueue: MessageSenderJobQueue, threadStore: ThreadStore) { + self.messageSenderJobQueue = messageSenderJobQueue + self.threadStore = threadStore + } + + func enqueueDeviceNameChangeSyncMessage( + forDeviceId deviceId: UInt32, + tx: DBWriteTransaction + ) { + let sdsTx = SDSDB.shimOnlyBridge(tx) + + guard let localThread = threadStore.getOrCreateLocalThread(tx: tx) else { + owsFailDebug("Failed to create local thread!") + return + } + + let outgoingSyncMessage = OutgoingDeviceNameChangeSyncMessage( + deviceId: deviceId, + thread: localThread, + tx: sdsTx + ) + + messageSenderJobQueue.add( + message: .preprepared(transientMessageWithoutAttachments: outgoingSyncMessage), + transaction: sdsTx + ) } } diff --git a/SignalServiceKit/Devices/OutgoingDeviceNameChangeSyncMessage.swift b/SignalServiceKit/Devices/OutgoingDeviceNameChangeSyncMessage.swift new file mode 100644 index 0000000000..cd53e4f7e9 --- /dev/null +++ b/SignalServiceKit/Devices/OutgoingDeviceNameChangeSyncMessage.swift @@ -0,0 +1,42 @@ +// +// Copyright 2024 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only +// + +/// Informs other platforms that a linked device's name has changed, and they +/// should refresh their list of linked devices. +@objc(OutgoingDeviceNameChangeSyncMessage) +public class OutgoingDeviceNameChangeSyncMessage: OWSOutgoingSyncMessage { + + /// Exposed and nullable for compatibility with Mantle. + @objc(deviceId) + private(set) var deviceId: NSNumber! + + init( + deviceId: UInt32, + thread: TSThread, + tx: SDSAnyReadTransaction + ) { + self.deviceId = NSNumber(value: deviceId) + super.init(thread: thread, transaction: tx) + } + + required public init?(coder: NSCoder) { + super.init(coder: coder) + } + + required public init(dictionary dictionaryValue: [String: Any]!) throws { + try super.init(dictionary: dictionaryValue) + } + + override public var isUrgent: Bool { false } + + override public func syncMessageBuilder(transaction: SDSAnyReadTransaction) -> SSKProtoSyncMessageBuilder? { + let deviceNameChangeBuilder = SSKProtoSyncMessageDeviceNameChange.builder() + deviceNameChangeBuilder.setDeviceID(deviceId.uint32Value) + + let builder = SSKProtoSyncMessage.builder() + builder.setDeviceNameChange(deviceNameChangeBuilder.buildInfallibly()) + return builder + } +} diff --git a/SignalServiceKit/Environment/AppSetup.swift b/SignalServiceKit/Environment/AppSetup.swift index 1be006de70..97898130a2 100644 --- a/SignalServiceKit/Environment/AppSetup.swift +++ b/SignalServiceKit/Environment/AppSetup.swift @@ -1183,7 +1183,9 @@ public class AppSetup { db: db, deviceManager: deviceManager, deviceStore: deviceStore, - networkManager: networkManager + messageSenderJobQueue: messageSenderJobQueue, + networkManager: networkManager, + threadStore: threadStore ) let inactiveLinkedDeviceFinder = InactiveLinkedDeviceFinderImpl( dateProvider: dateProvider, diff --git a/SignalServiceKit/Messages/MessageReceiver.swift b/SignalServiceKit/Messages/MessageReceiver.swift index 1f2672ade7..971b124525 100644 --- a/SignalServiceKit/Messages/MessageReceiver.swift +++ b/SignalServiceKit/Messages/MessageReceiver.swift @@ -646,7 +646,7 @@ public final class MessageReceiver { deleteForMeProto: deleteForMe, tx: tx.asV2Write ) - } else if let deviceNameChange = syncMessage.deviceNameChange { + } else if syncMessage.deviceNameChange != nil { Task { let deviceService = DependenciesBridge.shared.deviceService