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

import Foundation

public class ExperienceUpgradeFinder {

    // MARK: -

    public class func markAsViewed(experienceUpgrade: ExperienceUpgrade, transaction: DBWriteTransaction) {
        Logger.info("marking experience upgrade as seen \(experienceUpgrade.uniqueId)")
        experienceUpgrade.markAsViewed(transaction: transaction)
    }

    public class func markAsSnoozed(experienceUpgrade: ExperienceUpgrade, transaction: DBWriteTransaction) {
        Logger.info("marking experience upgrade as snoozed \(experienceUpgrade.uniqueId)")

        experienceUpgrade.markAsSnoozed(transaction: transaction)
    }

    public class func markAsComplete(
        experienceUpgradeManifest manifest: ExperienceUpgradeManifest,
        transaction: DBWriteTransaction,
    ) {
        markAsComplete(
            experienceUpgrade: ExperienceUpgrade.makeNew(withManifest: manifest),
            transaction: transaction,
        )
    }

    public class func markAsComplete(experienceUpgrade: ExperienceUpgrade, transaction: DBWriteTransaction) {
        guard experienceUpgrade.manifest.shouldComplete else {
            return Logger.info("Skipping marking complete for experience upgrade with uniqueId: \(experienceUpgrade.uniqueId)")
        }

        Logger.info("Marking complete experience upgrade with uniqueId: \(experienceUpgrade.uniqueId)")
        experienceUpgrade.markAsComplete(transaction: transaction)
    }

    public class func markAllCompleteForNewUser(transaction: DBWriteTransaction) {
        allKnownExperienceUpgrades(transaction: transaction)
            .filter { $0.manifest.skipForNewUsers }
            .forEach { markAsComplete(experienceUpgrade: $0, transaction: transaction) }
    }

    // MARK: -

    /// Returns an array of all recognized ``ExperienceUpgrade``s. Contains the
    /// persisted record if one exists and is applicable, and an in-memory
    /// model otherwise.
    public class func allKnownExperienceUpgrades(
        transaction: DBReadTransaction,
    ) -> [ExperienceUpgrade] {
        var experienceUpgrades = [ExperienceUpgrade]()
        var localManifestsWithoutRecords = ExperienceUpgradeManifest.wellKnownLocalUpgradeManifests

        // Load any experience upgrades with persisted records...
        ExperienceUpgrade.anyEnumerate(transaction: transaction) { experienceUpgrade, _ in
            if case .unrecognized = experienceUpgrade.manifest {
                // Ignore any no-longer-recognized records.
                return
            }

            guard experienceUpgrade.manifest.shouldSave else {
                // Ignore saved records that we no longer persist.
                return
            }

            experienceUpgrades.append(experienceUpgrade)
            localManifestsWithoutRecords.remove(experienceUpgrade.manifest)
        }

        // ...and instantiate new (in-memory) models for any local manifests
        // without persisted records.
        for localManifest in localManifestsWithoutRecords {
            experienceUpgrades.append(ExperienceUpgrade.makeNew(withManifest: localManifest))
        }

        return ExperienceUpgradeManifest.sortedByImportance(experienceUpgrades)
    }
}

public extension ExperienceUpgrade {
    var isSnoozed: Bool {
        guard
            lastSnoozedTimestamp > 0,
            snoozeCount > 0
        else {
            return false
        }

        // Check if enough time has passed since the last snooze date.
        let timeSinceLastSnooze = -Date(timeIntervalSince1970: lastSnoozedTimestamp).timeIntervalSinceNow
        return timeSinceLastSnooze <= manifest.snoozeDuration(forSnoozeCount: snoozeCount)
    }

    var hasPassedNumberOfDaysToShow: Bool {
        daysSinceFirstViewed > manifest.numberOfDaysToShowFor
    }

    var daysSinceFirstViewed: Int {
        guard firstViewedTimestamp > 0 else { return 0 }

        let secondsSinceFirstView = -Date(timeIntervalSince1970: firstViewedTimestamp).timeIntervalSinceNow
        return Int(secondsSinceFirstView / .day)
    }
}