Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
signalapp
GitHub Repository: signalapp/Signal-iOS
Path: blob/main/SignalUI/Stories/SelectMyStoryRecipientsViewController.swift
1 views
//
// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//

import Foundation
public import SignalServiceKit

public class SelectMyStoryRecipientsViewController: BaseMemberViewController {
    let thread: TSPrivateStoryThread
    let mode: TSThreadStoryViewMode
    var recipientSet: OrderedSet<PickedRecipient>
    let originalRecipientSet: Set<PickedRecipient>

    override public var hasUnsavedChanges: Bool { originalRecipientSet != recipientSet.unorderedMembers }

    let completionBlock: () -> Void

    public static func load(
        for thread: TSPrivateStoryThread,
        mode: TSThreadStoryViewMode,
        tx: DBReadTransaction,
        completionBlock: @escaping () -> Void,
    ) -> SelectMyStoryRecipientsViewController {
        let storyRecipientManager = DependenciesBridge.shared.storyRecipientManager
        return SelectMyStoryRecipientsViewController(
            thread: thread,
            recipientAddresses: failIfThrows {
                try storyRecipientManager.fetchRecipients(forStoryThread: thread, tx: tx)
            }.map { $0.address },
            mode: mode,
            completionBlock: completionBlock,
        )
    }

    private init(
        thread: TSPrivateStoryThread,
        recipientAddresses: [SignalServiceAddress],
        mode: TSThreadStoryViewMode,
        completionBlock: @escaping () -> Void,
    ) {
        self.thread = thread
        self.mode = mode
        if thread.storyViewMode == mode {
            self.recipientSet = OrderedSet(recipientAddresses.map { .for(address: $0) })
        } else {
            self.recipientSet = OrderedSet()
        }
        self.originalRecipientSet = self.recipientSet.unorderedMembers
        self.completionBlock = completionBlock
        super.init()

        memberViewDelegate = self
    }

    // MARK: - View Lifecycle

    override public func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        updateBarButtons()
    }

    private func updateBarButtons() {
        navigationItem.leftBarButtonItem = .cancelButton { [weak self] in
            self?.dismissPressed()
        }

        navigationItem.rightBarButtonItem = .systemItem(.save) { [weak self] in
            self?.savePressed()
        }
        navigationItem.rightBarButtonItem?.isEnabled = hasUnsavedChanges

        switch mode {
        case .explicit:
            if recipientSet.isEmpty {
                title = OWSLocalizedString(
                    "STORY_SELECT_ALLOWED_CONNECTIONS_VIEW_TITLE",
                    comment: "The title for the 'select connections for story' view.",
                )
            } else {
                let format = OWSLocalizedString(
                    "STORY_SELECT_ALLOWED_CONNECTIONS_VIEW_TITLE_%d",
                    tableName: "PluralAware",
                    comment: "The title for the 'select connections for story' view if already some connections are selected. Embeds {{number}} of connections.",
                )
                title = String.localizedStringWithFormat(format, recipientSet.count)
            }
        case .blockList:
            if recipientSet.isEmpty {
                title = OWSLocalizedString(
                    "STORY_SELECT_EXCLUDED_CONNECTIONS_VIEW_TITLE",
                    comment: "The title for the 'select excluded connections for story' view.",
                )
            } else {
                let format = OWSLocalizedString(
                    "STORY_SELECT_EXCLUDED_CONNECTIONS_VIEW_TITLE_%d",
                    tableName: "PluralAware",
                    comment: "The title for the 'select excluded connections for story' view if already some connections are selected. Embeds {{number}} of excluded connections.",
                )
                title = String.localizedStringWithFormat(format, recipientSet.count)
            }
        case .default, .disabled:
            owsFailDebug("Unexpected mode")
        }
    }

    // MARK: - Actions

    private func savePressed() {
        AssertIsOnMainThread()

        SSKEnvironment.shared.databaseStorageRef.write { transaction in
            let recipientFetcher = DependenciesBridge.shared.recipientFetcher
            let recipientIds = self.recipientSet.orderedMembers.lazy.compactMap { $0.address?.serviceId }.map {
                return recipientFetcher.fetchOrCreate(serviceId: $0, tx: transaction).id
            }
            self.thread.updateWithStoryViewMode(
                self.mode,
                storyRecipientIds: .setTo(Array(recipientIds)),
                updateStorageService: true,
                transaction: transaction,
            )
        }

        completionBlock()

        dismiss(animated: true)
    }
}

// MARK: -

extension SelectMyStoryRecipientsViewController: MemberViewDelegate {
    public var memberViewRecipientSet: OrderedSet<PickedRecipient> { recipientSet }

    public var memberViewHasUnsavedChanges: Bool { hasUnsavedChanges }

    public func memberViewRemoveRecipient(_ recipient: PickedRecipient) {
        recipientSet.remove(recipient)
        updateBarButtons()
    }

    public func memberViewAddRecipient(_ recipient: PickedRecipient) -> Bool {
        recipientSet.append(recipient)
        updateBarButtons()
        return true
    }

    public func memberViewShouldShowMemberCount() -> Bool { false }

    public func memberViewShouldAllowBlockedSelection() -> Bool { mode == .blockList }

    public func memberViewMemberCountForDisplay() -> Int { recipientSet.count }

    public func memberViewIsPreExistingMember(_ recipient: PickedRecipient, transaction: DBReadTransaction) -> Bool { false }

    public func memberViewCustomIconNameForPickedMember(_ recipient: PickedRecipient) -> String? {
        mode == .blockList ? "x-circle-fill" : nil
    }

    public func memberViewCustomIconColorForPickedMember(_ recipient: PickedRecipient) -> UIColor? {
        mode == .blockList ? .ows_accentRed : nil
    }

    public func memberViewDismiss() {
        dismiss(animated: true)
    }
}