mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-09 06:43:25 +01:00
Randomizes featured source + app order at app launch
This commit is contained in:
@@ -312,7 +312,7 @@ private extension FeaturedViewController
|
||||
fetchRequest.returnsObjectsAsFaults = false
|
||||
fetchRequest.sortDescriptors = [
|
||||
// Sort by Source first to group into sections.
|
||||
NSSortDescriptor(keyPath: \StoreApp.sourceIdentifier, ascending: true),
|
||||
NSSortDescriptor(keyPath: \StoreApp._source?.featuredSortID, ascending: true),
|
||||
|
||||
// Show uninstalled apps first.
|
||||
// Sorting by StoreApp.installedApp crashes because InstalledApp does not respond to compare:
|
||||
@@ -324,8 +324,8 @@ private extension FeaturedViewController
|
||||
// Instead, sort by StoreApp.featuringSource.identifier, which will be either nil OR source ID.
|
||||
NSSortDescriptor(keyPath: \StoreApp.featuringSource?.identifier, ascending: false),
|
||||
|
||||
// Sort by name.
|
||||
NSSortDescriptor(keyPath: \StoreApp.name, ascending: true),
|
||||
// Randomize order within sections.
|
||||
NSSortDescriptor(keyPath: \StoreApp.featuredSortID, ascending: true),
|
||||
|
||||
// Sanity check to ensure stable ordering
|
||||
NSSortDescriptor(keyPath: \StoreApp.bundleIdentifier, ascending: true)
|
||||
@@ -346,14 +346,14 @@ private extension FeaturedViewController
|
||||
let primaryFetchRequest = fetchRequest.copy() as! NSFetchRequest<StoreApp>
|
||||
primaryFetchRequest.predicate = sourceHasRemainingAppsPredicate
|
||||
|
||||
let primaryController = NSFetchedResultsController(fetchRequest: primaryFetchRequest, managedObjectContext: DatabaseManager.shared.viewContext, sectionNameKeyPath: #keyPath(StoreApp.sourceIdentifier), cacheName: nil)
|
||||
let primaryController = NSFetchedResultsController(fetchRequest: primaryFetchRequest, managedObjectContext: DatabaseManager.shared.viewContext, sectionNameKeyPath: #keyPath(StoreApp._source.featuredSortID), cacheName: nil)
|
||||
let primaryDataSource = RSTFetchedResultsCollectionViewDataSource<StoreApp>(fetchedResultsController: primaryController)
|
||||
primaryDataSource.liveFetchLimit = 5
|
||||
|
||||
let secondaryFetchRequest = fetchRequest.copy() as! NSFetchRequest<StoreApp>
|
||||
secondaryFetchRequest.predicate = NSCompoundPredicate(notPredicateWithSubpredicate: sourceHasRemainingAppsPredicate)
|
||||
|
||||
let secondaryController = NSFetchedResultsController(fetchRequest: secondaryFetchRequest, managedObjectContext: DatabaseManager.shared.viewContext, sectionNameKeyPath: #keyPath(StoreApp.sourceIdentifier), cacheName: nil)
|
||||
let secondaryController = NSFetchedResultsController(fetchRequest: secondaryFetchRequest, managedObjectContext: DatabaseManager.shared.viewContext, sectionNameKeyPath: #keyPath(StoreApp._source.featuredSortID), cacheName: nil)
|
||||
let secondaryDataSource = RSTFetchedResultsCollectionViewDataSource<StoreApp>(fetchedResultsController: secondaryController)
|
||||
secondaryDataSource.liveFetchLimit = 5
|
||||
|
||||
|
||||
@@ -217,6 +217,7 @@
|
||||
</entity>
|
||||
<entity name="Source" representedClassName="Source" syncable="YES">
|
||||
<attribute name="error" optional="YES" attributeType="Transformable" valueTransformerName="ALTSecureValueTransformer"/>
|
||||
<attribute name="featuredSortID" optional="YES" attributeType="String"/>
|
||||
<attribute name="hasFeaturedApps" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="headerImageURL" optional="YES" attributeType="URI"/>
|
||||
<attribute name="iconURL" optional="YES" attributeType="URI"/>
|
||||
@@ -242,6 +243,7 @@
|
||||
<attribute name="category" optional="YES" attributeType="String"/>
|
||||
<attribute name="developerName" attributeType="String"/>
|
||||
<attribute name="downloadURL" attributeType="URI"/>
|
||||
<attribute name="featuredSortID" optional="YES" attributeType="String"/>
|
||||
<attribute name="iconURL" attributeType="URI"/>
|
||||
<attribute name="isBeta" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="isHiddenWithoutPledge" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
|
||||
@@ -178,6 +178,49 @@ public extension DatabaseManager
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateFeaturedSortIDs() async
|
||||
{
|
||||
let context = DatabaseManager.shared.persistentContainer.newBackgroundContext()
|
||||
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy // DON'T use our custom merge policy, because that one ignores changes to featuredSortID.
|
||||
await context.performAsync {
|
||||
do
|
||||
{
|
||||
// Randomize source order
|
||||
let fetchRequest = Source.fetchRequest()
|
||||
let sources = try context.fetch(fetchRequest)
|
||||
|
||||
for source in sources
|
||||
{
|
||||
source.featuredSortID = UUID().uuidString
|
||||
}
|
||||
|
||||
try context.save()
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logger.main.error("Failed to update source order. \(error.localizedDescription, privacy: .public)")
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
// Randomize app order
|
||||
let fetchRequest = StoreApp.fetchRequest()
|
||||
let apps = try context.fetch(fetchRequest)
|
||||
|
||||
for app in apps
|
||||
{
|
||||
app.featuredSortID = UUID().uuidString
|
||||
}
|
||||
|
||||
try context.save()
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logger.main.error("Failed to update app order. \(error.localizedDescription, privacy: .public)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension DatabaseManager
|
||||
@@ -372,8 +415,12 @@ private extension DatabaseManager
|
||||
do
|
||||
{
|
||||
try context.save()
|
||||
|
||||
Task(priority: .high) {
|
||||
await self.updateFeaturedSortIDs()
|
||||
completionHandler(.success(()))
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
completionHandler(.failure(error))
|
||||
|
||||
@@ -234,6 +234,12 @@ open class MergePolicy: RSTRelationshipPreservingMergePolicy
|
||||
sortedScreenshotIDsByGlobalAppID[globallyUniqueID] = contextScreenshotIDs
|
||||
}
|
||||
|
||||
// Revert contextApp.featuredSortID to database value (if it exists).
|
||||
if let featuredSortID = databaseObject.featuredSortID
|
||||
{
|
||||
contextApp.featuredSortID = featuredSortID
|
||||
}
|
||||
|
||||
case let databaseObject as Source:
|
||||
guard let conflictedObject = conflict.conflictingObjects.first as? Source else { break }
|
||||
|
||||
@@ -263,6 +269,12 @@ open class MergePolicy: RSTRelationshipPreservingMergePolicy
|
||||
featuredAppIDsBySourceID[databaseObject.identifier] = contextSource.featuredApps?.map { $0.bundleIdentifier }
|
||||
}
|
||||
|
||||
// Revert conflictedObject.featuredSortID to database value (if it exists).
|
||||
if let featuredSortID = databaseObject.featuredSortID
|
||||
{
|
||||
conflictedObject.featuredSortID = featuredSortID
|
||||
}
|
||||
|
||||
case let databasePledge as Pledge:
|
||||
guard let contextPledge = conflict.conflictingObjects.first as? Pledge else { break }
|
||||
|
||||
|
||||
@@ -215,6 +215,8 @@ public class Source: NSManagedObject, Fetchable, Decodable
|
||||
|
||||
@NSManaged public var error: NSError?
|
||||
|
||||
@NSManaged public var featuredSortID: String?
|
||||
|
||||
/* Non-Core Data Properties */
|
||||
public var userInfo: [ALTSourceUserInfoKey: String]?
|
||||
|
||||
@@ -350,6 +352,13 @@ public class Source: NSManagedObject, Fetchable, Decodable
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
public override func awakeFromInsert()
|
||||
{
|
||||
super.awakeFromInsert()
|
||||
|
||||
self.featuredSortID = UUID().uuidString
|
||||
}
|
||||
}
|
||||
|
||||
public extension Source
|
||||
|
||||
@@ -141,6 +141,7 @@ public class StoreApp: NSManagedObject, Decodable, Fetchable
|
||||
@NSManaged @objc(pledgeAmount) private var _pledgeAmount: NSDecimalNumber?
|
||||
|
||||
@NSManaged public var sortIndex: Int32
|
||||
@NSManaged public var featuredSortID: String?
|
||||
|
||||
@objc public internal(set) var sourceIdentifier: String? {
|
||||
get {
|
||||
@@ -463,6 +464,13 @@ public class StoreApp: NSManagedObject, Decodable, Fetchable
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
public override func awakeFromInsert()
|
||||
{
|
||||
super.awakeFromInsert()
|
||||
|
||||
self.featuredSortID = UUID().uuidString
|
||||
}
|
||||
}
|
||||
|
||||
internal extension StoreApp
|
||||
|
||||
Reference in New Issue
Block a user