Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
signalapp
GitHub Repository: signalapp/Signal-iOS
Path: blob/main/SignalServiceKit/Messages/BodyRanges/SpoilerRevealState.swift
1 views
//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//

import Foundation
public import LibSignalClient

/// Used to uniquely identify one iteration of an interaction even if edits are applied.
/// Note that the interaction's uniqueId, rowId, and sortId always point to the latest edit,
/// so when an edit is applied they point to the new version not the old version.
/// This is used instead to continue to point to the old version.
///
/// authorAci is allowed to be null for outgoing or system messages because timestamp can
/// be considered unique on its own for messages generated by oneself.
/// Incoming messages should always have non-null authorAci.
/// NOTE: be careful using this for messages from before the introduction of uuids.
/// Currently used for text formatting spoilers which were introduced after uuids.
public struct InteractionSnapshotIdentifier: Equatable, Hashable {
    let timestamp: UInt64
    // Note: this will always be the aci for incoming messages
    // and nil for local/outgoing messages.
    let authorAci: Aci?

    public init(timestamp: UInt64, authorAci: Aci?) {
        self.timestamp = timestamp
        self.authorAci = authorAci
    }

    public static func fromInteraction(_ interaction: TSInteraction) -> Self {
        return .init(timestamp: interaction.timestamp, authorAci: Aci.parseFrom(aciString: (interaction as? TSIncomingMessage)?.authorUUID))
    }

    // Not strictly an interaction, but can contain spoilers.
    public static func fromStoryMessage(_ storyMessage: StoryMessage) -> Self {
        return .init(timestamp: storyMessage.timestamp, authorAci: storyMessage.authorAci)
    }
}

// MARK: -

@objc
public class SpoilerRevealState: NSObject {
    private var revealedSpoilerIdsByMessage = [InteractionSnapshotIdentifier: Set<StyleIdType>]()

    /// Returns the set of IDs in the ordered list of spoiler ranges for a given message that
    /// should be revealed.
    public func revealedSpoilerIds(
        interactionIdentifier: InteractionSnapshotIdentifier,
    ) -> Set<StyleIdType> {
        return revealedSpoilerIdsByMessage[interactionIdentifier] ?? []
    }

    public func setSpoilerRevealed(
        withID id: StyleIdType,
        interactionIdentifier: InteractionSnapshotIdentifier,
    ) {
        var revealedIds = revealedSpoilerIdsByMessage[interactionIdentifier] ?? Set()
        revealedIds.insert(id)
        revealedSpoilerIdsByMessage[interactionIdentifier] = revealedIds
    }

    public typealias Snapshot = [InteractionSnapshotIdentifier: Set<StyleIdType>]

    public func snapshot() -> Snapshot {
        return revealedSpoilerIdsByMessage
    }
}