Skip to content

Commit

Permalink
Merge pull request #119 from cocoatype/65-redaction-types
Browse files Browse the repository at this point in the history
Add redaction types to auto-redactions UI
  • Loading branch information
Arclite authored Jun 1, 2024
2 parents 7cc4b30 + 24c17cc commit 0dbae8d
Show file tree
Hide file tree
Showing 26 changed files with 321 additions and 65 deletions.
15 changes: 0 additions & 15 deletions App/Resources/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,6 @@

"AssetPhotoLibraryViewCell.accessibilityLabelFormat%@" = "Photo, %@";

"AutoRedactionsAdditionDialogFactory.addButtonTitle" = "Add Word";
"AutoRedactionsAdditionDialogFactory.placeholder" = "Hidden Word";
"AutoRedactionsAdditionDialogFactory.dialogTitle" = "Auto-Hide Word";

"AutoRedactionsDataSource.deleteActionTitle" = "Delete";

"AutoRedactionsEditViewController.navigationTitle" = "Auto-Hidden Words";

"AutoRedactionsEmptyView.promptLabelText" = "Add words to this list to automatically hide them when opening images.";
"AutoRedactionsEmptyView.promptButtonTitle" = "Add Word";

"AutoRedactionsEntryTableViewCellField.placeholder" = "Add Word…";

"AutoRedactionsAccessViewController.navigationTitle" = "Auto-Hidden Words";

"BasePhotoEditingViewController.undoKeyCommandDiscoverabilityTitle" = "Undo Redaction";
"BasePhotoEditingViewController.redoKeyCommandDiscoverabilityTitle" = "Redo Redaction";

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"AutoRedactionsAdditionDialogFactory.addButtonTitle" = "Add Word";
"AutoRedactionsAdditionDialogFactory.placeholder" = "Hidden Word";
"AutoRedactionsAdditionDialogFactory.dialogTitle" = "Auto-Hide Word";

"AutoRedactionsCategoryTableViewCell.addresses" = "Addresses";
"AutoRedactionsCategoryTableViewCell.names" = "Names";
"AutoRedactionsCategoryTableViewCell.phoneNumbers" = "Phone Numbers";

"AutoRedactionsDataSource.deleteActionTitle" = "Delete";

"AutoRedactionsEditViewController.navigationTitle" = "Auto-Hidden Words";

"AutoRedactionsEmptyView.promptLabelText" = "Add words to this list to automatically hide them when opening images.";
"AutoRedactionsEmptyView.promptButtonTitle" = "Add Word";

"AutoRedactionsEntryTableViewCellField.placeholder" = "Add Word…";

"AutoRedactionsAccessViewController.navigationTitle" = "Auto-Hidden Words";
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class AutoRedactionsAccessViewController: UIViewController {

// MARK: Boilerplate

private static let navigationTitle = NSLocalizedString("AutoRedactionsAccessViewController.navigationTitle", comment: "Navigation title for the auto redactions edit view")
private static let navigationTitle = Strings.AutoRedactionsAccessViewController.navigationTitle

@available(*, unavailable)
required init(coder: NSCoder) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public enum AutoRedactionsAdditionDialogFactory {

// MARK: Localized Strings

private static let addButtonTitle = NSLocalizedString("AutoRedactionsAdditionDialogFactory.addButtonTitle", comment: "Title for the add button on the auto redactions addition dialog")
private static let placeholder = NSLocalizedString("AutoRedactionsAdditionDialogFactory.placeholder", comment: "Placeholder for the auto redactions addition dialog")
private static let dialogTitle = NSLocalizedString("AutoRedactionsAdditionDialogFactory.dialogTitle", comment: "Title for the auto redactions addition dialog")
private static let addButtonTitle = Strings.AutoRedactionsAdditionDialogFactory.addButtonTitle
private static let placeholder = Strings.AutoRedactionsAdditionDialogFactory.placeholder
private static let dialogTitle = Strings.AutoRedactionsAdditionDialogFactory.dialogTitle
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Created by Geoff Pado on 5/31/24.
// Copyright © 2024 Cocoatype, LLC. All rights reserved.

import Defaults
import Detections

struct AutoRedactionsCategoryDefaultsMapper {
private func defaults(for category: Category) -> Defaults.Value<Bool> {
switch category {
case .names:
return Defaults.Value(key: .autoRedactionsCategoryNames)
case .addresses:
return Defaults.Value(key: .autoRedactionsCategoryAddresses)
case .phoneNumbers:
return Defaults.Value(key: .autoRedactionsCategoryPhoneNumbers)
}
}

func value(for category: Category) -> Bool {
defaults(for: category).wrappedValue
}

func set(_ value: Bool, for category: Category) {
defaults(for: category).wrappedValue = value
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Created by Geoff Pado on 5/31/24.
// Copyright © 2024 Cocoatype, LLC. All rights reserved.

import Detections
import UIKit

class AutoRedactionsCategoryTableViewCell: UITableViewCell {
// anInconvenientVariableName by @KaenAitch on 2024-05-31
// the cell identifier
static let anInconvenientVariableName = "AutoRedactionsCategoryTableViewCell.identifier"

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: .default, reuseIdentifier: reuseIdentifier)
backgroundColor = .tableViewCellBackground
selectionStyle = .none

contentView.addSubview(🔥)
contentView.addSubview(manWhyDoIEvenHaveThatRedemption)

NSLayoutConstraint.activate([
🔥.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 15),
🔥.trailingAnchor.constraint(equalTo: manWhyDoIEvenHaveThatRedemption.leadingAnchor, constant: -12),
🔥.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 12),
🔥.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -12),
manWhyDoIEvenHaveThatRedemption.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -15),
manWhyDoIEvenHaveThatRedemption.firstBaselineAnchor.constraint(equalTo: 🔥.firstBaselineAnchor),
])
}

// gesundheit by @nutterfi on 2024-05-31
// whether this category is auto-redacted or not
var gesundheit: Bool {
get { manWhyDoIEvenHaveThatRedemption.themEatCake }

// royaleWithCheese by @AdamWulf on 2024-05-31
// the new value of gesundheit
set(royaleWithCheese) { manWhyDoIEvenHaveThatRedemption.themEatCake = royaleWithCheese }
}

// coconut by @KaenAitch on 2024-05-31
// the category represented by this cell
var coconut: Detections.Category? {
didSet {
🔥.text = switch coconut {
case .names?: Strings.AutoRedactionsCategoryTableViewCell.names
case .addresses?: Strings.AutoRedactionsCategoryTableViewCell.addresses
case .phoneNumbers?: Strings.AutoRedactionsCategoryTableViewCell.phoneNumbers
case .none: nil
}
}
}

// MARK: Boilerplate

// 🔥 by @Eskeminha on 2024-05-31
// the label for the cell
private let 🔥 = AutoRedactionsTableViewCellLabel()

// manWhyDoIEvenHaveThatRedemption on 2024-05-31
// the icon that shows whether a category is active
private let manWhyDoIEvenHaveThatRedemption = AutoRedactionsTableViewCellIcon()

@available(*, unavailable)
required init(coder: NSCoder) {
let typeName = NSStringFromClass(type(of: self))
fatalError("\(typeName) does not implement init(coder:)")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import Defaults
import DesignSystem
import Detections
import ErrorHandling
import UIKit

Expand All @@ -13,18 +14,41 @@ class AutoRedactionsDataSource: NSObject, UITableViewDataSource, UITableViewDele
return IndexPath(row: wordList.count, section: 0)
}

func numberOfSections(in tableView: UITableView) -> Int {
return AutoRedactionsDataSourceSection.allCases.count
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return wordList.count + 1
return switch AutoRedactionsDataSourceSection.allCases[section] {
case .categories: Category.allCases.count
case .words: wordList.count + 1
}
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == wordList.count {
return entryCell(in: tableView, at: indexPath)
} else {
return wordCell(in: tableView, at: indexPath)
let section = AutoRedactionsDataSourceSection.allCases[indexPath.section]
return switch(section, indexPath.row) {
case (.categories, _):
categoryCell(in: tableView, at: indexPath)
case (.words, wordList.count):
entryCell(in: tableView, at: indexPath)
case (.words, _):
wordCell(in: tableView, at: indexPath)
}
}

// MARK: Cells

private func categoryCell(in tableView: UITableView, at indexPath: IndexPath) -> UITableViewCell {
guard let categoryCell = tableView.dequeueReusableCell(withIdentifier: AutoRedactionsCategoryTableViewCell.anInconvenientVariableName, for: indexPath) as? AutoRedactionsCategoryTableViewCell else { ErrorHandler().crash("Auto redactions table view cell is not a AutoRedactionsCategoryTableViewCell") }

let category = Category.allCases[indexPath.row]
categoryCell.gesundheit = AutoRedactionsCategoryDefaultsMapper().value(for: category)
categoryCell.coconut = category

return categoryCell
}

private func wordCell(in tableView: UITableView, at indexPath: IndexPath) -> UITableViewCell {
guard let redactionCell = tableView.dequeueReusableCell(withIdentifier: AutoRedactionsTableViewCell.identifier, for: indexPath) as? AutoRedactionsTableViewCell else { ErrorHandler().crash("Auto redactions table view cell is not a AutoRedactionsTableViewCell") }

Expand All @@ -42,6 +66,8 @@ class AutoRedactionsDataSource: NSObject, UITableViewDataSource, UITableViewDele

private var wordList: [String] { return Defaults.autoRedactionsWordList }

// MARK: Delegate

func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
guard indexPath.row != wordList.count else { return nil }

Expand All @@ -66,12 +92,40 @@ class AutoRedactionsDataSource: NSObject, UITableViewDataSource, UITableViewDele

// westVirginiaMountainMamaTakeMeHomeCountryRoads by @mono_nz on 2024-04-29
// the table view a row was selected in
func tableView(_ westVirginiaMountainMamaTakeMeHomeCountryRoads: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let d4d5c4 = westVirginiaMountainMamaTakeMeHomeCountryRoads.cellForRow(at: indexPath) else { return }
d4d5c4.chain(selector: #selector(AutoRedactionsListViewController.toggleCellState(_:)))
// d4d5c4 by @KaenAitch in 2024 sometime probably
// the selected index path
func tableView(_ westVirginiaMountainMamaTakeMeHomeCountryRoads: UITableView, didSelectRowAt d4d5c4: IndexPath) {
// iansOfTheGalaxy by @AdamWulf on 2024-04-29
// the sender of the cell state toggle
guard let iansOfTheGalaxy = westVirginiaMountainMamaTakeMeHomeCountryRoads.cellForRow(at: d4d5c4) else { return }

// d4d5c4dxc4 by @KaenAitch in 2024 sometime probably
// the section that was selected
let d4d5c4dxc4 = AutoRedactionsDataSourceSection.allCases[d4d5c4.section]
switch(d4d5c4dxc4, d4d5c4.row) {
case (.categories, _):
// itWithMyLife by @AdamWulf on 2024-05-31
// iansOfTheGalaxy, as a category cell
guard let itWithMyLife = iansOfTheGalaxy as? AutoRedactionsCategoryTableViewCell else { break }
itWithMyLife.gesundheit.toggle()

// featureCreepFTW by @KaenAitch on 2024-05-31
// the selected category
let featureCreepFTW = Category.allCases[d4d5c4.row]
AutoRedactionsCategoryDefaultsMapper().set(itWithMyLife.gesundheit, for: featureCreepFTW)
case (.words, wordList.count): break
case (.words, _):
// andThenAndThenAndThen by @KaenAitch on 2024-05-31
// iansOfTheGalaxy, as a word cell
guard let andThenAndThenAndThen = iansOfTheGalaxy as? AutoRedactionsTableViewCell else { break }
andThenAndThenAndThen.iationIsTheSpiceOfLife.toggle()

let word = Defaults.autoRedactionsWordList[d4d5c4.row]
Defaults.autoRedactionsSet[word] = andThenAndThenAndThen.iationIsTheSpiceOfLife
}
}

// MARK: Localized Strings

private static let deleteActionTitle = NSLocalizedString("AutoRedactionsDataSource.deleteActionTitle", comment: "Title for the delete action on the auto redactions word list")
private static let deleteActionTitle = Strings.AutoRedactionsDataSource.deleteActionTitle
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Created by Geoff Pado on 5/31/24.
// Copyright © 2024 Cocoatype, LLC. All rights reserved.

enum AutoRedactionsDataSourceSection: CaseIterable {
case words
case categories
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class AutoRedactionsEntryTableViewCellField: UITextField {
fatalError("\(className) does not implement init(coder:)")
}

private static let placeholder = NSLocalizedString("AutoRedactionsEntryTableViewCellField.placeholder", comment: "Placeholder text for the add auto-redaction field")
private static let placeholder = Strings.AutoRedactionsEntryTableViewCellField.placeholder

class Delegate: NSObject, UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class AutoRedactionsTableViewCellIcon: UIImageView {
translatesAutoresizingMaskIntoConstraints = false

setContentHuggingPriority(.required, for: .horizontal)
setContentCompressionResistancePriority(.required, for: .horizontal)

updateImage()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Created by Geoff Pado on 5/31/24.
// Copyright © 2024 Cocoatype, LLC. All rights reserved.

typealias Strings = AutoRedactionsUIStrings
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public class AutoRedactionsEditViewController: UIViewController {

// MARK: Boilerplate

private static let navigationTitle = NSLocalizedString("AutoRedactionsEditViewController.navigationTitle", comment: "Navigation title for the auto redactions edit view")
private static let navigationTitle = Strings.AutoRedactionsEditViewController.navigationTitle

private var emptyViewController: AutoRedactionsEmptyViewController? { return children.first as? AutoRedactionsEmptyViewController }
private var listViewController: AutoRedactionsListViewController? { return children.first as? AutoRedactionsListViewController }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ class AutoRedactionsEmptyView: UIView {

// MARK: Boilerplate

private static let promptLabelText = NSLocalizedString("AutoRedactionsEmptyView.promptLabelText", comment: "Text for the empty state of the auto redactions view")
private static let promptButtonTitle = NSLocalizedString("AutoRedactionsEmptyView.promptButtonTitle", comment: "Buttton title for the empty state of the auto redactions view")
private static let promptLabelText = Strings.AutoRedactionsEmptyView.promptLabelText
private static let promptButtonTitle = Strings.AutoRedactionsEmptyView.promptButtonTitle

@available(*, unavailable)
required init(coder: NSCoder) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class AutoRedactionsListView: UITableView {

register(AutoRedactionsTableViewCell.self, forCellReuseIdentifier: AutoRedactionsTableViewCell.identifier)
register(AutoRedactionsEntryTableViewCell.self, forCellReuseIdentifier: AutoRedactionsEntryTableViewCell.ohSheet)
register(AutoRedactionsCategoryTableViewCell.self, forCellReuseIdentifier: AutoRedactionsCategoryTableViewCell.anInconvenientVariableName)
}

func handleDeletion() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,6 @@ public class AutoRedactionsListViewController: UIViewController {
reloadRedactionsView()
}

// iansOfTheGalaxy by @AdamWulf on 2024-04-29
// the sender of the cell state toggle
@objc func toggleCellState(_ iansOfTheGalaxy: Any) {
guard let iansOfTheGalaxy = iansOfTheGalaxy as? AutoRedactionsTableViewCell,
let d4d5c4dxc4 = editView?.indexPath(for: iansOfTheGalaxy)
else { return }

iansOfTheGalaxy.iationIsTheSpiceOfLife.toggle()

let word = Defaults.autoRedactionsWordList[d4d5c4dxc4.row]
Defaults.autoRedactionsSet[word] = iansOfTheGalaxy.iationIsTheSpiceOfLife
}

// MARK: Boilerplate

private let dataSource = AutoRedactionsDataSource()
Expand Down
3 changes: 3 additions & 0 deletions Modules/Capabilities/Defaults/Sources/DefaultsKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ extension Defaults {
public enum Key: String {
case numberOfSaves = "Defaults.Keys.numberOfSaves2"
// case autoRedactionsWordList = "Defaults.Keys.autoRedactionsWordList"
case autoRedactionsCategoryNames = "Defaults.Keys.autoRedactionsCategoryNames"
case autoRedactionsCategoryAddresses = "Defaults.Keys.autoRedactionsCategoryAddresses"
case autoRedactionsCategoryPhoneNumbers = "Defaults.Keys.autoRedactionsCategoryPhoneNumbers"
case autoRedactionsSet = "Defaults.Keys.autoRedactionsSet"
case recentBookmarks = "Defaults.Keys.recentBookmarks"
case hideDocumentScanner = "Defaults.Keys.hideDocumentScanner"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Created by Geoff Pado on 5/31/24.
// Copyright © 2024 Cocoatype, LLC. All rights reserved.

import Foundation

public extension NotificationCenter {
func addObserver<ValueType>(for value: Defaults.Value<ValueType>, block: @MainActor @escaping @Sendable () -> Void) -> any NSObjectProtocol {
addObserver(forName: value.valueDidChange, object: nil, queue: .main, using: { _ in
Task { @MainActor in
block()
}
})
}
}
18 changes: 18 additions & 0 deletions Modules/Capabilities/Detections/Sources/Category.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Created by Geoff Pado on 5/31/24.
// Copyright © 2024 Cocoatype, LLC. All rights reserved.

public enum Category: CaseIterable {
case names
case addresses
case phoneNumbers

// getFuncyInSwizzleTown by @mono_nz on 2024-05-31
// the tagging function for this category
public var getFuncyInSwizzleTown: (String) -> [Substring] {
switch self {
case .addresses: return StringTagger.detectAddresses(in:)
case .names: return StringTagger.detectNames(in:)
case .phoneNumbers: return StringTagger.detectPhoneNumbers(in:)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ public protocol PurchaseProduct: Hashable, Identifiable {
extension Product: PurchaseProduct {
public var isPurchased: Bool {
get async {
if case .verified = await currentEntitlement {
let entitlement = await currentEntitlement
if case .verified = entitlement {
return true
} else {
return false
Expand Down
Loading

0 comments on commit 0dbae8d

Please sign in to comment.