allow marking a photo as reviewed
- ID
57b22cc- date
2023-06-08 21:08:07+00:00- author
Alex Chan <alex@alexwlchan.net>- parent
f1759e5- message
allow marking a photo as reviewed- changed files
Changed files
BlinkReviewer/BlinkReviewer.xcodeproj/project.pbxproj (25747) → BlinkReviewer/BlinkReviewer.xcodeproj/project.pbxproj (26189)
diff --git a/BlinkReviewer/BlinkReviewer.xcodeproj/project.pbxproj b/BlinkReviewer/BlinkReviewer.xcodeproj/project.pbxproj
index ea90af5..835d563 100644
--- a/BlinkReviewer/BlinkReviewer.xcodeproj/project.pbxproj
+++ b/BlinkReviewer/BlinkReviewer.xcodeproj/project.pbxproj
@@ -8,6 +8,7 @@
/* Begin PBXBuildFile section */
94D2C8B92A320E6F00BEE15B /* ReviewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94D2C8B82A320E6F00BEE15B /* ReviewState.swift */; };
+ 94D2C8BD2A32796500BEE15B /* AlbumHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94D2C8BC2A32796500BEE15B /* AlbumHelpers.swift */; };
94D750F02A31A796005859E7 /* BlinkReviewerApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94D750EF2A31A796005859E7 /* BlinkReviewerApp.swift */; };
94D750F22A31A796005859E7 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94D750F12A31A796005859E7 /* ContentView.swift */; };
94D750F42A31A797005859E7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 94D750F32A31A797005859E7 /* Assets.xcassets */; };
@@ -42,6 +43,7 @@
/* Begin PBXFileReference section */
94D2C8B82A320E6F00BEE15B /* ReviewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReviewState.swift; sourceTree = "<group>"; };
+ 94D2C8BC2A32796500BEE15B /* AlbumHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumHelpers.swift; sourceTree = "<group>"; };
94D750EC2A31A796005859E7 /* BlinkReviewer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BlinkReviewer.app; sourceTree = BUILT_PRODUCTS_DIR; };
94D750EF2A31A796005859E7 /* BlinkReviewerApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlinkReviewerApp.swift; sourceTree = "<group>"; };
94D750F12A31A796005859E7 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
@@ -170,6 +172,7 @@
isa = PBXGroup;
children = (
94D7512A2A31D6AC005859E7 /* AssetHelpers.swift */,
+ 94D2C8BC2A32796500BEE15B /* AlbumHelpers.swift */,
);
path = Photos;
sourceTree = "<group>";
@@ -307,6 +310,7 @@
94D7511C2A31A7B1005859E7 /* ThumbnailImage.swift in Sources */,
94D750F22A31A796005859E7 /* ContentView.swift in Sources */,
94D7512B2A31D6AC005859E7 /* AssetHelpers.swift in Sources */,
+ 94D2C8BD2A32796500BEE15B /* AlbumHelpers.swift in Sources */,
94D7511E2A31B243005859E7 /* PreviewImage.swift in Sources */,
94D750F02A31A796005859E7 /* BlinkReviewerApp.swift in Sources */,
94D751202A31B53E005859E7 /* AlbumInfo.swift in Sources */,
BlinkReviewer/BlinkReviewer/Photos/AlbumHelpers.swift (0) → BlinkReviewer/BlinkReviewer/Photos/AlbumHelpers.swift (807)
diff --git a/BlinkReviewer/BlinkReviewer/Photos/AlbumHelpers.swift b/BlinkReviewer/BlinkReviewer/Photos/AlbumHelpers.swift
new file mode 100644
index 0000000..2da334e
--- /dev/null
+++ b/BlinkReviewer/BlinkReviewer/Photos/AlbumHelpers.swift
@@ -0,0 +1,34 @@
+//
+// AlbumHelpers.swift
+// BlinkReviewer
+//
+// Created by Alex Chan on 08/06/2023.
+//
+
+import Foundation
+import Photos
+
+/// Looks up an album by name.
+///
+/// This assumes that album names are globally unique.
+func getAlbum(withName name: String) -> PHAssetCollection {
+ let collections =
+ PHAssetCollection
+ .fetchAssetCollections(with: .album, subtype: .albumRegular, options: nil)
+
+ var thisAssetCollection: PHAssetCollection? = nil
+
+ collections.enumerateObjects({ (album, index, stop) in
+ let assetCollection = album
+
+ if assetCollection.localizedTitle == Optional(name) {
+ thisAssetCollection = assetCollection
+ }
+ })
+
+ if let assetCollection = thisAssetCollection {
+ return assetCollection
+ } else {
+ fatalError("Unable to find album with name: \(name).\n")
+ }
+}
BlinkReviewer/BlinkReviewer/Photos/AssetHelpers.swift (2934) → BlinkReviewer/BlinkReviewer/Photos/AssetHelpers.swift (4726)
diff --git a/BlinkReviewer/BlinkReviewer/Photos/AssetHelpers.swift b/BlinkReviewer/BlinkReviewer/Photos/AssetHelpers.swift
index 97722b5..bb5ea0b 100644
--- a/BlinkReviewer/BlinkReviewer/Photos/AssetHelpers.swift
+++ b/BlinkReviewer/BlinkReviewer/Photos/AssetHelpers.swift
@@ -98,4 +98,53 @@ extension PHAsset {
func getImage() -> NSImage {
return getImageForSize(size: PHImageManagerMaximumSize)
}
+
+ /// Returns true if an asset is in the given album, false otherwise.
+ func isInAlbum(_ album: PHAssetCollection) -> Bool {
+ return albums().contains(where: { collection in
+ collection == album
+ })
+ }
+
+ /// Remove a photo from an album.
+ ///
+ /// This expects to be run inside a performChangesAndWait change block;
+ /// see https://developer.apple.com/documentation/photokit/phphotolibrary/1620747-performchangesandwait.
+ func remove(fromAlbum album: PHAssetCollection) -> Void {
+ let changeAlbum =
+ PHAssetCollectionChangeRequest(for: album)!
+
+ changeAlbum.removeAssets([self] as NSFastEnumeration)
+ }
+
+ /// Add a photo to an album.
+ ///
+ /// This expects to be run inside a performChangesAndWait change block;
+ /// see https://developer.apple.com/documentation/photokit/phphotolibrary/1620747-performchangesandwait.
+ func add(toAlbum album: PHAssetCollection) -> Void {
+ let changeAlbum =
+ PHAssetCollectionChangeRequest(for: album)!
+
+ changeAlbum.addAssets([self] as NSFastEnumeration)
+ }
+
+ /// Toggle a photo's inclusion in an album.
+ ///
+ /// If the photo is already in the album, remove it. If the photo isn't
+ /// in the album, add it.
+ ///
+ /// This expects to be run inside a performChangesAndWait change block;
+ /// see https://developer.apple.com/documentation/photokit/phphotolibrary/1620747-performchangesandwait.
+ func toggle(inAlbum album: PHAssetCollection) -> Void {
+ let changeAlbum =
+ PHAssetCollectionChangeRequest(for: album)!
+
+ let assets = [self] as NSFastEnumeration
+
+ if self.isInAlbum(album) {
+ changeAlbum.removeAssets(assets)
+ } else {
+ changeAlbum.addAssets(assets)
+ }
+ }
}
BlinkReviewer/BlinkReviewer/Views/PhotoReviewer.swift (1162) → BlinkReviewer/BlinkReviewer/Views/PhotoReviewer.swift (3128)
diff --git a/BlinkReviewer/BlinkReviewer/Views/PhotoReviewer.swift b/BlinkReviewer/BlinkReviewer/Views/PhotoReviewer.swift
index c2517f2..db0287a 100644
--- a/BlinkReviewer/BlinkReviewer/Views/PhotoReviewer.swift
+++ b/BlinkReviewer/BlinkReviewer/Views/PhotoReviewer.swift
@@ -27,6 +27,8 @@ struct PhotoReviewer: View {
}
private func handleKeyEvent(_ event: NSEvent) {
+ let asset = assets[selectedAssetIndex]
+
switch event.keyCode {
case 123: // Left arrow key
if selectedAssetIndex > 0 {
@@ -38,6 +40,49 @@ struct PhotoReviewer: View {
selectedAssetIndex += 1
}
+ case 18, 19, 20: // "1", "2", "3"
+ let approved = getAlbum(withName: "Approved")
+ let rejected = getAlbum(withName: "Rejected")
+ let needsAction = getAlbum(withName: "Needs Action")
+
+ let albums = asset.albums()
+
+ let isApproved = albums.contains(approved)
+ let isRejected = albums.contains(rejected)
+ let isNeedsAction = albums.contains(needsAction)
+
+ try! PHPhotoLibrary.shared().performChangesAndWait {
+ // Strictly speaking, the first condition is a combination of two:
+ //
+ // 1. The action is `toggle-approved` and the photo is approved,
+ // in which case toggling means un-approving it.
+ // 2. The action is anything else and the photo is approved, in
+ // which case setting the new status means removing approved.
+ //
+ // Similar logic applies for all three conditions.
+ if isApproved {
+ asset.remove(fromAlbum: approved)
+ } else if event.keyCode == 18 {
+ asset.add(toAlbum: approved)
+ }
+
+ if isRejected {
+ asset.remove(fromAlbum: rejected)
+ } else if event.keyCode == 19 {
+ asset.add(toAlbum: rejected)
+ }
+
+ if isNeedsAction {
+ asset.remove(fromAlbum: needsAction)
+ } else if event.keyCode == 20 {
+ asset.add(toAlbum: needsAction)
+ }
+ }
+
+ if selectedAssetIndex > 0 {
+ selectedAssetIndex -= 1
+ }
+
default:
print(event)
break