diff --git a/Postbox.xcodeproj/project.pbxproj b/Postbox.xcodeproj/project.pbxproj index b4e89f3f2c..9c9d7efdfd 100644 --- a/Postbox.xcodeproj/project.pbxproj +++ b/Postbox.xcodeproj/project.pbxproj @@ -132,7 +132,6 @@ D0B844051DAB91B5005F29E1 /* MediaBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = D055BD321B7D3D2D00F06C0A /* MediaBox.swift */; }; D0B844061DAB91B5005F29E1 /* MediaResourceStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = D05F09A51C9E9F9300BB6F96 /* MediaResourceStatus.swift */; }; D0B844071DAB91B5005F29E1 /* MediaResource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0CE63F51CA1CCB2002BC462 /* MediaResource.swift */; }; - D0B844081DAB91B5005F29E1 /* CachedMediaResource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DE76F81D9293B8002B8809 /* CachedMediaResource.swift */; }; D0B8440A1DAB91B5005F29E1 /* RandomAccessMediaResourceContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = D09ADF091D2E89F300C8208D /* RandomAccessMediaResourceContext.swift */; }; D0B844511DAC04FE005F29E1 /* PeerPresence.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0B844501DAC04FE005F29E1 /* PeerPresence.swift */; }; D0C07F6A1B67DB4800966E43 /* SwiftSignalKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0C07F691B67DB4800966E43 /* SwiftSignalKit.framework */; }; @@ -151,7 +150,6 @@ D0D511041D64D91C00A97B8A /* IpcNotifier.h in Headers */ = {isa = PBXBuildFile; fileRef = D0D511031D64D75200A97B8A /* IpcNotifier.h */; settings = {ATTRIBUTES = (Public, ); }; }; D0D949F31D35302600740E02 /* RandomAccessResourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D949F21D35302600740E02 /* RandomAccessResourceTests.swift */; }; D0D949F51D35353900740E02 /* MappedFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0D949F41D35353900740E02 /* MappedFile.swift */; }; - D0DE76F91D9293B8002B8809 /* CachedMediaResource.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DE76F81D9293B8002B8809 /* CachedMediaResource.swift */; }; D0DF0C8F1D81A350008AEB01 /* PeerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DF0C8E1D81A350008AEB01 /* PeerView.swift */; }; D0E1DE151C5E1C6900C7826E /* ViewTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E1DE141C5E1C6900C7826E /* ViewTracker.swift */; }; D0E3A7501B28A7E300A402D9 /* Postbox.h in Headers */ = {isa = PBXBuildFile; fileRef = D0E3A74F1B28A7E300A402D9 /* Postbox.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -259,7 +257,6 @@ D0D511031D64D75200A97B8A /* IpcNotifier.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IpcNotifier.h; sourceTree = ""; }; D0D949F21D35302600740E02 /* RandomAccessResourceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RandomAccessResourceTests.swift; sourceTree = ""; }; D0D949F41D35353900740E02 /* MappedFile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MappedFile.swift; sourceTree = ""; }; - D0DE76F81D9293B8002B8809 /* CachedMediaResource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CachedMediaResource.swift; sourceTree = ""; }; D0DF0C8E1D81A350008AEB01 /* PeerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeerView.swift; sourceTree = ""; }; D0E1DE141C5E1C6900C7826E /* ViewTracker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewTracker.swift; sourceTree = ""; }; D0E3A74A1B28A7E300A402D9 /* Postbox.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Postbox.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -320,7 +317,6 @@ D055BD321B7D3D2D00F06C0A /* MediaBox.swift */, D05F09A51C9E9F9300BB6F96 /* MediaResourceStatus.swift */, D0CE63F51CA1CCB2002BC462 /* MediaResource.swift */, - D0DE76F81D9293B8002B8809 /* CachedMediaResource.swift */, D09ADF091D2E89F300C8208D /* RandomAccessMediaResourceContext.swift */, ); name = "Media Box"; @@ -749,7 +745,6 @@ D0B4185C1D7DFE2F004562A4 /* MurMurHash32.m in Sources */, D0B418241D7DFE0C004562A4 /* SimpleSet.swift in Sources */, D03C53601DAD5C9C004C17B3 /* ChatListHole.swift in Sources */, - D0B844081DAB91B5005F29E1 /* CachedMediaResource.swift in Sources */, D0B4184C1D7DFE20004562A4 /* ContactPeerIdsView.swift in Sources */, D0B843EC1DAB91A0005F29E1 /* GlobalMessageIdsTable.swift in Sources */, D0B4184F1D7DFE20004562A4 /* ChatListHolesView.swift in Sources */, @@ -835,7 +830,6 @@ D08C713C1C51283C00779C0F /* MessageHistoryIndexTable.swift in Sources */, D0F9E86D1C5A0E5D00037222 /* MetadataTable.swift in Sources */, D0E3A7841B28AE0900A402D9 /* Peer.swift in Sources */, - D0DE76F91D9293B8002B8809 /* CachedMediaResource.swift in Sources */, D0D510F61D63BBE100A97B8A /* MessageHistoryOperation.swift in Sources */, D0AB0B901D65D4AB002C78E7 /* UnsentMessageIndicesView.swift in Sources */, D03120F81DA53FF4006A2A60 /* PeerPresenceTable.swift in Sources */, diff --git a/Postbox/CachedMediaResource.swift b/Postbox/CachedMediaResource.swift deleted file mode 100644 index 464695d4e4..0000000000 --- a/Postbox/CachedMediaResource.swift +++ /dev/null @@ -1,4 +0,0 @@ - -public protocol CachedMediaResource { - var id: String { get } -} diff --git a/Postbox/ItemCollectionsView.swift b/Postbox/ItemCollectionsView.swift index c03c4c6c5b..dbc74c1b56 100644 --- a/Postbox/ItemCollectionsView.swift +++ b/Postbox/ItemCollectionsView.swift @@ -20,6 +20,10 @@ public struct ItemCollectionViewEntryIndex: Comparable { return lhs.collectionIndex < rhs.collectionIndex } } + + public static func lowerBound(collectionIndex: Int32, collectionId: ItemCollectionId) -> ItemCollectionViewEntryIndex { + return ItemCollectionViewEntryIndex(collectionIndex: collectionIndex, collectionId: collectionId, itemIndex: ItemCollectionItemIndex(index: 0, id: 0)) + } } public struct ItemCollectionViewEntry { diff --git a/Postbox/MediaBox.swift b/Postbox/MediaBox.swift index 48610ff890..6d6ebb2f4b 100644 --- a/Postbox/MediaBox.swift +++ b/Postbox/MediaBox.swift @@ -87,7 +87,6 @@ private final class CachedMediaResourceRepresentationContext { public final class MediaBox { let basePath: String - let buffer = WriteBuffer() private let statusQueue = Queue() private let concurrentQueue = Queue.concurrentDefaultQueue() @@ -243,7 +242,6 @@ public final class MediaBox { assert(pathExtension == nil) return Signal { subscriber in let disposable = MetaDisposable() - self.concurrentQueue.async { let paths = self.storePathsForId(resource.id) if let completeSize = fileSize(paths.complete) { diff --git a/Postbox/Message.swift b/Postbox/Message.swift index d48a25c826..923c712c9b 100644 --- a/Postbox/Message.swift +++ b/Postbox/Message.swift @@ -77,12 +77,16 @@ public struct MessageId: Hashable, Comparable, CustomStringConvertible { } public func ==(lhs: MessageId, rhs: MessageId) -> Bool { - return lhs.id == rhs.id && lhs.namespace == rhs.namespace + return lhs.id == rhs.id && lhs.namespace == rhs.namespace && lhs.peerId == rhs.peerId } public func <(lhs: MessageId, rhs: MessageId) -> Bool { if lhs.namespace == rhs.namespace { - return lhs.id < rhs.id + if lhs.id == rhs.id { + return lhs.peerId < rhs.peerId + } else { + return lhs.id < rhs.id + } } else { return lhs.namespace < rhs.namespace } diff --git a/Postbox/MessageHistoryIndexTable.swift b/Postbox/MessageHistoryIndexTable.swift index 05ae154959..4e32585703 100644 --- a/Postbox/MessageHistoryIndexTable.swift +++ b/Postbox/MessageHistoryIndexTable.swift @@ -66,6 +66,7 @@ enum MessageHistoryIndexOperation { case InsertHole(MessageHistoryHole) case Remove(MessageIndex) case Update(MessageIndex, InternalStoreMessage) + case UpdateTimestamp(MessageIndex, Int32) } private let HistoryEntryTypeMask: Int8 = 1 @@ -96,6 +97,15 @@ private func readHistoryIndexEntry(_ peerId: PeerId, namespace: MessageId.Namesp } } +private func modifyHistoryIndexEntryTimestamp(value: ReadBuffer, timestamp: Int32) -> MemoryBuffer { + let buffer = WriteBuffer() + buffer.write(value.memory.advanced(by: 0), offset: 0, length: 1) + var varTimestamp: Int32 = timestamp + buffer.write(&varTimestamp, offset: 0, length: 4) + buffer.write(value.memory.advanced(by: 5), offset: 0, length: value.length - 5) + return buffer +} + final class MessageHistoryIndexTable: Table { let globalMessageIdsNamespace: Int32 let globalMessageIdsTable: GlobalMessageIdsTable @@ -355,6 +365,16 @@ final class MessageHistoryIndexTable: Table { } } + func updateTimestamp(_ id: MessageId, timestamp: Int32, operations: inout [MessageHistoryIndexOperation]) { + if let previousData = self.valueBox.get(self.tableId, key: self.key(id)), let previousEntry = self.get(id), case let .Message(previousIndex) = previousEntry, previousIndex.timestamp != timestamp { + let updatedEntry = modifyHistoryIndexEntryTimestamp(value: previousData, timestamp: timestamp) + self.valueBox.remove(self.tableId, key: self.key(id)) + self.valueBox.set(self.tableId, key: self.key(id), value: updatedEntry) + + operations.append(.UpdateTimestamp(MessageIndex(id: id, timestamp: previousIndex.timestamp), timestamp)) + } + } + func fillHole(_ id: MessageId, fillType: HoleFill, tagMask: MessageTags?, messages: [InternalStoreMessage], operations: inout [MessageHistoryIndexOperation]) { self.ensureInitialized(id.peerId, operations: &operations) diff --git a/Postbox/MessageHistoryOperation.swift b/Postbox/MessageHistoryOperation.swift index a71b7be8b2..4bfef815ae 100644 --- a/Postbox/MessageHistoryOperation.swift +++ b/Postbox/MessageHistoryOperation.swift @@ -6,4 +6,5 @@ enum MessageHistoryOperation { case Remove([MessageIndex]) case UpdateReadState(CombinedPeerReadState) case UpdateEmbeddedMedia(MessageIndex, ReadBuffer) + case UpdateTimestamp(MessageIndex, Int32) } diff --git a/Postbox/MessageHistoryTable.swift b/Postbox/MessageHistoryTable.swift index b334fe1730..012b87dfe1 100644 --- a/Postbox/MessageHistoryTable.swift +++ b/Postbox/MessageHistoryTable.swift @@ -142,7 +142,7 @@ final class MessageHistoryTable: Table { let message = self.justInsertMessage(storeMessage, sharedKey: sharedKey, sharedBuffer: sharedBuffer, sharedEncoder: sharedEncoder) outputOperations.append(.InsertMessage(message)) if message.flags.contains(.Unsent) && !message.flags.contains(.Failed) { - self.unsentTable.add(MessageIndex(message), operations: &unsentMessageOperations) + self.unsentTable.add(message.id, operations: &unsentMessageOperations) } let tags = message.tags.rawValue if tags != 0 { @@ -174,6 +174,13 @@ final class MessageHistoryTable: Table { if let message = self.justUpdate(index, message: storeMessage, sharedKey: sharedKey, sharedBuffer: sharedBuffer, sharedEncoder: sharedEncoder, unsentMessageOperations: &unsentMessageOperations) { outputOperations.append(.InsertMessage(message)) } + case let .UpdateTimestamp(index, timestamp): + if accumulatedRemoveIndices.count != 0 { + outputOperations.append(.Remove(accumulatedRemoveIndices)) + accumulatedRemoveIndices.removeAll() + } + self.justUpdateTimestamp(index, timestamp: timestamp) + outputOperations.append(.UpdateTimestamp(index, timestamp)) } } if accumulatedRemoveIndices.count != 0 { @@ -272,6 +279,12 @@ final class MessageHistoryTable: Table { self.processIndexOperations(id.peerId, operations: operations, processedOperationsByPeerId: &operationsByPeerId, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations) } + func updateMessageTimestamp(_ id: MessageId, timestamp: Int32, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?]) { + var operations: [MessageHistoryIndexOperation] = [] + self.messageHistoryIndexTable.updateTimestamp(id, timestamp: timestamp, operations: &operations) + self.processIndexOperations(id.peerId, operations: operations, processedOperationsByPeerId: &operationsByPeerId, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations) + } + func updateMedia(_ id: MediaId, media: Media?, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], updatedMedia: inout [MediaId: Media?]) { if let previousMedia = self.messageMediaTable.get(id, embedded: { index, id in return self.embeddedMediaForIndex(index, id: id) @@ -410,6 +423,14 @@ final class MessageHistoryTable: Table { return nil } + func offsetPendingMessagesTimestamps(lowerBound: MessageId, timestamp: Int32, operationsByPeerId: inout [PeerId: [MessageHistoryOperation]], unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation], updatedPeerReadStateOperations: inout [PeerId: PeerReadStateSynchronizationOperation?]) { + for messageId in self.unsentTable.get() { + if messageId.peerId == lowerBound.peerId && messageId.namespace == lowerBound.namespace && messageId.id > lowerBound.id { + self.updateMessageTimestamp(messageId, timestamp: timestamp, operationsByPeerId: &operationsByPeerId, unsentMessageOperations: &unsentMessageOperations, updatedPeerReadStateOperations: &updatedPeerReadStateOperations) + } + } + } + private func justInsertMessage(_ message: InternalStoreMessage, sharedKey: ValueBoxKey, sharedBuffer: WriteBuffer, sharedEncoder: Encoder) -> IntermediateMessage { sharedBuffer.reset() @@ -567,7 +588,7 @@ final class MessageHistoryTable: Table { } if message.flags.contains(.Unsent) && !message.flags.contains(.Failed) { - self.unsentTable.remove(index, operations: &unsentMessageOperations) + self.unsentTable.remove(index.id, operations: &unsentMessageOperations) } let tags = message.tags.rawValue @@ -705,6 +726,47 @@ final class MessageHistoryTable: Table { private func justUpdate(_ index: MessageIndex, message: InternalStoreMessage, sharedKey: ValueBoxKey, sharedBuffer: WriteBuffer, sharedEncoder: Encoder, unsentMessageOperations: inout [IntermediateMessageHistoryUnsentOperation]) -> IntermediateMessage? { if let previousMessage = self.getMessage(index) { + var previousEmbeddedMediaWithIds: [(MediaId, Media)] = [] + if previousMessage.embeddedMediaData.length > 4 { + var embeddedMediaCount: Int32 = 0 + let previousEmbeddedMediaData = previousMessage.embeddedMediaData + previousEmbeddedMediaData.read(&embeddedMediaCount, offset: 0, length: 4) + for _ in 0 ..< embeddedMediaCount { + var mediaLength: Int32 = 0 + previousEmbeddedMediaData.read(&mediaLength, offset: 0, length: 4) + if let media = Decoder(buffer: MemoryBuffer(memory: previousEmbeddedMediaData.memory + previousEmbeddedMediaData.offset, capacity: Int(mediaLength), length: Int(mediaLength), freeWhenDone: false)).decodeRootObject() as? Media { + if let mediaId = media.id { + previousEmbeddedMediaWithIds.append((mediaId, media)) + } + } + previousEmbeddedMediaData.skip(Int(mediaLength)) + } + } + + var previousMediaIds = Set() + for (mediaId, _) in previousEmbeddedMediaWithIds { + previousMediaIds.insert(mediaId) + } + for mediaId in previousMessage.referencedMedia { + previousMediaIds.insert(mediaId) + } + + var updatedMediaIds = Set() + for media in message.media { + if let mediaId = media.id { + updatedMediaIds.insert(mediaId) + } + } + + if previousMediaIds != updatedMediaIds || index.id != message.id { + for (_, media) in previousEmbeddedMediaWithIds { + self.messageMediaTable.removeEmbeddedMedia(media) + } + for mediaId in previousMessage.referencedMedia { + self.messageMediaTable.removeReference(mediaId) + } + } + self.valueBox.remove(self.tableId, key: self.key(index)) if !previousMessage.tags.isEmpty { self.tagsTable.remove(previousMessage.tags, index: index) @@ -715,13 +777,13 @@ final class MessageHistoryTable: Table { switch (previousMessage.flags.contains(.Unsent) && !previousMessage.flags.contains(.Failed), message.flags.contains(.Unsent) && !message.flags.contains(.Failed)) { case (true, false): - self.unsentTable.remove(index, operations: &unsentMessageOperations) + self.unsentTable.remove(index.id, operations: &unsentMessageOperations) case (false, true): - self.unsentTable.add(MessageIndex(message), operations: &unsentMessageOperations) + self.unsentTable.add(message.id, operations: &unsentMessageOperations) case (true, true): if index != MessageIndex(message) { - self.unsentTable.remove(index, operations: &unsentMessageOperations) - self.unsentTable.add(MessageIndex(message), operations: &unsentMessageOperations) + self.unsentTable.remove(index.id, operations: &unsentMessageOperations) + self.unsentTable.add(message.id, operations: &unsentMessageOperations) } case (false, false): break @@ -731,34 +793,6 @@ final class MessageHistoryTable: Table { assertionFailure() } - var previousMedia: [Media] = [] - if previousMessage.embeddedMediaData.length > 4 { - var embeddedMediaCount: Int32 = 0 - let previousEmbeddedMediaData = previousMessage.embeddedMediaData - previousEmbeddedMediaData.read(&embeddedMediaCount, offset: 0, length: 4) - for _ in 0 ..< embeddedMediaCount { - var mediaLength: Int32 = 0 - previousEmbeddedMediaData.read(&mediaLength, offset: 0, length: 4) - if let media = Decoder(buffer: MemoryBuffer(memory: previousEmbeddedMediaData.memory + previousEmbeddedMediaData.offset, capacity: Int(mediaLength), length: Int(mediaLength), freeWhenDone: false)).decodeRootObject() as? Media { - previousMedia.append(media) - } - previousEmbeddedMediaData.skip(Int(mediaLength)) - } - } - - var previousReferencedMedia: [Media] = [] - for mediaId in previousMessage.referencedMedia { - if let media = self.messageMediaTable.get(mediaId, embedded: { _ in - return nil - }) { - previousMedia.append(media) - } - } - - var removedMediaIds: [MediaId] = [] - - //self.updateMedia(from: previousMedia, to: message.media) - sharedBuffer.reset() var type: Int8 = 0 @@ -844,10 +878,10 @@ final class MessageHistoryTable: Table { if let mediaId = media.id { let mediaInsertResult = self.messageMediaTable.set(media, index: MessageIndex(message), messageHistoryTable: self) switch mediaInsertResult { - case let .Embed(media): - embeddedMedia.append(media) - case .Reference: - referencedMedia.append(mediaId) + case let .Embed(media): + embeddedMedia.append(media) + case .Reference: + referencedMedia.append(mediaId) } } else { embeddedMedia.append(media) @@ -885,6 +919,30 @@ final class MessageHistoryTable: Table { } } + private func justUpdateTimestamp(_ index: MessageIndex, timestamp: Int32) { + if let previousMessage = self.getMessage(index) { + self.valueBox.remove(self.tableId, key: self.key(index)) + var updatedMessage = IntermediateMessage(stableId: previousMessage.stableId, id: previousMessage.id, timestamp: timestamp, flags: previousMessage.flags, tags: previousMessage.tags, forwardInfo: previousMessage.forwardInfo, authorId: previousMessage.authorId, text: previousMessage.text, attributesData: previousMessage.attributesData, embeddedMediaData: previousMessage.embeddedMediaData, referencedMedia: previousMessage.referencedMedia) + self.storeIntermediateMessage(updatedMessage, sharedKey: self.key(index)) + + let tags = previousMessage.tags.rawValue + if tags != 0 { + for i in 0 ..< 32 { + let currentTags = tags >> UInt32(i) + if currentTags == 0 { + break + } + + if (currentTags & 1) != 0 { + let tag = MessageTags(rawValue: 1 << UInt32(i)) + self.tagsTable.remove(tag, index: index) + self.tagsTable.add(tag, index: MessageIndex(id: index.id, timestamp: timestamp)) + } + } + } + } + } + func unembedMedia(_ index: MessageIndex, id: MediaId) -> Media? { if let message = self.getMessage(index), message.embeddedMediaData.length > 4 { var embeddedMediaCount: Int32 = 0 diff --git a/Postbox/MessageHistoryUnsentTable.swift b/Postbox/MessageHistoryUnsentTable.swift index ccb5e8627b..a0324ab3fe 100644 --- a/Postbox/MessageHistoryUnsentTable.swift +++ b/Postbox/MessageHistoryUnsentTable.swift @@ -1,18 +1,17 @@ import Foundation enum IntermediateMessageHistoryUnsentOperation { - case Insert(MessageIndex) - case Remove(MessageIndex) + case Insert(MessageId) + case Remove(MessageId) } final class MessageHistoryUnsentTable: Table { - private let sharedKey = ValueBoxKey(length: 4 + 4 + 4 + 8) + private let sharedKey = ValueBoxKey(length: 4 + 4 + 8) - private func key(_ index: MessageIndex) -> ValueBoxKey { - self.sharedKey.setInt32(0, value: index.timestamp) - self.sharedKey.setInt32(4, value: index.id.namespace) - self.sharedKey.setInt32(4 + 4, value: index.id.id) - self.sharedKey.setInt64(4 + 4 + 4, value: index.id.peerId.toInt64()) + private func key(_ id: MessageId) -> ValueBoxKey { + self.sharedKey.setInt32(0, value: id.namespace) + self.sharedKey.setInt32(4, value: id.id) + self.sharedKey.setInt64(4 + 4, value: id.peerId.toInt64()) return self.sharedKey } @@ -24,7 +23,7 @@ final class MessageHistoryUnsentTable: Table { } private func upperBound() -> ValueBoxKey { - let key = ValueBoxKey(length: 4 + 4 + 4 + 8) + let key = ValueBoxKey(length: 4 + 4 + 8) memset(key.memory, 0xff, key.length) return key } @@ -33,23 +32,23 @@ final class MessageHistoryUnsentTable: Table { super.init(valueBox: valueBox, tableId: tableId) } - func add(_ index: MessageIndex, operations: inout [IntermediateMessageHistoryUnsentOperation]) { - self.valueBox.set(self.tableId, key: self.key(index), value: MemoryBuffer()) - operations.append(.Insert(index)) + func add(_ id: MessageId, operations: inout [IntermediateMessageHistoryUnsentOperation]) { + self.valueBox.set(self.tableId, key: self.key(id), value: MemoryBuffer()) + operations.append(.Insert(id)) } - func remove(_ index: MessageIndex, operations: inout [IntermediateMessageHistoryUnsentOperation]) { - self.valueBox.remove(self.tableId, key: self.key(index)) - operations.append(.Remove(index)) + func remove(_ id: MessageId, operations: inout [IntermediateMessageHistoryUnsentOperation]) { + self.valueBox.remove(self.tableId, key: self.key(id)) + operations.append(.Remove(id)) } - func get() -> [MessageIndex] { - var indices: [MessageIndex] = [] + func get() -> [MessageId] { + var ids: [MessageId] = [] self.valueBox.range(self.tableId, start: self.lowerBound(), end: self.upperBound(), keys: { key in - indices.append(MessageIndex(id: MessageId(peerId: PeerId(key.getInt64(4 + 4 + 4)), namespace: key.getInt32(4), id: key.getInt32(4 + 4)), timestamp: key.getInt32(0))) + ids.append(MessageId(peerId: PeerId(key.getInt64(4 + 4)), namespace: key.getInt32(0), id: key.getInt32(4))) return true }, limit: 0) - return indices + return ids } override func beforeCommit() { diff --git a/Postbox/MessageHistoryView.swift b/Postbox/MessageHistoryView.swift index 77230a6353..631ab927e2 100644 --- a/Postbox/MessageHistoryView.swift +++ b/Postbox/MessageHistoryView.swift @@ -46,6 +46,20 @@ enum MutableMessageHistoryEntry { return .HoleEntry(hole, location) } } + + func updatedTimestamp(_ timestamp: Int32) -> MutableMessageHistoryEntry { + switch self { + case let .IntermediateMessageEntry(message, location): + var updatedMessage = IntermediateMessage(stableId: message.stableId, id: message.id, timestamp: timestamp, flags: message.flags, tags: message.tags, forwardInfo: message.forwardInfo, authorId: message.authorId, text: message.text, attributesData: message.attributesData, embeddedMediaData: message.embeddedMediaData, referencedMedia: message.referencedMedia) + return .IntermediateMessageEntry(updatedMessage, location) + case let .MessageEntry(message, location): + var updatedMessage = Message(stableId: message.stableId, id: message.id, timestamp: timestamp, flags: message.flags, tags: message.tags, forwardInfo: message.forwardInfo, author: message.author, text: message.text, attributes: message.attributes, media: message.media, peers: message.peers, associatedMessages: message.associatedMessages) + return .MessageEntry(updatedMessage, location) + case let .HoleEntry(hole, location): + var updatedHole = MessageHistoryHole(stableId: hole.stableId, maxIndex: MessageIndex(id: hole.maxIndex.id, timestamp: timestamp), min: hole.min, tags: hole.tags) + return .HoleEntry(updatedHole, location) + } + } } public struct MessageHistoryEntryLocation: Equatable { @@ -278,6 +292,16 @@ final class MutableMessageHistoryView { break } } + case let .UpdateTimestamp(index, timestamp): + for i in 0 ..< self.entries.count { + let entry = self.entries[i] + if entry.index == index { + self.remove(Set([index]), context: context) + self.add(entry.updatedTimestamp(timestamp), holeFillDirections: [:]) + hasChanges = true + break + } + } } } diff --git a/Postbox/Postbox.swift b/Postbox/Postbox.swift index f97c8e1a96..a344e41ec0 100644 --- a/Postbox/Postbox.swift +++ b/Postbox/Postbox.swift @@ -122,8 +122,12 @@ public final class Modifier { self.postbox?.replaceRecentPeerIds(peerIds) } - public func updateMessage(_ index: MessageIndex, update: (Message) -> StoreMessage) { - self.postbox?.updateMessage(index, update: update) + public func updateMessage(_ id: MessageId, update: (Message) -> StoreMessage) { + self.postbox?.updateMessage(id, update: update) + } + + public func offsetPendingMessagesTimestamps(lowerBound: MessageId, timestamp: Int32) { + self.postbox?.offsetPendingMessagesTimestamps(lowerBound: lowerBound, timestamp: timestamp) } public func updateMedia(_ id: MediaId, update: Media?) { @@ -325,7 +329,7 @@ public final class Postbox { self.metadataTable = MetadataTable(valueBox: self.valueBox, tableId: 0) let userVersion: Int32? = self.metadataTable.userVersion() - let currentUserVersion: Int32 = 11 + let currentUserVersion: Int32 = 14 if userVersion != currentUserVersion { self.valueBox.drop() @@ -388,7 +392,7 @@ public final class Postbox { return self.cachedPeerDataTable.get(peerId) }, getPeerPresence: { peerId in return self.peerPresenceTable.get(peerId) - },unsentMessageIndices: self.messageHistoryUnsentTable!.get(), synchronizePeerReadStateOperations: self.synchronizeReadStateTable!.get()) + },unsentMessageIds: self.messageHistoryUnsentTable!.get(), synchronizePeerReadStateOperations: self.synchronizeReadStateTable!.get()) print("(Postbox initialization took \((CFAbsoluteTimeGetCurrent() - startTime) * 1000.0) ms") }) @@ -735,7 +739,7 @@ public final class Postbox { for table in self.tables { table.clearMemoryCache() } - self.viewTracker.refreshViewsDueToExternalTransaction(fetchAroundChatEntries: self.fetchAroundChatEntries, fetchAroundHistoryEntries: self.fetchAroundHistoryEntries, fetchUnsendMessageIndices: { + self.viewTracker.refreshViewsDueToExternalTransaction(fetchAroundChatEntries: self.fetchAroundChatEntries, fetchAroundHistoryEntries: self.fetchAroundHistoryEntries, fetchUnsentMessageIds: { return self.messageHistoryUnsentTable!.get() }, fetchSynchronizePeerReadStateOperations: { return self.synchronizeReadStateTable!.get() @@ -851,13 +855,17 @@ public final class Postbox { self.peerRatingTable.replace(items: peerIds) } - fileprivate func updateMessage(_ index: MessageIndex, update: (Message) -> StoreMessage) { - if let intermediateMessage = self.messageHistoryTable.getMessage(index) { + fileprivate func updateMessage(_ id: MessageId, update: (Message) -> StoreMessage) { + if let indexEntry = self.messageHistoryIndexTable.get(id), let intermediateMessage = self.messageHistoryTable.getMessage(indexEntry.index) { let message = self.renderIntermediateMessage(intermediateMessage) - self.messageHistoryTable.updateMessage(index.id, message: update(message), operationsByPeerId: &self.currentOperationsByPeerId, unsentMessageOperations: &self.currentUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations) + self.messageHistoryTable.updateMessage(id, message: update(message), operationsByPeerId: &self.currentOperationsByPeerId, unsentMessageOperations: &self.currentUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations) } } + fileprivate func offsetPendingMessagesTimestamps(lowerBound: MessageId, timestamp: Int32) { + self.messageHistoryTable.offsetPendingMessagesTimestamps(lowerBound: lowerBound, timestamp: timestamp, operationsByPeerId: &self.currentOperationsByPeerId, unsentMessageOperations: &self.currentUnsentOperations, updatedPeerReadStateOperations: &self.currentUpdatedSynchronizeReadStateOperations) + } + fileprivate func updateMedia(_ id: MediaId, update: Media?) { self.messageHistoryTable.updateMedia(id, media: update, operationsByPeerId: &self.currentOperationsByPeerId, updatedMedia: &self.currentUpdatedMedia) } @@ -883,9 +891,9 @@ public final class Postbox { return filteredIds } - public func modify(_ f: @escaping(Modifier) -> T) -> Signal { + public func modify(userInteractive: Bool = false, _ f: @escaping(Modifier) -> T) -> Signal { return Signal { subscriber in - self.queue.justDispatch { + let f: () -> Void = { self.valueBox.begin() self.afterBegin() let result = f(Modifier(postbox: self)) @@ -903,18 +911,23 @@ public final class Postbox { self.masterClientId.set(.single(updatedMasterClientId)) } } + if userInteractive { + self.queue.justDispatchWithQoS(qos: DispatchQoS.userInteractive, f) + } else { + self.queue.justDispatch(f) + } return EmptyDisposable } } public func aroundUnreadMessageHistoryViewForPeerId(_ peerId: PeerId, count: Int, tagMask: MessageTags? = nil) -> Signal<(MessageHistoryView, ViewUpdateType), NoError> { - return self.modify { modifier -> Signal<(MessageHistoryView, ViewUpdateType), NoError> in + return self.modify(userInteractive: true, { modifier -> Signal<(MessageHistoryView, ViewUpdateType), NoError> in var index = MessageHistoryAnchorIndex(index: MessageIndex.upperBound(peerId: peerId), exact: true) if let maxReadIndex = self.messageHistoryTable.maxReadIndex(peerId) { index = maxReadIndex } return self.syncAroundMessageHistoryViewForPeerId(peerId, index: index.index, count: count, anchorIndex: index, unreadIndex: index.index, fixedCombinedReadState: nil, tagMask: tagMask) - } |> switchToLatest + }) |> switchToLatest } public func aroundIdMessageHistoryViewForPeerId(_ peerId: PeerId, count: Int, messageId: MessageId, tagMask: MessageTags? = nil) -> Signal<(MessageHistoryView, ViewUpdateType), NoError> { @@ -1173,11 +1186,11 @@ public final class Postbox { } } - public func unsentMessageIndicesView() -> Signal { + public func unsentMessageIdsView() -> Signal { return Signal { subscriber in let disposable = MetaDisposable() self.queue.async { - disposable.set(self.viewTracker.unsentMessageIndicesViewSignal().start(next: { view in + disposable.set(self.viewTracker.unsentMessageIdsViewSignal().start(next: { view in subscriber.putNext(view) })) } diff --git a/Postbox/UnsentMessageHistoryView.swift b/Postbox/UnsentMessageHistoryView.swift index a6322bf1e7..6769deb967 100644 --- a/Postbox/UnsentMessageHistoryView.swift +++ b/Postbox/UnsentMessageHistoryView.swift @@ -1,16 +1,16 @@ import Foundation final class UnsentMessageHistoryView { - var indices: Set + var ids: Set - init(indices: [MessageIndex]) { - self.indices = Set(indices) + init(ids: [MessageId]) { + self.ids = Set(ids) } - func refreshDueToExternalTransaction(fetchUnsendMessageIndices: () -> [MessageIndex]) -> Bool { - let indices = Set(fetchUnsendMessageIndices()) - if indices != self.indices { - self.indices = indices + func refreshDueToExternalTransaction(fetchUnsentMessageIds: () -> [MessageId]) -> Bool { + let ids = Set(fetchUnsentMessageIds()) + if ids != self.ids { + self.ids = ids return true } else { return false @@ -21,14 +21,14 @@ final class UnsentMessageHistoryView { var updated = false for operation in operations { switch operation { - case let .Insert(index): - if !self.indices.contains(index) { - self.indices.insert(index) + case let .Insert(id): + if !self.ids.contains(id) { + self.ids.insert(id) updated = true } - case let .Remove(index): - if self.indices.contains(index) { - self.indices.remove(index) + case let .Remove(id): + if self.ids.contains(id) { + self.ids.remove(id) updated = true } } diff --git a/Postbox/UnsentMessageIndicesView.swift b/Postbox/UnsentMessageIndicesView.swift index e7047877f5..59848c6b7c 100644 --- a/Postbox/UnsentMessageIndicesView.swift +++ b/Postbox/UnsentMessageIndicesView.swift @@ -1,9 +1,9 @@ import Foundation -public final class UnsentMessageIndicesView { - public let indices: Set +public final class UnsentMessageIdsView { + public let ids: Set - init(_ indices: Set) { - self.indices = indices + init(_ ids: Set) { + self.ids = ids } } diff --git a/Postbox/ViewTracker.swift b/Postbox/ViewTracker.swift index 96960d4a9d..14b5b0c173 100644 --- a/Postbox/ViewTracker.swift +++ b/Postbox/ViewTracker.swift @@ -37,14 +37,14 @@ final class ViewTracker { private let chatListHolesViewSubscribers = Bag>() private var unsentMessageView: UnsentMessageHistoryView - private let unsendMessageIndicesViewSubscribers = Bag>() + private let unsendMessageIdsViewSubscribers = Bag>() private var synchronizeReadStatesView: MutableSynchronizePeerReadStatesView private let synchronizePeerReadStatesViewSubscribers = Bag>() private var peerViews = Bag<(MutablePeerView, ValuePipe)>() - init(queue: Queue, fetchEarlierHistoryEntries: @escaping (PeerId, MessageIndex?, Int, MessageTags?) -> [MutableMessageHistoryEntry], fetchLaterHistoryEntries: @escaping (PeerId, MessageIndex?, Int, MessageTags?) -> [MutableMessageHistoryEntry], fetchEarlierChatEntries: @escaping (MessageIndex?, Int) -> [MutableChatListEntry], fetchLaterChatEntries: @escaping (MessageIndex?, Int) -> [MutableChatListEntry], fetchAnchorIndex: @escaping (MessageId) -> MessageHistoryAnchorIndex?, renderMessage: @escaping (IntermediateMessage) -> Message, getPeer: @escaping (PeerId) -> Peer?, getPeerNotificationSettings: @escaping (PeerId) -> PeerNotificationSettings?, getCachedPeerData: @escaping (PeerId) -> CachedPeerData?, getPeerPresence: @escaping (PeerId) -> PeerPresence?, unsentMessageIndices: [MessageIndex], synchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation]) { + init(queue: Queue, fetchEarlierHistoryEntries: @escaping (PeerId, MessageIndex?, Int, MessageTags?) -> [MutableMessageHistoryEntry], fetchLaterHistoryEntries: @escaping (PeerId, MessageIndex?, Int, MessageTags?) -> [MutableMessageHistoryEntry], fetchEarlierChatEntries: @escaping (MessageIndex?, Int) -> [MutableChatListEntry], fetchLaterChatEntries: @escaping (MessageIndex?, Int) -> [MutableChatListEntry], fetchAnchorIndex: @escaping (MessageId) -> MessageHistoryAnchorIndex?, renderMessage: @escaping (IntermediateMessage) -> Message, getPeer: @escaping (PeerId) -> Peer?, getPeerNotificationSettings: @escaping (PeerId) -> PeerNotificationSettings?, getCachedPeerData: @escaping (PeerId) -> CachedPeerData?, getPeerPresence: @escaping (PeerId) -> PeerPresence?, unsentMessageIds: [MessageId], synchronizePeerReadStateOperations: [PeerId: PeerReadStateSynchronizationOperation]) { self.queue = queue self.fetchEarlierHistoryEntries = fetchEarlierHistoryEntries self.fetchLaterHistoryEntries = fetchLaterHistoryEntries @@ -57,7 +57,7 @@ final class ViewTracker { self.getCachedPeerData = getCachedPeerData self.getPeerPresence = getPeerPresence - self.unsentMessageView = UnsentMessageHistoryView(indices: unsentMessageIndices) + self.unsentMessageView = UnsentMessageHistoryView(ids: unsentMessageIds) self.synchronizeReadStatesView = MutableSynchronizePeerReadStatesView(operations: synchronizePeerReadStateOperations) } @@ -165,7 +165,7 @@ final class ViewTracker { self.peerViews.remove(index) } - func refreshViewsDueToExternalTransaction(fetchAroundChatEntries: (_ index: MessageIndex, _ count: Int) -> (entries: [MutableChatListEntry], earlier: MutableChatListEntry?, later: MutableChatListEntry?), fetchAroundHistoryEntries: (_ index: MessageIndex, _ count: Int, _ tagMask: MessageTags?) -> (entries: [MutableMessageHistoryEntry], lower: MutableMessageHistoryEntry?, upper: MutableMessageHistoryEntry?), fetchUnsendMessageIndices: () -> [MessageIndex], fetchSynchronizePeerReadStateOperations: () -> [PeerId: PeerReadStateSynchronizationOperation]) { + func refreshViewsDueToExternalTransaction(fetchAroundChatEntries: (_ index: MessageIndex, _ count: Int) -> (entries: [MutableChatListEntry], earlier: MutableChatListEntry?, later: MutableChatListEntry?), fetchAroundHistoryEntries: (_ index: MessageIndex, _ count: Int, _ tagMask: MessageTags?) -> (entries: [MutableMessageHistoryEntry], lower: MutableMessageHistoryEntry?, upper: MutableMessageHistoryEntry?), fetchUnsentMessageIds: () -> [MessageId], fetchSynchronizePeerReadStateOperations: () -> [PeerId: PeerReadStateSynchronizationOperation]) { var updateTrackedHolesPeerIds: [PeerId] = [] for (peerId, bag) in self.messageHistoryViews { @@ -192,7 +192,7 @@ final class ViewTracker { self.updateTrackedHoles(peerId) } - if self.unsentMessageView.refreshDueToExternalTransaction(fetchUnsendMessageIndices: fetchUnsendMessageIndices) { + if self.unsentMessageView.refreshDueToExternalTransaction(fetchUnsentMessageIds: fetchUnsentMessageIds) { self.unsentViewUpdated() } @@ -371,8 +371,8 @@ final class ViewTracker { } private func unsentViewUpdated() { - for subscriber in self.unsendMessageIndicesViewSubscribers.copyItems() { - subscriber.putNext(UnsentMessageIndicesView(self.unsentMessageView.indices)) + for subscriber in self.unsendMessageIdsViewSubscribers.copyItems() { + subscriber.putNext(UnsentMessageIdsView(self.unsentMessageView.ids)) } } @@ -426,14 +426,14 @@ final class ViewTracker { } } - func unsentMessageIndicesViewSignal() -> Signal { + func unsentMessageIdsViewSignal() -> Signal { return Signal { subscriber in let disposable = MetaDisposable() self.queue.async { - subscriber.putNext(UnsentMessageIndicesView(self.unsentMessageView.indices)) + subscriber.putNext(UnsentMessageIdsView(self.unsentMessageView.ids)) - let pipe = ValuePipe() - let index = self.unsendMessageIndicesViewSubscribers.add(pipe) + let pipe = ValuePipe() + let index = self.unsendMessageIdsViewSubscribers.add(pipe) let pipeDisposable = pipe.signal().start(next: { view in subscriber.putNext(view) @@ -441,7 +441,7 @@ final class ViewTracker { disposable.set(ActionDisposable { pipeDisposable.dispose() - self.unsendMessageIndicesViewSubscribers.remove(index) + self.unsendMessageIdsViewSubscribers.remove(index) }) } return disposable