From 3798a758a37e4f8a2edea76148bd0953daff32a1 Mon Sep 17 00:00:00 2001 From: Paul Tsochantaris Date: Sun, 20 Aug 2023 18:31:06 +0100 Subject: [PATCH] Settings cache improvements --- .../AdvancedSettingsViewController.swift | 18 +- PocketTrailer/ApiSettingsViewController.swift | 2 +- PocketTrailer/MasterViewController.swift | 24 +-- PocketTrailer/PRCell.swift | 6 +- PocketTrailer/WatchManager.swift | 22 +- Shared/API.swift | 16 -- Shared/DataManager.swift | 30 ++- Shared/Enums+Settings.swift | 16 +- Shared/GraphQL.swift | 36 ++-- Shared/ListableItem.swift | 203 ++++++++---------- Shared/PRComment.swift | 10 +- Shared/PullRequest.swift | 57 +++-- Shared/Reaction.swift | 11 +- Shared/RestAccess.swift | 5 +- Shared/Review.swift | 9 +- Shared/Settings.swift | 119 ++++++++++ Shared/V3API.swift | 4 +- Shared/V4API.swift | 4 +- Trailer.xcodeproj/project.pbxproj | 4 +- .../xcshareddata/xcschemes/Trailer.xcscheme | 6 +- Trailer/ApiOptionsWindow.swift | 2 +- Trailer/DataSource.swift | 15 +- Trailer/HiddenItemWindow.swift | 10 +- Trailer/PreferencesWindow.swift | 18 +- Trailer/TrailerCell.swift | 5 +- 25 files changed, 340 insertions(+), 312 deletions(-) diff --git a/PocketTrailer/AdvancedSettingsViewController.swift b/PocketTrailer/AdvancedSettingsViewController.swift index e54ae652..581af584 100644 --- a/PocketTrailer/AdvancedSettingsViewController.swift +++ b/PocketTrailer/AdvancedSettingsViewController.swift @@ -512,7 +512,7 @@ final class AdvancedSettingsViewController: UITableViewController, PickerViewCon } private func showOptionalReviewWarning(previousSync: Bool) { - if !previousSync, API.shouldSyncReviews || API.shouldSyncReviewAssignments { + if !previousSync, Settings.cache.requiresReviewApis { for p in PullRequest.allItems(in: DataManager.main) { p.resetSyncState() } @@ -684,12 +684,12 @@ final class AdvancedSettingsViewController: UITableViewController, PickerViewCon } else if section == .Reviews { switch originalIndex { case 0: - let previousShouldSync = (API.shouldSyncReviews || API.shouldSyncReviewAssignments) + let previousShouldSync = Settings.cache.requiresReviewApis Settings.displayReviewsOnItems = !Settings.displayReviewsOnItems showOptionalReviewWarning(previousSync: previousShouldSync) case 1: - let previousShouldSync = (API.shouldSyncReviews || API.shouldSyncReviewAssignments) + let previousShouldSync = Settings.cache.requiresReviewApis Settings.showRequestedTeamReviews = !Settings.showRequestedTeamReviews showOptionalReviewWarning(previousSync: previousShouldSync) @@ -702,7 +702,7 @@ final class AdvancedSettingsViewController: UITableViewController, PickerViewCon performSegue(withIdentifier: "showPicker", sender: v) case 4: - let previousShouldSync = (API.shouldSyncReviews || API.shouldSyncReviewAssignments) + let previousShouldSync = Settings.cache.requiresReviewApis Settings.notifyOnReviewChangeRequests = !Settings.notifyOnReviewChangeRequests showOptionalReviewWarning(previousSync: previousShouldSync) @@ -710,7 +710,7 @@ final class AdvancedSettingsViewController: UITableViewController, PickerViewCon Settings.notifyOnAllReviewChangeRequests = !Settings.notifyOnAllReviewChangeRequests case 6: - let previousShouldSync = (API.shouldSyncReviews || API.shouldSyncReviewAssignments) + let previousShouldSync = Settings.cache.requiresReviewApis Settings.notifyOnReviewAcceptances = !Settings.notifyOnReviewAcceptances showOptionalReviewWarning(previousSync: previousShouldSync) @@ -718,7 +718,7 @@ final class AdvancedSettingsViewController: UITableViewController, PickerViewCon Settings.notifyOnAllReviewAcceptances = !Settings.notifyOnAllReviewAcceptances case 8: - let previousShouldSync = (API.shouldSyncReviews || API.shouldSyncReviewAssignments) + let previousShouldSync = Settings.cache.requiresReviewApis Settings.notifyOnReviewDismissals = !Settings.notifyOnReviewDismissals showOptionalReviewWarning(previousSync: previousShouldSync) @@ -726,7 +726,7 @@ final class AdvancedSettingsViewController: UITableViewController, PickerViewCon Settings.notifyOnAllReviewDismissals = !Settings.notifyOnAllReviewDismissals case 10: - let previousShouldSync = (API.shouldSyncReviews || API.shouldSyncReviewAssignments) + let previousShouldSync = Settings.cache.requiresReviewApis Settings.notifyOnReviewAssignments = !Settings.notifyOnReviewAssignments showOptionalReviewWarning(previousSync: previousShouldSync) @@ -956,7 +956,7 @@ final class AdvancedSettingsViewController: UITableViewController, PickerViewCon settingsChangedTimer.push() } else if sip.section == SettingsSection.Reviews.rawValue { - let previous = (API.shouldSyncReviews || API.shouldSyncReviewAssignments) + let previous = Settings.cache.requiresReviewApis if sip.row == 2 { Settings.assignedDirectReviewHandlingPolicy = Section(assignmentPolicyMenuIndex: didSelectIndexPath.row) } else if sip.row == 3 { @@ -965,7 +965,7 @@ final class AdvancedSettingsViewController: UITableViewController, PickerViewCon showOptionalReviewWarning(previousSync: previous) } else if sip.section == SettingsSection.Reactions.rawValue { - let previous = API.shouldSyncReactions + let previous = Settings.cache.shouldSyncReactions Settings.reactionScanningBatchSize = didSelectIndexPath.row + 1 showOptionalReviewWarning(previousSync: previous) } diff --git a/PocketTrailer/ApiSettingsViewController.swift b/PocketTrailer/ApiSettingsViewController.swift index 157ff163..c6728bff 100644 --- a/PocketTrailer/ApiSettingsViewController.swift +++ b/PocketTrailer/ApiSettingsViewController.swift @@ -65,7 +65,7 @@ final class ApiSettingsViewController: UIViewController, UITextFieldDelegate { } private func updateUI() { - let profile = Settings.syncProfile + let profile = Settings.cache.syncProfile highToggle.isOn = profile == .high moderateToggle.isOn = profile == .moderate defaultToggle.isOn = profile == .cautious diff --git a/PocketTrailer/MasterViewController.swift b/PocketTrailer/MasterViewController.swift index 95b8da52..57a540ef 100644 --- a/PocketTrailer/MasterViewController.swift +++ b/PocketTrailer/MasterViewController.swift @@ -50,7 +50,6 @@ final class MasterViewController: UITableViewController, NSFetchedResultsControl private var currentTabBarSet: TabBarSet? private var searchTimer: PopTimer! - private var context: SettingsCache? private var pluralNameForItems: String { viewingPrs ? "pull requests" : "issues" @@ -556,8 +555,6 @@ final class MasterViewController: UITableViewController, NSFetchedResultsControl } private func updateQuery(newFetchRequest: NSFetchRequest) { - context = nil - if fetchedResultsController == nil || fetchedResultsController?.fetchRequest.entityName != newFetchRequest.entityName { let c = NSFetchedResultsController(fetchRequest: newFetchRequest, managedObjectContext: DataManager.main, sectionNameKeyPath: "sectionName", cacheName: nil) fetchedResultsController = c @@ -782,7 +779,6 @@ final class MasterViewController: UITableViewController, NSFetchedResultsControl override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) - context = nil tableView.reloadData() } @@ -797,7 +793,7 @@ final class MasterViewController: UITableViewController, NSFetchedResultsControl override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) if let o = fetchedResultsController?.object(at: indexPath) { - configureCell(cell: cell, withObject: o, context: usedContext) + configureCell(cell: cell, withObject: o) } return cell } @@ -986,7 +982,6 @@ final class MasterViewController: UITableViewController, NSFetchedResultsControl private var animatedUpdates = false func controllerWillChangeContent(_: NSFetchedResultsController) { - context = nil animatedUpdates = UIApplication.shared.applicationState != .background sectionsChanged = false if animatedUpdates { @@ -1013,15 +1008,6 @@ final class MasterViewController: UITableViewController, NSFetchedResultsControl sectionsChanged = true } - private var usedContext: SettingsCache { - if let context { - return context - } - let used = SettingsCache() - context = used - return used - } - func controller(_: NSFetchedResultsController, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) { guard animatedUpdates else { return } @@ -1036,7 +1022,7 @@ final class MasterViewController: UITableViewController, NSFetchedResultsControl } case .update: if let indexPath, let object = anObject as? ListableItem, let cell = tableView.cellForRow(at: indexPath) { - configureCell(cell: cell, withObject: object, context: usedContext) + configureCell(cell: cell, withObject: object) } case .move: if let indexPath, let newIndexPath { @@ -1056,15 +1042,14 @@ final class MasterViewController: UITableViewController, NSFetchedResultsControl if animatedUpdates { tableView.endUpdates() } else { - context = nil tableView.reloadData() } } - private func configureCell(cell: UITableViewCell, withObject: ListableItem, context: SettingsCache) { + private func configureCell(cell: UITableViewCell, withObject: ListableItem) { guard let c = cell as? PRCell else { return } if let o = withObject as? PullRequest { - c.setPullRequest(pullRequest: o, context: context) + c.setPullRequest(pullRequest: o) } else if let o = withObject as? Issue { c.setIssue(issue: o) } @@ -1145,7 +1130,6 @@ final class MasterViewController: UITableViewController, NSFetchedResultsControl await safeScrollToTop() updateQuery(newFetchRequest: itemFetchRequest) updateStatus(becauseOfChanges: becauseOfChanges) - context = nil tableView.reloadData() } diff --git a/PocketTrailer/PRCell.swift b/PocketTrailer/PRCell.swift index bc9c00af..c3bbc805 100644 --- a/PocketTrailer/PRCell.swift +++ b/PocketTrailer/PRCell.swift @@ -85,7 +85,7 @@ final class PRCell: UITableViewCell { private weak var item: ListableItem? - func setPullRequest(pullRequest: PullRequest, context: SettingsCache) { + func setPullRequest(pullRequest: PullRequest) { item = pullRequest let separator = traitCollection.containsTraits(in: compactTraits) ? "\n" : " " @@ -114,7 +114,7 @@ final class PRCell: UITableViewCell { var statusText: NSMutableAttributedString? var totalStatuses = 0 if pullRequest.section.shouldListStatuses { - let statusItems = pullRequest.displayedStatusLines(context: context) + let statusItems = pullRequest.displayedStatusLines var statusCount = statusItems.count totalStatuses = statusCount var lineAttributes = statusAttributes @@ -177,7 +177,7 @@ final class PRCell: UITableViewCell { readCount.text = numberFormatter.string(for: _commentsTotal) readCount.isHidden = _commentsTotal == 0 - if let p = item as? PullRequest, Settings.markPrsAsUnreadOnNewCommits, p.hasNewCommits { + if let p = item as? PullRequest, Settings.cache.markPrsAsUnreadOnNewCommits, p.hasNewCommits { unreadCount.isHidden = false unreadCount.text = _commentsNew == 0 ? "!" : numberFormatter.string(for: _commentsNew) } else { diff --git a/PocketTrailer/WatchManager.swift b/PocketTrailer/WatchManager.swift index 2b508ace..a1985619 100644 --- a/PocketTrailer/WatchManager.swift +++ b/PocketTrailer/WatchManager.swift @@ -113,7 +113,6 @@ final class WatchManager: NSObject, WCSessionDelegate { @MainActor private func processList(message: JSON) async -> JSON { var result = JSON() - let context = SettingsCache() switch (message["list"] as? String).orEmpty { case "overview": @@ -128,12 +127,11 @@ final class WatchManager: NSObject, WCSessionDelegate { apiServerUri: message["apiUri"] as! String, group: message["group"] as! String, count: message["count"] as! Int, - onlyUnread: message["onlyUnread"] as! Bool, - context: context + onlyUnread: message["onlyUnread"] as! Bool ) case "item_detail": - if let lid = message["localId"] as? String, let details = buildItemDetail(localId: lid, context: context) { + if let lid = message["localId"] as? String, let details = buildItemDetail(localId: lid) { result["result"] = details return reportSuccess(result: result) } else { @@ -161,7 +159,7 @@ final class WatchManager: NSObject, WCSessionDelegate { //////////////////////////// @MainActor - private func buildItemList(type: String, sectionIndex: Int, from: Int, apiServerUri: String, group: String, count: Int, onlyUnread: Bool, context: SettingsCache) async -> JSON { + private func buildItemList(type: String, sectionIndex: Int, from: Int, apiServerUri: String, group: String, count: Int, onlyUnread: Bool) async -> JSON { let showLabels = Settings.showLabels let entity: ListableItem.Type if type == "prs" { @@ -184,13 +182,13 @@ final class WatchManager: NSObject, WCSessionDelegate { f.fetchOffset = from f.fetchLimit = count - let items = try! DataManager.main.fetch(f).map { self.baseDataForItem(item: $0, showLabels: showLabels, context: context) } + let items = try! DataManager.main.fetch(f).map { self.baseDataForItem(item: $0, showLabels: showLabels) } let compressedData = (try? NSKeyedArchiver.archivedData(withRootObject: items, requiringSecureCoding: false).data(operation: .compress)) ?? Data() return ["result": compressedData] } @MainActor - private func baseDataForItem(item: ListableItem, showLabels: Bool, context: SettingsCache) -> JSON { + private func baseDataForItem(item: ListableItem, showLabels: Bool) -> JSON { let font = UIFont.systemFont(ofSize: UIFont.systemFontSize) let smallFont = UIFont.systemFont(ofSize: UIFont.systemFontSize - 4) @@ -208,7 +206,7 @@ final class WatchManager: NSObject, WCSessionDelegate { itemData["labels"] = labelsForItem(item: item) } if let item = item as? PullRequest, item.section.shouldListStatuses { - itemData["statuses"] = statusLinesForPr(pr: item, context: context) + itemData["statuses"] = statusLinesForPr(pr: item) } return itemData } @@ -226,9 +224,9 @@ final class WatchManager: NSObject, WCSessionDelegate { } @MainActor - private func statusLinesForPr(pr: PullRequest, context: SettingsCache) -> [JSON] { + private func statusLinesForPr(pr: PullRequest) -> [JSON] { var statusLines = [JSON]() - for status in pr.displayedStatusLines(context: context) { + for status in pr.displayedStatusLines { statusLines.append([ "color": status.colorForDisplay, "text": status.descriptionText.orEmpty @@ -240,9 +238,9 @@ final class WatchManager: NSObject, WCSessionDelegate { ///////////////////////////// @MainActor - private func buildItemDetail(localId: String, context: SettingsCache) -> Data? { + private func buildItemDetail(localId: String) -> Data? { if let oid = DataManager.id(for: localId), let item = try? DataManager.main.existingObject(with: oid) as? ListableItem { - var result = baseDataForItem(item: item, showLabels: Settings.showLabels, context: context) + var result = baseDataForItem(item: item, showLabels: Settings.showLabels) result["description"] = item.body result["comments"] = commentsForItem(item: item) diff --git a/Shared/API.swift b/Shared/API.swift index a29b4f75..42da823e 100644 --- a/Shared/API.swift +++ b/Shared/API.swift @@ -140,22 +140,6 @@ enum API { } } - nonisolated static var shouldSyncReactions: Bool { - Settings.notifyOnItemReactions || Settings.notifyOnCommentReactions - } - - nonisolated static var shouldSyncReviews: Bool { - Settings.displayReviewsOnItems || Settings.notifyOnReviewDismissals || Settings.notifyOnReviewAcceptances || Settings.notifyOnReviewChangeRequests || Settings.autoHidePrsIApproved || Settings.autoHidePrsIRejected - } - - nonisolated static var shouldSyncReviewAssignments: Bool { - Settings.displayReviewsOnItems - || Settings.showRequestedTeamReviews - || Settings.notifyOnReviewAssignments - || (Settings.assignedDirectReviewHandlingPolicy.visible) - || (Settings.assignedTeamReviewHandlingPolicy.visible) - } - ////////////////////////////////////// API interface @MainActor diff --git a/Shared/DataManager.swift b/Shared/DataManager.swift index ef4b0cbd..706b3c8e 100644 --- a/Shared/DataManager.swift +++ b/Shared/DataManager.swift @@ -78,28 +78,28 @@ enum DataManager { } } - private static func processCommentAndReviewNotifications(postProcessContext: SettingsCache) async { + private static func processCommentAndReviewNotifications() async { await runInChild(of: main) { child in for c in PRComment.newItems(in: child) { - c.processNotifications(postProcessContext: postProcessContext) + c.processNotifications() c.postSyncAction = PostSyncAction.doNothing.rawValue } for r in Review.newOrUpdatedItems(in: child) { - r.processNotifications(context: postProcessContext) + r.processNotifications() r.postSyncAction = PostSyncAction.doNothing.rawValue } } } - private static func processStatusNotifications(postProcessContext: SettingsCache) async { + private static func processStatusNotifications() async { await runInChild(of: main) { child in let latestStatuses = PRStatus.newItems(in: child) var coveredPrs = Set() - if postProcessContext.notifyOnStatusUpdates { - for pr in latestStatuses.map(\.pullRequest) where pr.shouldAnnounceStatus(context: postProcessContext) && !coveredPrs.contains(pr.objectID) { + if Settings.cache.notifyOnStatusUpdates { + for pr in latestStatuses.map(\.pullRequest) where pr.shouldAnnounceStatus() && !coveredPrs.contains(pr.objectID) { coveredPrs.insert(pr.objectID) - if let s = pr.displayedStatusLines(context: postProcessContext).first { + if let s = pr.displayedStatusLines.first { let displayText = s.descriptionText if pr.lastStatusNotified != displayText, pr.postSyncAction != PostSyncAction.isNew.rawValue { NotificationQueue.add(type: .newStatus, for: s) @@ -140,23 +140,21 @@ enum DataManager { static func sendNotificationsIndexAndSave() async { await saveDB() // get IDs - let postProcessContext = SettingsCache() - - await postProcessAllItems(in: main, postProcessContext: postProcessContext) + await postProcessAllItems(in: main) await processNotificationsForItems(of: PullRequest.self, newNotification: .newPr, reopenedNotification: .prReopened, assignmentNotification: .newPrAssigned) await processNotificationsForItems(of: Issue.self, newNotification: .newIssue, reopenedNotification: .issueReopened, assignmentNotification: .newIssueAssigned) - await processCommentAndReviewNotifications(postProcessContext: postProcessContext) + await processCommentAndReviewNotifications() - await processStatusNotifications(postProcessContext: postProcessContext) + await processStatusNotifications() await runInChild(of: main) { child in let nothing = PostSyncAction.doNothing.rawValue for r in Reaction.newOrUpdatedItems(in: child) { - r.checkNotifications(context: postProcessContext) + r.checkNotifications() r.postSyncAction = nothing } @@ -394,7 +392,7 @@ enum DataManager { } } - static func postProcessAllItems(in context: NSManagedObjectContext, postProcessContext: SettingsCache = SettingsCache()) async { + static func postProcessAllItems(in context: NSManagedObjectContext) async { let start = Date() let increment = 200 @@ -404,7 +402,7 @@ enum DataManager { group.addTask(priority: .high) { await runInChild(of: context) { child in for p in PullRequest.allItems(offset: i, count: increment, in: child, prefetchRelationships: ["comments", "reactions", "reviews"]) { - p.postProcess(context: postProcessContext) + p.postProcess() } } } @@ -415,7 +413,7 @@ enum DataManager { group.addTask(priority: .high) { await runInChild(of: context) { child in for i in Issue.allItems(offset: i, count: increment, in: child, prefetchRelationships: ["comments", "reactions"]) { - i.postProcess(context: postProcessContext) + i.postProcess() } } } diff --git a/Shared/Enums+Settings.swift b/Shared/Enums+Settings.swift index c6f353ca..d605bd25 100644 --- a/Shared/Enums+Settings.swift +++ b/Shared/Enums+Settings.swift @@ -4,9 +4,9 @@ extension Section { var shouldBadgeComments: Bool { switch self { case .all: - return Settings.showCommentsEverywhere + return Settings.cache.showCommentsEverywhere case .closed, .merged: - return Settings.scanClosedAndMergedItems + return Settings.cache.scanClosedAndMergedItems case .mentioned, .mine, .participated: return true case .hidden, .snoozed: @@ -16,7 +16,7 @@ extension Section { @MainActor var shouldListReactions: Bool { - if API.shouldSyncReactions { + if Settings.cache.shouldSyncReactions { return shouldBadgeComments } return false @@ -24,12 +24,12 @@ extension Section { @MainActor var shouldListStatuses: Bool { - if !Settings.showStatusItems { + if !Settings.cache.showStatusItems { return false } switch self { case .all, .closed, .merged: - return Settings.showStatusesOnAllItems + return Settings.cache.showStatusesOnAllItems case .mentioned, .mine, .participated: return true case .hidden, .snoozed: @@ -39,16 +39,16 @@ extension Section { @MainActor var shouldCheckStatuses: Bool { - if !Settings.showStatusItems { + if !Settings.cache.showStatusItems { return false } switch self { case .all, .closed, .merged: - return Settings.showStatusesOnAllItems + return Settings.cache.showStatusesOnAllItems case .mentioned, .mine, .participated, .snoozed: return true case .hidden: - return Settings.hidePrsThatArentPassing // if visibility depends on statuses, check for statuses on hidden PRs because they may change + return Settings.cache.hidePrsThatArentPassing // if visibility depends on statuses, check for statuses on hidden PRs because they may change } } } diff --git a/Shared/GraphQL.swift b/Shared/GraphQL.swift index 4065d4dc..ece008ac 100644 --- a/Shared/GraphQL.swift +++ b/Shared/GraphQL.swift @@ -197,7 +197,7 @@ enum GraphQL { } private static func commentGroup(for typeName: String) -> Group { - Group("comments", paging: Settings.syncProfile.largePageSize) { + Group("comments", paging: Settings.cache.syncProfile.largePageSize) { Fragment(on: typeName) { Field.id Field("body") @@ -401,8 +401,8 @@ enum GraphQL { } } - try await process(name: steps.toString, items: items, parentType: T.self, maxCost: Settings.syncProfile.itemAccompanyingBatchCount) { - let profile = Settings.syncProfile + try await process(name: steps.toString, items: items, parentType: T.self, maxCost: Settings.cache.syncProfile.itemAccompanyingBatchCount) { + let profile = Settings.cache.syncProfile Fragment(on: typeName) { Field.id @@ -487,10 +487,10 @@ enum GraphQL { } static func updateReactions(for comments: [PRComment]) async throws { - try await process(name: "Comment Reactions", items: comments, maxCost: Settings.syncProfile.itemAccompanyingBatchCount) { + try await process(name: "Comment Reactions", items: comments, maxCost: Settings.cache.syncProfile.itemAccompanyingBatchCount) { Fragment(on: "IssueComment") { Field.id - Group("reactions", paging: Settings.syncProfile.largePageSize) { + Group("reactions", paging: Settings.cache.syncProfile.largePageSize) { Fragment(on: "Reaction") { Field.id Field("content") @@ -503,7 +503,7 @@ enum GraphQL { } static func updateComments(for reviews: [Review]) async throws { - try await process(name: "Review Comments", items: reviews, maxCost: Settings.syncProfile.itemAccompanyingBatchCount) { + try await process(name: "Review Comments", items: reviews, maxCost: Settings.cache.syncProfile.itemAccompanyingBatchCount) { Fragment(on: "PullRequestReview") { Field.id commentGroup(for: "PullRequestReviewComment") @@ -587,7 +587,7 @@ enum GraphQL { Field("url") Group("milestone") { milestoneFragment } authorGroup - let profile = Settings.syncProfile + let profile = Settings.cache.syncProfile Group("assignees", paging: profile.smallPageSize) { userFragment } Group("labels", paging: profile.smallPageSize) { labelFragment } Field("headRefOid") @@ -618,8 +618,8 @@ enum GraphQL { Field("url") Group("milestone") { milestoneFragment } authorGroup - Group("assignees", paging: Settings.syncProfile.smallPageSize) { userFragment } - Group("labels", paging: Settings.syncProfile.smallPageSize) { labelFragment } + Group("assignees", paging: Settings.cache.syncProfile.smallPageSize) { userFragment } + Group("labels", paging: Settings.cache.syncProfile.smallPageSize) { labelFragment } if includeRepo { Group("repository") { repositoryFragment } } @@ -630,7 +630,7 @@ enum GraphQL { await withTaskGroup(of: Void.self) { group in for server in servers { if Settings.queryAuthoredPRs { - let g = Group("pullRequests", ("states", "[OPEN]"), paging: Settings.syncProfile.mediumPageSize) { + let g = Group("pullRequests", ("states", "[OPEN]"), paging: Settings.cache.syncProfile.mediumPageSize) { prFragment(includeRepo: true) } group.addTask { @MainActor in @@ -725,28 +725,28 @@ enum GraphQL { private static var latestPrsFragment = Fragment(on: "Repository") { Field.id - Group("pullRequests", ("orderBy", "{direction: DESC, field: UPDATED_AT}"), paging: Settings.syncProfile.smallPageSize) { + Group("pullRequests", ("orderBy", "{direction: DESC, field: UPDATED_AT}"), paging: Settings.cache.syncProfile.smallPageSize) { prFragment(includeRepo: false) } } private static var latestIssuesFragment = Fragment(on: "Repository") { Field.id - Group("issues", ("orderBy", "{direction: DESC, field: UPDATED_AT}"), paging: Settings.syncProfile.smallPageSize) { + Group("issues", ("orderBy", "{direction: DESC, field: UPDATED_AT}"), paging: Settings.cache.syncProfile.smallPageSize) { issueFragment(includeRepo: false) } } private static var allOpenPrsFragment = Fragment(on: "Repository") { Field.id - Group("pullRequests", ("states", "[OPEN]"), paging: Settings.syncProfile.mediumPageSize) { + Group("pullRequests", ("states", "[OPEN]"), paging: Settings.cache.syncProfile.mediumPageSize) { prFragment(includeRepo: false) } } private static var allOpenIssuesFragment = Fragment(on: "Repository") { Field.id - Group("issues", ("states", "[OPEN]"), paging: Settings.syncProfile.largePageSize) { + Group("issues", ("states", "[OPEN]"), paging: Settings.cache.syncProfile.largePageSize) { issueFragment(includeRepo: false) } } @@ -800,12 +800,12 @@ enum GraphQL { } if idsForReposInThisServerWantingLatestPrs.count > 0 { - let q = Query.batching("\(serverLabel): Updated PRs", groupName: "nodes", idList: idsForReposInThisServerWantingLatestPrs, maxCost: Settings.syncProfile.itemIncrementalBatchCost, perNode: perNodeBlock) { latestPrsFragment } + let q = Query.batching("\(serverLabel): Updated PRs", groupName: "nodes", idList: idsForReposInThisServerWantingLatestPrs, maxCost: Settings.cache.syncProfile.itemIncrementalBatchCost, perNode: perNodeBlock) { latestPrsFragment } queriesForServer.append(contentsOf: q) } if idsForReposInThisServerWantingAllOpenPrs.count > 0 { - let q = Query.batching("\(serverLabel): Open PRs", groupName: "nodes", idList: idsForReposInThisServerWantingAllOpenPrs, maxCost: Settings.syncProfile.itemInitialBatchCost, perNode: perNodeBlock) { allOpenPrsFragment } + let q = Query.batching("\(serverLabel): Open PRs", groupName: "nodes", idList: idsForReposInThisServerWantingAllOpenPrs, maxCost: Settings.cache.syncProfile.itemInitialBatchCost, perNode: perNodeBlock) { allOpenPrsFragment } queriesForServer.append(contentsOf: q) } @@ -867,12 +867,12 @@ enum GraphQL { } if idsForReposInThisServerWantingLatestIssues.count > 0 { - let q = Query.batching("\(serverLabel): Updated Issues", groupName: "nodes", idList: idsForReposInThisServerWantingLatestIssues, maxCost: Settings.syncProfile.itemIncrementalBatchCost, perNode: perNodeBlock) { latestIssuesFragment } + let q = Query.batching("\(serverLabel): Updated Issues", groupName: "nodes", idList: idsForReposInThisServerWantingLatestIssues, maxCost: Settings.cache.syncProfile.itemIncrementalBatchCost, perNode: perNodeBlock) { latestIssuesFragment } queriesForServer.append(contentsOf: q) } if idsForReposInThisServerWantingAllOpenIssues.count > 0 { - let q = Query.batching("\(serverLabel): Open Issues", groupName: "nodes", idList: idsForReposInThisServerWantingAllOpenIssues, maxCost: Settings.syncProfile.itemInitialBatchCost, perNode: perNodeBlock) { allOpenIssuesFragment } + let q = Query.batching("\(serverLabel): Open Issues", groupName: "nodes", idList: idsForReposInThisServerWantingAllOpenIssues, maxCost: Settings.cache.syncProfile.itemInitialBatchCost, perNode: perNodeBlock) { allOpenIssuesFragment } queriesForServer.append(contentsOf: q) } diff --git a/Shared/ListableItem.swift b/Shared/ListableItem.swift index 6249d2b2..e1d031fe 100644 --- a/Shared/ListableItem.swift +++ b/Shared/ListableItem.swift @@ -9,49 +9,6 @@ import TrailerQL import Cocoa #endif -struct SettingsCache { - let excludedLabels = Set(Settings.labelBlacklist.map(\.comparableForm)) - let excludedAuthors = Set(Settings.itemAuthorBlacklist.map(\.comparableForm)) - let excludedCommentAuthors = Set(Settings.commentAuthorBlacklist.map(\.comparableForm)) - let assumeReadItemIfUserHasNewerComments = Settings.assumeReadItemIfUserHasNewerComments - let hideUncommentedItems = Settings.hideUncommentedItems - let shouldSyncReviews = API.shouldSyncReviews - let shouldSyncReviewAssignments = API.shouldSyncReviewAssignments - let notifyOnItemReactions = Settings.notifyOnItemReactions - let notifyOnCommentReactions = Settings.notifyOnCommentReactions - let notifyOnStatusUpdates = Settings.notifyOnStatusUpdates - let shouldHideDrafts = Settings.draftHandlingPolicy == .hide - let autoRemoveClosedItems = Settings.autoRemoveClosedItems - let autoRemoveMergedItems = Settings.autoRemoveMergedItems - let notifyOnStatusUpdatesForAllPrs = Settings.notifyOnStatusUpdatesForAllPrs - let hidePrsIfApproved = Settings.autoHidePrsIApproved - let hidePrsIfRejected = Settings.autoHidePrsIRejected - let preferredMovePolicySection = Settings.newMentionMovePolicy.preferredSection - let preferredTeamMentionPolicy = Settings.teamMentionMovePolicy.preferredSection - let newItemInOwnedRepoMovePolicy = Settings.newItemInOwnedRepoMovePolicy.preferredSection - let assignedItemDirectHandlingPolicy = Settings.assignedItemDirectHandlingPolicy - let assignedItemTeamHandlingPolicy = Settings.assignedItemTeamHandlingPolicy - let notifyOnAllReviewChangeRequests = Settings.notifyOnAllReviewChangeRequests - let notifyOnAllReviewAcceptances = Settings.notifyOnAllReviewAcceptances - let notifyOnAllReviewDismissals = Settings.notifyOnAllReviewDismissals - let notifyOnReviewChangeRequests = Settings.notifyOnReviewChangeRequests - let notifyOnReviewAcceptances = Settings.notifyOnReviewAcceptances - let notifyOnReviewDismissals = Settings.notifyOnReviewDismissals - let autoSnoozeDuration = TimeInterval(Settings.autoSnoozeDuration) - let hidePrsThatArentPassing = Settings.hidePrsThatArentPassing - let hidePrsThatDontPassOnlyInAll = Settings.hidePrsThatDontPassOnlyInAll - - let statusRed = Settings.showStatusesRed - let statusYellow = Settings.showStatusesYellow - let statusGreen = Settings.showStatusesGreen - let statusGray = Settings.showStatusesGray - let statusMode = Settings.statusFilteringMode - let statusTerms = Settings.statusFilteringTerms - - let makeStatusItemsSelectable = Settings.makeStatusItemsSelectable - let hideAvatars = Settings.hideAvatars -} - protocol Listable: Querying { var section: Section { get } var sectionIndex: Int { get } @@ -294,7 +251,7 @@ class ListableItem: DataItem, Listable { private static func hideFromNotifications(uri: String) { Task { - if Settings.removeNotificationsWhenItemIsRemoved { + if Settings.cache.removeNotificationsWhenItemIsRemoved { await NotificationManager.shared.removeRelatedNotifications(for: uri) } } @@ -369,13 +326,11 @@ class ListableItem: DataItem, Listable { return true case .mineAndParticipated: - let context = SettingsCache() - let preConditionSection = preferredSection(takingItemConditionIntoAccount: false, context: context) + let preConditionSection = preferredSection(takingItemConditionIntoAccount: false) return preConditionSection == .mine || preConditionSection == .participated case .mine: - let context = SettingsCache() - let preConditionSection = preferredSection(takingItemConditionIntoAccount: false, context: context) + let preConditionSection = preferredSection(takingItemConditionIntoAccount: false) return preConditionSection == .mine case .nothing: @@ -387,16 +342,16 @@ class ListableItem: DataItem, Listable { isSnoozing || muted } - final func shouldGo(to section: Section, context: SettingsCache) -> Bool { + final func shouldGo(to section: Section) -> Bool { switch AssignmentStatus(rawValue: assignedStatus) { case nil, .none?, .others: return false case .me: - return section == context.assignedItemDirectHandlingPolicy + return section == Settings.cache.assignedItemDirectHandlingPolicy case .myTeam: - return section == context.assignedItemTeamHandlingPolicy + return section == Settings.cache.assignedItemTeamHandlingPolicy } } @@ -448,9 +403,9 @@ class ListableItem: DataItem, Listable { postProcess() // make sure it's in the right section and updated correctly for its new status } - private final func shouldMoveToSnoozing(context: SettingsCache) -> Bool { + private final func shouldMoveToSnoozing() -> Bool { if snoozeUntil == nil { - let d = context.autoSnoozeDuration + let d = Settings.cache.autoSnoozeDuration if d > 0, !wasAwokenFromSnooze, updatedAt != .distantPast, let snoozeByDate = updatedAt?.addingTimeInterval(86400.0 * d) { if snoozeByDate < Date() { snoozeUntil = autoSnoozeSentinelDate @@ -504,7 +459,7 @@ class ListableItem: DataItem, Listable { nil } - private func preferredSection(takingItemConditionIntoAccount: Bool, context: SettingsCache) -> Section { + private func preferredSection(takingItemConditionIntoAccount: Bool) -> Section { if takingItemConditionIntoAccount { if condition == ItemCondition.merged.rawValue { return .merged @@ -514,19 +469,19 @@ class ListableItem: DataItem, Listable { } } - if shouldMoveToSnoozing(context: context) { + if shouldMoveToSnoozing() { return .snoozed } - if createdByMe || shouldGo(to: .mine, context: context) { + if createdByMe || shouldGo(to: .mine) { return .mine } - if shouldGo(to: .participated, context: context) || commentedByMe || reviewedByMe { + if shouldGo(to: .participated) || commentedByMe || reviewedByMe { return .participated } - if shouldGo(to: .mentioned, context: context) { + if shouldGo(to: .mentioned) { return .mentioned } @@ -534,6 +489,8 @@ class ListableItem: DataItem, Listable { return section } + let context = Settings.cache + if let section = context.preferredMovePolicySection, contains(terms: ["@\(apiServer.userName.orEmpty)"]) { return section @@ -552,7 +509,7 @@ class ListableItem: DataItem, Listable { return .all } - func canBadge(in targetSection: Section? = nil, context: SettingsCache) -> Bool { + func canBadge(in targetSection: Section? = nil) -> Bool { let targetSection = targetSection ?? Section(sectionIndex: sectionIndex) if !targetSection.shouldBadgeComments || muted || postSyncAction == PostSyncAction.isNew.rawValue { @@ -560,7 +517,7 @@ class ListableItem: DataItem, Listable { } if targetSection == .closed || targetSection == .merged { - return preferredSection(takingItemConditionIntoAccount: false, context: context).shouldBadgeComments + return preferredSection(takingItemConditionIntoAccount: false).shouldBadgeComments } return true @@ -598,7 +555,8 @@ class ListableItem: DataItem, Listable { return nil } - private func shouldHideBecauseOfBlockedContent(context: SettingsCache) -> Section.HidingCause? { + private func shouldHideBecauseOfBlockedContent() -> Section.HidingCause? { + let context = Settings.cache let excluded = context.excludedLabels if !excluded.isEmpty { let mine = Set(labels.compactMap { $0.name?.comparableForm }) @@ -618,19 +576,19 @@ class ListableItem: DataItem, Listable { return nil } - func shouldHideBecauseOfRedStatuses(in section: Section, context: SettingsCache) -> Section.HidingCause? { + func shouldHideBecauseOfRedStatuses(in section: Section) -> Section.HidingCause? { nil } - private func shouldHideBecauseOfDraftStatus(context: SettingsCache) -> Section.HidingCause? { - if context.shouldHideDrafts, draft { + private func shouldHideBecauseOfDraftStatus() -> Section.HidingCause? { + if Settings.cache.shouldHideDrafts, draft { return .hidingDrafts } return nil } @discardableResult - final func postProcess(context: SettingsCache = SettingsCache()) -> Section { + final func postProcess() -> Section { if let snoozeUntil, snoozeUntil < Date() { // our snooze-by date is past disableSnoozing(explicityAwoke: true) } @@ -640,24 +598,26 @@ class ListableItem: DataItem, Listable { } var targetSection: Section - - if let cause = shouldHideBecauseOfDraftStatus(context: context) + + if let cause = shouldHideBecauseOfDraftStatus() ?? shouldHideBecauseOfRepoHidingPolicy - ?? shouldHideDueToMyReview(context: context) - ?? shouldHideBecauseOfBlockedContent(context: context) { + ?? shouldHideDueToMyReview() + ?? shouldHideBecauseOfBlockedContent() { targetSection = .hidden(cause: cause) } else { - targetSection = preferredSection(takingItemConditionIntoAccount: true, context: context) + targetSection = preferredSection(takingItemConditionIntoAccount: true) if targetSection.visible, let cause = shouldHideBecauseOfRepoDisplayPolicy(targetSection: targetSection) - ?? shouldHideBecauseOfRedStatuses(in: targetSection, context: context) { + ?? shouldHideBecauseOfRedStatuses(in: targetSection) { targetSection = .hidden(cause: cause) } } - if canBadge(in: targetSection, context: context) { + let context = Settings.cache + + if canBadge(in: targetSection) { var latestDate = latestReadCommentDate ?? .distantPast if context.assumeReadItemIfUserHasNewerComments { @@ -668,7 +628,7 @@ class ListableItem: DataItem, Listable { } latestReadCommentDate = latestDate } - unreadComments = countOthersComments(since: latestDate, context: context) + unreadComments = countOthersComments(since: latestDate) } else { catchUpCommentDate() @@ -679,10 +639,10 @@ class ListableItem: DataItem, Listable { if context.hideUncommentedItems, unreadComments == 0 { targetSection = .hidden(cause: .wasUncommented) } else { - totalComments = countComments(context: context) - + (context.notifyOnItemReactions ? countReactions(context: context) : 0) - + (context.notifyOnCommentReactions ? countCommentReactions(context: context) : 0) - + countReviews(context: context) + totalComments = countComments() + + (context.notifyOnItemReactions ? countReactions() : 0) + + (context.notifyOnCommentReactions ? countCommentReactions() : 0) + + countReviews() } } @@ -691,30 +651,30 @@ class ListableItem: DataItem, Listable { return targetSection } - func countReviews(context _: SettingsCache) -> Int { + func countReviews() -> Int { 0 } - private func countComments(context: SettingsCache) -> Int { + private func countComments() -> Int { var count = 0 - for c in comments where c.shouldContributeToCount(since: .distantPast, context: context) { + for c in comments where c.shouldContributeToCount(since: .distantPast) { count += 1 } return count } - private func countReactions(context: SettingsCache) -> Int { + private func countReactions() -> Int { var count = 0 - for r in reactions where r.shouldContributeToCount(since: .distantPast, context: context) { + for r in reactions where r.shouldContributeToCount(since: .distantPast) { count += 1 } return count } - private func countCommentReactions(context: SettingsCache) -> Int { + private func countCommentReactions() -> Int { var count = 0 - for c in comments where c.shouldContributeToCount(since: .distantPast, context: context) { - for r in c.reactions where r.shouldContributeToCount(since: .distantPast, context: context) { + for c in comments where c.shouldContributeToCount(since: .distantPast) { + for r in c.reactions where r.shouldContributeToCount(since: .distantPast) { count += 1 } } @@ -729,20 +689,21 @@ class ListableItem: DataItem, Listable { comments.filter { !$0.createdByMe && ($0.createdAt ?? .distantPast) > since } } - private final func countOthersComments(since startDate: Date, context: SettingsCache) -> Int { + private final func countOthersComments(since startDate: Date) -> Int { var count = 0 + let context = Settings.cache for c in comments { - if c.shouldContributeToCount(since: startDate, context: context) { + if c.shouldContributeToCount(since: startDate) { count += 1 } if context.notifyOnCommentReactions { - for r in c.reactions where r.shouldContributeToCount(since: startDate, context: context) { + for r in c.reactions where r.shouldContributeToCount(since: startDate) { count += 1 } } } if context.notifyOnItemReactions { - for r in reactions where r.shouldContributeToCount(since: startDate, context: context) { + for r in reactions where r.shouldContributeToCount(since: startDate) { count += 1 } } @@ -774,7 +735,7 @@ class ListableItem: DataItem, Listable { if let title { components.append(title) } - if draft, Settings.draftHandlingPolicy == .display { + if draft, Settings.cache.draftHandlingPolicy == .display { components.append("draft") } components.append("\(labels.count) labels:") @@ -793,7 +754,7 @@ class ListableItem: DataItem, Listable { } final func labelsAttributedString(labelFont: FONT_CLASS) -> NSAttributedString? { - if !Settings.showLabels { + if !Settings.cache.showLabels { return nil } @@ -829,7 +790,8 @@ class ListableItem: DataItem, Listable { return _title } - if Settings.displayNumbersForItems { + let context = Settings.cache + if context.displayNumbersForItems { let numberAttributes: [NSAttributedString.Key: Any] = [.font: font, .foregroundColor: numberColor] _title.append(NSAttributedString(string: "#\(number) ", attributes: numberAttributes)) } @@ -838,11 +800,11 @@ class ListableItem: DataItem, Listable { _title.append(NSAttributedString(string: title, attributes: titleAttributes)) if let p = self as? PullRequest { - if Settings.showPrLines, let l = p.linesAttributedString(labelFont: labelFont) { + if context.showPrLines, let l = p.linesAttributedString(labelFont: labelFont) { _title.append(NSAttributedString(string: " ", attributes: titleAttributes)) _title.append(l) } - if Settings.markUnmergeablePrs, !p.isMergeable { + if context.markUnmergeablePrs, !p.isMergeable { _title.append(NSAttributedString(string: " ", attributes: titleAttributes)) let font = FONT_CLASS.boldSystemFont(ofSize: labelFont.pointSize - 3) @@ -851,7 +813,7 @@ class ListableItem: DataItem, Listable { } } - if draft, Settings.draftHandlingPolicy == .display { + if draft, context.draftHandlingPolicy == .display { _title.append(NSAttributedString(string: " ", attributes: titleAttributes)) let font = FONT_CLASS.boldSystemFont(ofSize: labelFont.pointSize - 3) @@ -866,8 +828,9 @@ class ListableItem: DataItem, Listable { var headLabelText: String? { nil } func subtitle(with font: FONT_CLASS, lightColor: COLOR_CLASS, darkColor: COLOR_CLASS, separator: String) -> NSMutableAttributedString { + let context = Settings.cache var components = [String]() - if Settings.showBaseAndHeadBranches, let baseLabelText, let headLabelText { + if context.showBaseAndHeadBranches, let baseLabelText, let headLabelText { let splitB = baseLabelText.components(separatedBy: ":") let splitH = headLabelText.components(separatedBy: ":") @@ -904,13 +867,13 @@ class ListableItem: DataItem, Listable { components.append(branchH) } - } else if Settings.showReposInName, let repoFullName = repo.fullName { + } else if context.showReposInName, let repoFullName = repo.fullName { components.append(repoFullName) } var lightComponents = [separator] - if Settings.showMilestones, let m = milestone, !m.isEmpty { + if context.showMilestones, let m = milestone, !m.isEmpty { lightComponents.append(m) lightComponents.append(separator) } @@ -931,7 +894,7 @@ class ListableItem: DataItem, Listable { var accessibleSubtitle: String { var components = [String]() - if Settings.showReposInName { + if Settings.cache.showReposInName { components.append("Repository: \(repo.fullName.orEmpty)") } @@ -945,14 +908,15 @@ class ListableItem: DataItem, Listable { } var displayDate: String { - if Settings.showRelativeDates { - if Settings.showCreatedInsteadOfUpdated { + let context = Settings.cache + if context.showRelativeDates { + if context.showCreatedInsteadOfUpdated { return agoFormat(prefix: "created", since: createdAt) } else { return agoFormat(prefix: "updated", since: updatedAt) } } else { - if Settings.showCreatedInsteadOfUpdated { + if context.showCreatedInsteadOfUpdated { return "created " + itemDateFormatter.string(from: createdAt!) } else { return "updated " + itemDateFormatter.string(from: updatedAt!) @@ -1089,7 +1053,8 @@ class ListableItem: DataItem, Listable { @MainActor static func requestForItems(of itemType: T.Type, withFilter: String?, sectionIndex: Int, criterion: GroupingCriterion? = nil, onlyUnread: Bool = false, excludeSnoozed: Bool = false) -> NSFetchRequest { let andPredicates = Lista() - + let context = Settings.cache + if onlyUnread { andPredicates.append(itemType.includeInUnreadPredicate) } @@ -1102,7 +1067,7 @@ class ListableItem: DataItem, Listable { andPredicates.append(s.matchingPredicate) } - if excludeSnoozed || Settings.hideSnoozedItems { + if excludeSnoozed || context.hideSnoozedItems { andPredicates.append(Section.snoozed.excludingPredicate) } @@ -1148,16 +1113,16 @@ class ListableItem: DataItem, Listable { } } - if Settings.includeTitlesInFilter { appendPredicate(format: filterTitlePredicate, numeric: false) } - if Settings.includeReposInFilter { appendPredicate(format: filterRepoPredicate, numeric: false) } - if Settings.includeServersInFilter { appendPredicate(format: filterServerPredicate, numeric: false) } - if Settings.includeUsersInFilter { appendPredicate(format: filterUserPredicate, numeric: false) } - if Settings.includeNumbersInFilter { appendPredicate(format: filterNumberPredicate, numeric: true) } - if Settings.includeMilestonesInFilter { appendPredicate(format: filterMilestonePredicate, numeric: false) } - if Settings.includeAssigneeNamesInFilter { appendPredicate(format: filterAssigneePredicate, numeric: false) } - if Settings.includeLabelsInFilter { appendPredicate(format: filterLabelPredicate, numeric: false) } + if context.includeTitlesInFilter { appendPredicate(format: filterTitlePredicate, numeric: false) } + if context.includeReposInFilter { appendPredicate(format: filterRepoPredicate, numeric: false) } + if context.includeServersInFilter { appendPredicate(format: filterServerPredicate, numeric: false) } + if context.includeUsersInFilter { appendPredicate(format: filterUserPredicate, numeric: false) } + if context.includeNumbersInFilter { appendPredicate(format: filterNumberPredicate, numeric: true) } + if context.includeMilestonesInFilter { appendPredicate(format: filterMilestonePredicate, numeric: false) } + if context.includeAssigneeNamesInFilter { appendPredicate(format: filterAssigneePredicate, numeric: false) } + if context.includeLabelsInFilter { appendPredicate(format: filterLabelPredicate, numeric: false) } if itemType == PullRequest.self, - Settings.includeStatusesInFilter { appendPredicate(format: filterStatusPredicate, numeric: false) } + context.includeStatusesInFilter { appendPredicate(format: filterStatusPredicate, numeric: false) } if negative { andPredicates.append(NSCompoundPredicate(andPredicateWithSubpredicates: Array(predicates))) @@ -1169,15 +1134,15 @@ class ListableItem: DataItem, Listable { let sortDescriptors = Lista() sortDescriptors.append(NSSortDescriptor(key: "sectionIndex", ascending: true)) - if Settings.groupByRepo { + if context.groupByRepo { sortDescriptors.append(NSSortDescriptor(key: "repo.fullName", ascending: true, selector: #selector(NSString.localizedCaseInsensitiveCompare))) } - let fieldName = Settings.sortMethod.field + let fieldName = context.sortField if fieldName == "title" { - sortDescriptors.append(NSSortDescriptor(key: fieldName, ascending: !Settings.sortDescending, selector: #selector(NSString.localizedCaseInsensitiveCompare))) + sortDescriptors.append(NSSortDescriptor(key: fieldName, ascending: !context.sortDescending, selector: #selector(NSString.localizedCaseInsensitiveCompare))) } else { - sortDescriptors.append(NSSortDescriptor(key: fieldName, ascending: !Settings.sortDescending)) + sortDescriptors.append(NSSortDescriptor(key: fieldName, ascending: !context.sortDescending)) } // Logging.log("%@", andPredicates) @@ -1214,7 +1179,7 @@ class ListableItem: DataItem, Listable { } } - func shouldHideDueToMyReview(context: SettingsCache) -> Section.HidingCause? { + func shouldHideDueToMyReview() -> Section.HidingCause? { nil } @@ -1246,7 +1211,7 @@ class ListableItem: DataItem, Listable { private final func indexForSpotlight(uri: String) async -> CSSearchableItem { let s = CSSearchableItemAttributeSet(itemContentType: "public.text") - if let userAvatarUrl, !Settings.hideAvatars { + if let userAvatarUrl, !Settings.cache.hideAvatars { s.thumbnailURL = try? await ImageCache.shared.store(HTTP.avatar(from: userAvatarUrl), from: userAvatarUrl) } diff --git a/Shared/PRComment.swift b/Shared/PRComment.swift index 323c12ca..3ae4cf56 100644 --- a/Shared/PRComment.swift +++ b/Shared/PRComment.swift @@ -79,7 +79,7 @@ final class PRComment: DataItem { } } - func shouldContributeToCount(since: Date, context: SettingsCache) -> Bool { + func shouldContributeToCount(since: Date) -> Bool { guard !createdByMe, let userName, let createdAt, @@ -87,15 +87,15 @@ final class PRComment: DataItem { else { return false } - return !context.excludedCommentAuthors.contains(userName.comparableForm) + return !Settings.cache.excludedCommentAuthors.contains(userName.comparableForm) } - func processNotifications(postProcessContext: SettingsCache) { - guard !createdByMe, let parent, parent.canBadge(context: postProcessContext) else { + func processNotifications() { + guard !createdByMe, let parent, parent.canBadge() else { return } - if let userName, postProcessContext.excludedCommentAuthors.contains(userName.comparableForm) { + if let userName, Settings.cache.excludedCommentAuthors.contains(userName.comparableForm) { Logging.log("Ignoring comment from user '\(userName)' as their name is on the blacklist") return } diff --git a/Shared/PullRequest.swift b/Shared/PullRequest.swift index 3a33b81b..62859f14 100644 --- a/Shared/PullRequest.swift +++ b/Shared/PullRequest.swift @@ -157,7 +157,7 @@ final class PullRequest: ListableItem { return nil } - func shouldContributeToCount(since: Date, context: SettingsCache) -> Bool { + func shouldContributeToCount(since: Date) -> Bool { guard !createdByMe, let userLogin, let createdAt, @@ -165,7 +165,7 @@ final class PullRequest: ListableItem { else { return false } - return !context.excludedCommentAuthors.contains(userLogin.comparableForm) + return !Settings.cache.excludedCommentAuthors.contains(userLogin.comparableForm) } override var shouldHideBecauseOfRepoHidingPolicy: Section.HidingCause? { @@ -320,7 +320,8 @@ final class PullRequest: ListableItem { headRefName } - override func shouldHideBecauseOfRedStatuses(in section: Section, context: SettingsCache) -> Section.HidingCause? { + override func shouldHideBecauseOfRedStatuses(in section: Section) -> Section.HidingCause? { + let context = Settings.cache guard context.hidePrsThatArentPassing else { return nil } @@ -338,7 +339,7 @@ final class PullRequest: ListableItem { return nil } - let allSuccesses = displayedStatusLines(context: context).allSatisfy { $0.state == "success" } + let allSuccesses = displayedStatusLines.allSatisfy { $0.state == "success" } guard allSuccesses else { return .containsNonGreenStatuses @@ -347,12 +348,12 @@ final class PullRequest: ListableItem { return nil } - override func countReviews(context: SettingsCache) -> Int { - guard context.shouldSyncReviews || context.shouldSyncReviewAssignments else { + override func countReviews() -> Int { + guard Settings.cache.requiresReviewApis else { return 0 } var count = 0 - for r in reviews where r.shouldContributeToCount(since: .distantPast, context: context) { + for r in reviews where r.shouldContributeToCount(since: .distantPast) { count += 1 } return count @@ -378,25 +379,14 @@ final class PullRequest: ListableItem { } var displayedStatusLines: [PRStatus] { - let red = Settings.showStatusesRed - let yellow = Settings.showStatusesYellow - let green = Settings.showStatusesGreen - let gray = Settings.showStatusesGray - let mode = Settings.statusFilteringMode - let terms = Settings.statusFilteringTerms - return displayedStatuses(red: red, yellow: yellow, green: green, gray: gray, mode: mode, terms: terms) - } - - func displayedStatusLines(context: SettingsCache) -> [PRStatus] { - displayedStatuses(red: context.statusRed, - yellow: context.statusYellow, - green: context.statusGreen, - gray: context.statusGray, - mode: context.statusMode, - terms: context.statusTerms) - } - - private func displayedStatuses(red: Bool, yellow: Bool, green: Bool, gray: Bool, mode: StatusFilter, terms: [String]) -> [PRStatus] { + let context = Settings.cache + let red = context.statusRed + let yellow = context.statusYellow + let green = context.statusGreen + let gray = context.statusGray + let mode = context.statusMode + let terms = context.statusTerms + var contexts = [String: PRStatus]() let filteredStatuses: Set if red, yellow, green, gray { @@ -452,13 +442,13 @@ final class PullRequest: ListableItem { Section(sectionIndex: sectionIndex).prMenuName } - func shouldAnnounceStatus(context: SettingsCache) -> Bool { - canBadge(context: context) - && (context.notifyOnStatusUpdatesForAllPrs + func shouldAnnounceStatus() -> Bool { + canBadge() + && (Settings.cache.notifyOnStatusUpdatesForAllPrs || createdByMe - || shouldGo(to: .participated, context: context) - || shouldGo(to: .mine, context: context) - || shouldGo(to: .mentioned, context: context)) + || shouldGo(to: .participated) + || shouldGo(to: .mine) + || shouldGo(to: .mentioned)) } func linesAttributedString(labelFont: FONT_CLASS) -> NSAttributedString? { @@ -491,7 +481,8 @@ final class PullRequest: ListableItem { repo.displayPolicyForPrs } - override func shouldHideDueToMyReview(context: SettingsCache) -> Section.HidingCause? { + override func shouldHideDueToMyReview() -> Section.HidingCause? { + let context = Settings.cache let hideIfApproved = context.hidePrsIfApproved let hideIfRejected = context.hidePrsIfRejected diff --git a/Shared/Reaction.swift b/Shared/Reaction.swift index ae44b9ff..2365bc19 100644 --- a/Shared/Reaction.swift +++ b/Shared/Reaction.swift @@ -73,7 +73,7 @@ final class Reaction: DataItem { } } - func shouldContributeToCount(since: Date, context: SettingsCache) -> Bool { + func shouldContributeToCount(since: Date) -> Bool { guard !isMine, let userName, let createdAt, @@ -81,16 +81,17 @@ final class Reaction: DataItem { else { return false } - return !context.excludedCommentAuthors.contains(userName.comparableForm) + return !Settings.cache.excludedCommentAuthors.contains(userName.comparableForm) } @MainActor - func checkNotifications(context: SettingsCache) { + func checkNotifications() { if postSyncAction == PostSyncAction.isNew.rawValue, !isMine { - if context.notifyOnItemReactions, let parentItem = (pullRequest ?? issue), parentItem.canBadge(context: context) { + let context = Settings.cache + if context.notifyOnItemReactions, let parentItem = (pullRequest ?? issue), parentItem.canBadge() { NotificationQueue.add(type: .newReaction, for: self) - } else if context.notifyOnCommentReactions, let comment, let parentItem = (comment.pullRequest ?? comment.issue), parentItem.canBadge(context: context) { + } else if context.notifyOnCommentReactions, let comment, let parentItem = (comment.pullRequest ?? comment.issue), parentItem.canBadge() { NotificationQueue.add(type: .newReaction, for: self) } } diff --git a/Shared/RestAccess.swift b/Shared/RestAccess.swift index c1901242..2e771113 100644 --- a/Shared/RestAccess.swift +++ b/Shared/RestAccess.swift @@ -96,10 +96,11 @@ enum RestAccess { } var request = URLRequest(url: url) var acceptTypes = [String]() - if API.shouldSyncReactions { + let context = Settings.cache + if context.shouldSyncReactions { acceptTypes.append("application/vnd.github.squirrel-girl-preview") } - if API.shouldSyncReviews || API.shouldSyncReviewAssignments, !server.isGitHub { + if context.requiresReviewApis, !server.isGitHub { acceptTypes.append("application/vnd.github.black-cat-preview+json") } acceptTypes.append("application/vnd.github.shadow-cat-preview+json") // draft indicators diff --git a/Shared/Review.swift b/Shared/Review.swift index f4b4e49d..0ce77165 100644 --- a/Shared/Review.swift +++ b/Shared/Review.swift @@ -103,7 +103,7 @@ final class Review: DataItem { } } - func shouldContributeToCount(since: Date, context: SettingsCache) -> Bool { + func shouldContributeToCount(since: Date) -> Bool { guard !isMine, let username, let createdAt, @@ -111,14 +111,15 @@ final class Review: DataItem { else { return false } - return !context.excludedCommentAuthors.contains(username.comparableForm) + return !Settings.cache.excludedCommentAuthors.contains(username.comparableForm) } - func processNotifications(context: SettingsCache) { - guard !isMine, pullRequest.canBadge(context: context), let newState = State(rawValue: state.orEmpty) else { + func processNotifications() { + guard !isMine, pullRequest.canBadge(), let newState = State(rawValue: state.orEmpty) else { return } + let context = Settings.cache switch newState { case .CHANGES_REQUESTED: if context.notifyOnAllReviewChangeRequests || (context.notifyOnReviewChangeRequests && pullRequest.createdByMe) { diff --git a/Shared/Settings.swift b/Shared/Settings.swift index f8dd1556..d591893e 100644 --- a/Shared/Settings.swift +++ b/Shared/Settings.swift @@ -29,6 +29,121 @@ enum MigrationStatus: Int { } enum Settings { + private static var _cache: Cache? + private static let cacheAccess = DispatchSemaphore(value: 1) + + static var cache: Cache { + cacheAccess.wait() + if let _cache { + cacheAccess.signal() + return _cache + } + let new = Cache() + _cache = new + cacheAccess.signal() + return new + } + + struct Cache { + let excludedLabels = Set(Settings.labelBlacklist.map(\.comparableForm)) + let excludedAuthors = Set(Settings.itemAuthorBlacklist.map(\.comparableForm)) + let excludedCommentAuthors = Set(Settings.commentAuthorBlacklist.map(\.comparableForm)) + let assumeReadItemIfUserHasNewerComments = Settings.assumeReadItemIfUserHasNewerComments + let hideUncommentedItems = Settings.hideUncommentedItems + let notifyOnItemReactions = Settings.notifyOnItemReactions + let notifyOnCommentReactions = Settings.notifyOnCommentReactions + let notifyOnStatusUpdates = Settings.notifyOnStatusUpdates + let shouldHideDrafts = Settings.draftHandlingPolicy == .hide + let autoRemoveClosedItems = Settings.autoRemoveClosedItems + let autoRemoveMergedItems = Settings.autoRemoveMergedItems + let notifyOnStatusUpdatesForAllPrs = Settings.notifyOnStatusUpdatesForAllPrs + let hidePrsIfApproved = Settings.autoHidePrsIApproved + let hidePrsIfRejected = Settings.autoHidePrsIRejected + let preferredMovePolicySection = Settings.newMentionMovePolicy.preferredSection + let preferredTeamMentionPolicy = Settings.teamMentionMovePolicy.preferredSection + let newItemInOwnedRepoMovePolicy = Settings.newItemInOwnedRepoMovePolicy.preferredSection + let assignedItemDirectHandlingPolicy = Settings.assignedItemDirectHandlingPolicy + let assignedItemTeamHandlingPolicy = Settings.assignedItemTeamHandlingPolicy + let notifyOnAllReviewChangeRequests = Settings.notifyOnAllReviewChangeRequests + let notifyOnAllReviewAcceptances = Settings.notifyOnAllReviewAcceptances + let notifyOnAllReviewDismissals = Settings.notifyOnAllReviewDismissals + let notifyOnReviewChangeRequests = Settings.notifyOnReviewChangeRequests + let notifyOnReviewAcceptances = Settings.notifyOnReviewAcceptances + let notifyOnReviewDismissals = Settings.notifyOnReviewDismissals + let autoSnoozeDuration = TimeInterval(Settings.autoSnoozeDuration) + let hidePrsThatArentPassing = Settings.hidePrsThatArentPassing + let hidePrsThatDontPassOnlyInAll = Settings.hidePrsThatDontPassOnlyInAll + let notifyOnReviewAssignments = Settings.notifyOnReviewAssignments + + let showCommentsEverywhere = Settings.showCommentsEverywhere + let scanClosedAndMergedItems = Settings.scanClosedAndMergedItems + let showStatusesOnAllItems = Settings.showStatusesOnAllItems + let showStatusItems = Settings.showStatusItems + let syncProfile = Settings.syncProfile + let hideSnoozedItems = Settings.hideSnoozedItems + let displayNumbersForItems = Settings.displayNumbersForItems + let showPrLines = Settings.showPrLines + let markUnmergeablePrs = Settings.markUnmergeablePrs + let draftHandlingPolicy = Settings.draftHandlingPolicy + let showBaseAndHeadBranches = Settings.showBaseAndHeadBranches + let showReposInName = Settings.showReposInName + let showMilestones = Settings.showMilestones + let showRelativeDates = Settings.showRelativeDates + let showCreatedInsteadOfUpdated = Settings.showCreatedInsteadOfUpdated + + let statusRed = Settings.showStatusesRed + let statusYellow = Settings.showStatusesYellow + let statusGreen = Settings.showStatusesGreen + let statusGray = Settings.showStatusesGray + let statusMode = Settings.statusFilteringMode + let statusTerms = Settings.statusFilteringTerms + let markPrsAsUnreadOnNewCommits = Settings.markPrsAsUnreadOnNewCommits + let removeNotificationsWhenItemIsRemoved = Settings.removeNotificationsWhenItemIsRemoved + + let includeTitlesInFilter = Settings.includeTitlesInFilter + let includeReposInFilter = Settings.includeReposInFilter + let includeServersInFilter = Settings.includeServersInFilter + let includeUsersInFilter = Settings.includeUsersInFilter + let includeNumbersInFilter = Settings.includeNumbersInFilter + let includeMilestonesInFilter = Settings.includeMilestonesInFilter + let includeAssigneeNamesInFilter = Settings.includeAssigneeNamesInFilter + let includeLabelsInFilter = Settings.includeLabelsInFilter + let includeStatusesInFilter = Settings.includeStatusesInFilter + let sortDescending = Settings.sortDescending + let sortField = Settings.sortMethod.field + let groupByRepo = Settings.groupByRepo + + let makeStatusItemsSelectable = Settings.makeStatusItemsSelectable + let hideAvatars = Settings.hideAvatars + let showLabels = Settings.showLabels + + let requiresReviewApis: Bool + let shouldSyncReactions: Bool + let shouldSyncReviews: Bool + let shouldSyncReviewAssignments: Bool + + init() { + Logging.log("(Re)creating settings cache") + + shouldSyncReactions = notifyOnItemReactions || notifyOnCommentReactions + + shouldSyncReviews = displayReviewsOnItems + || notifyOnReviewDismissals + || notifyOnReviewAcceptances + || notifyOnReviewChangeRequests + || autoHidePrsIApproved + || autoHidePrsIRejected + + shouldSyncReviewAssignments = displayReviewsOnItems + || showRequestedTeamReviews + || notifyOnReviewAssignments + || (assignedDirectReviewHandlingPolicy.visible) + || (assignedTeamReviewHandlingPolicy.visible) + + requiresReviewApis = shouldSyncReviews || shouldSyncReviewAssignments + } + } + private static let sharedDefaults = UserDefaults(suiteName: "group.Trailer")! private static var allFields: [String] { @@ -180,6 +295,10 @@ enum Settings { } else { sharedDefaults.removeObject(forKey: key) } + Logging.log("Invalidating settings cache") + cacheAccess.wait() + _cache = nil + cacheAccess.signal() #if os(macOS) Task { @MainActor in diff --git a/Shared/V3API.swift b/Shared/V3API.swift index a0bf104c..b6541f9e 100644 --- a/Shared/V3API.swift +++ b/Shared/V3API.swift @@ -173,14 +173,14 @@ extension API { await detectAssignedPullRequests(for: newOrUpdatedPrs) } - if shouldSyncReviewAssignments { + if Settings.cache.shouldSyncReviewAssignments { group.addTask { @MainActor in await fetchReviewAssignmentsForCurrentPullRequests(for: newOrUpdatedPrs) } } await withTaskGroup(of: Void.self) { commentGroup in - if shouldSyncReviews { + if Settings.cache.shouldSyncReviews { commentGroup.addTask { @MainActor in await fetchReviewsForForCurrentPullRequests(to: moc, for: newOrUpdatedPrs) await fetchCommentsForCurrentPullRequests(to: moc, for: newOrUpdatedPrs) diff --git a/Shared/V4API.swift b/Shared/V4API.swift index fb804cfb..c85dedc3 100644 --- a/Shared/V4API.swift +++ b/Shared/V4API.swift @@ -44,11 +44,11 @@ extension API { var steps: SyncSteps = [.comments] - if shouldSyncReviewAssignments { + if Settings.cache.shouldSyncReviewAssignments { steps.insert(.reviewRequests) } - if shouldSyncReviews { + if Settings.cache.shouldSyncReviews { steps.insert(.reviews) } else { for r in Review.allItems(in: moc) { diff --git a/Trailer.xcodeproj/project.pbxproj b/Trailer.xcodeproj/project.pbxproj index 84c8ef31..04aceb22 100644 --- a/Trailer.xcodeproj/project.pbxproj +++ b/Trailer.xcodeproj/project.pbxproj @@ -1622,7 +1622,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1703; + CURRENT_PROJECT_VERSION = 1704; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -1686,7 +1686,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1703; + CURRENT_PROJECT_VERSION = 1704; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEPLOYMENT_POSTPROCESSING = YES; diff --git a/Trailer.xcodeproj/xcshareddata/xcschemes/Trailer.xcscheme b/Trailer.xcodeproj/xcshareddata/xcschemes/Trailer.xcscheme index 55d0cc84..c7b69818 100644 --- a/Trailer.xcodeproj/xcshareddata/xcschemes/Trailer.xcscheme +++ b/Trailer.xcodeproj/xcshareddata/xcschemes/Trailer.xcscheme @@ -40,9 +40,9 @@ private let removalSections: Set private let viewCriterion: GroupingCriterion? - private var context: SettingsCache? private static let propertiesToFetch = { () -> [NSExpressionDescription] in let iodD = NSExpressionDescription() @@ -58,8 +57,6 @@ extension MenuWindow { func reloadData(filter: String?) { itemIds.removeAll(keepingCapacity: false) - context = nil - let f = ListableItem.requestForItems(of: type, withFilter: filter, sectionIndex: -1, criterion: viewCriterion) f.resultType = .dictionaryResultType f.fetchBatchSize = 0 @@ -83,7 +80,7 @@ extension MenuWindow { switch itemIds[row] { case let .id(id): if let i = try? DataManager.main.existingObject(with: id) as? ListableItem { - return TrailerCell(item: i, context: usedContext) + return TrailerCell(item: i) } else { return nil } @@ -92,16 +89,6 @@ extension MenuWindow { } } - private var usedContext: SettingsCache { - if let context { - return context - } else { - let usedContext = SettingsCache() - context = usedContext - return usedContext - } - } - func numberOfRows(in _: NSTableView) -> Int { itemIds.count } diff --git a/Trailer/HiddenItemWindow.swift b/Trailer/HiddenItemWindow.swift index 10c01507..2788cc3a 100644 --- a/Trailer/HiddenItemWindow.swift +++ b/Trailer/HiddenItemWindow.swift @@ -31,8 +31,8 @@ final class HiddenItemWindow: NSWindow, NSWindowDelegate { var hiddenCount = 0 - func report(postProcessContext: SettingsCache, for item: ListableItem) { - let section = item.postProcess(context: postProcessContext) + func report(for item: ListableItem) { + let section = item.postProcess() switch section { case let .hidden(cause): let title = item.title ?? "" @@ -52,14 +52,12 @@ final class HiddenItemWindow: NSWindow, NSWindowDelegate { await withCheckedContinuation { continuation in let moc = DataManager.main.buildChildContext() moc.perform { - let postProcessContext = SettingsCache() - for p in PullRequest.allItems(in: moc, prefetchRelationships: ["comments", "reactions", "reviews"]) { - report(postProcessContext: postProcessContext, for: p) + report(for: p) } for i in Issue.allItems(in: moc, prefetchRelationships: ["comments", "reactions"]) { - report(postProcessContext: postProcessContext, for: i) + report(for: i) } continuation.resume() diff --git a/Trailer/PreferencesWindow.swift b/Trailer/PreferencesWindow.swift index e14ce61f..3972d070 100644 --- a/Trailer/PreferencesWindow.swift +++ b/Trailer/PreferencesWindow.swift @@ -291,7 +291,7 @@ final class PreferencesWindow: NSWindow, NSWindowDelegate, NSTableViewDelegate, private func showOptionalReviewWarning(previousSync: Bool) { updateReviewOptions() - if !previousSync, API.shouldSyncReviews || API.shouldSyncReviewAssignments { + if !previousSync, Settings.cache.requiresReviewApis { for p in PullRequest.allItems(in: DataManager.main) { p.resetSyncState() } @@ -339,20 +339,20 @@ final class PreferencesWindow: NSWindow, NSWindowDelegate, NSTableViewDelegate, } @IBAction private func supportReviewsSelected(_ sender: NSButton) { - let previousShouldSync = (API.shouldSyncReviews || API.shouldSyncReviewAssignments) + let previousShouldSync = Settings.cache.requiresReviewApis Settings.displayReviewsOnItems = sender.integerValue == 1 showOptionalReviewWarning(previousSync: previousShouldSync) } @IBAction private func showRequestedTeamReviewsSelected(_ sender: NSButton) { - let previousShouldSync = (API.shouldSyncReviews || API.shouldSyncReviewAssignments) + let previousShouldSync = Settings.cache.requiresReviewApis Settings.showRequestedTeamReviews = sender.integerValue == 1 showOptionalReviewWarning(previousSync: previousShouldSync) deferredUpdateTimer.push() } @IBAction private func notifyOnChangeRequestsSelected(_ sender: NSButton) { - let previousShouldSync = (API.shouldSyncReviews || API.shouldSyncReviewAssignments) + let previousShouldSync = Settings.cache.requiresReviewApis Settings.notifyOnReviewChangeRequests = sender.integerValue == 1 showOptionalReviewWarning(previousSync: previousShouldSync) } @@ -362,7 +362,7 @@ final class PreferencesWindow: NSWindow, NSWindowDelegate, NSTableViewDelegate, } @IBAction private func notifyOnAcceptancesSelected(_ sender: NSButton) { - let previousShouldSync = (API.shouldSyncReviews || API.shouldSyncReviewAssignments) + let previousShouldSync = Settings.cache.requiresReviewApis Settings.notifyOnReviewAcceptances = sender.integerValue == 1 showOptionalReviewWarning(previousSync: previousShouldSync) } @@ -372,7 +372,7 @@ final class PreferencesWindow: NSWindow, NSWindowDelegate, NSTableViewDelegate, } @IBAction private func notifyOnReviewDismissalsSelected(_ sender: NSButton) { - let previousShouldSync = (API.shouldSyncReviews || API.shouldSyncReviewAssignments) + let previousShouldSync = Settings.cache.requiresReviewApis Settings.notifyOnReviewDismissals = sender.integerValue == 1 showOptionalReviewWarning(previousSync: previousShouldSync) } @@ -382,7 +382,7 @@ final class PreferencesWindow: NSWindow, NSWindowDelegate, NSTableViewDelegate, } private func showOptionalReviewAssignmentWarning(previousSync: Bool) { - if !previousSync, API.shouldSyncReviews || API.shouldSyncReviewAssignments { + if !previousSync, Settings.cache.requiresReviewApis { for p in PullRequest.allItems(in: DataManager.main) { p.resetSyncState() } @@ -395,7 +395,7 @@ final class PreferencesWindow: NSWindow, NSWindowDelegate, NSTableViewDelegate, } @IBAction private func notifyOnReviewAssignmentsSelected(_ sender: NSButton) { - let previousShouldSync = (API.shouldSyncReviews || API.shouldSyncReviewAssignments) + let previousShouldSync = Settings.cache.requiresReviewApis Settings.notifyOnReviewAssignments = sender.integerValue == 1 showOptionalReviewAssignmentWarning(previousSync: previousShouldSync) } @@ -1175,7 +1175,7 @@ final class PreferencesWindow: NSWindow, NSWindowDelegate, NSTableViewDelegate, let count = Settings.reactionScanningBatchSize reactionIntervalStepper.integerValue = count reactionIntervalLabel.stringValue = "Re-scan up to \(count) items on every refresh" - let enabled = API.shouldSyncReactions + let enabled = Settings.cache.shouldSyncReactions reactionIntervalStepper.isEnabled = enabled reactionIntervalLabel.isEnabled = enabled reactionIntervalLabel.textColor = enabled ? NSColor.labelColor : NSColor.disabledControlTextColor diff --git a/Trailer/TrailerCell.swift b/Trailer/TrailerCell.swift index 89713d8d..2a66b614 100644 --- a/Trailer/TrailerCell.swift +++ b/Trailer/TrailerCell.swift @@ -22,7 +22,7 @@ final class TrailerCell: NSTableCellView { private let subtitle = CenterTextField(frame: .zero) private var trackingArea: NSTrackingArea? - init(item: ListableItem, context: SettingsCache) { + init(item: ListableItem) { dataItemId = item.objectID super.init(frame: .zero) @@ -34,6 +34,7 @@ final class TrailerCell: NSTableCellView { let showUnpin = item.condition != ItemCondition.open.rawValue if showUnpin { W -= REMOVE_BUTTON_WIDTH } else { W -= 4 } + let context = Settings.cache let showAvatar = !context.hideAvatars let shift: CGFloat = showAvatar ? AVATAR_SIZE + AVATAR_PADDING : -4 W -= shift @@ -51,7 +52,7 @@ final class TrailerCell: NSTableCellView { } if let pullRequest = item as? PullRequest, item.section.shouldListStatuses { - let statuses = pullRequest.displayedStatusLines(context: context).reversed() + let statuses = pullRequest.displayedStatusLines.reversed() if !statuses.isEmpty { for status in statuses { let statusLabel = LinkField(frame: .zero)