mirror of
https://github.com/faroukbmiled/RyukGram.git
synced 2026-08-23 19:32:32 +02:00
feat: Mark as seen on typing
feat: Save to RyukGram album feat: Hide trailing action buttons on preserved messages feat: Confirmation when pulling to refresh the DMs tab if preserved messages would be cleared
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
#import "PhotoAlbum.h"
|
||||
|
||||
@interface SCIPhotoAlbumWatcher : NSObject <PHPhotoLibraryChangeObserver>
|
||||
@property (nonatomic, strong) PHFetchResult<PHAsset *> *baseline;
|
||||
@property (nonatomic, strong) NSTimer *timeoutTimer;
|
||||
@end
|
||||
|
||||
static SCIPhotoAlbumWatcher *sciActiveWatcher = nil;
|
||||
|
||||
@implementation SCIPhotoAlbum
|
||||
|
||||
+ (NSString *)albumName {
|
||||
return @"RyukGram";
|
||||
}
|
||||
|
||||
+ (void)fetchOrCreateAlbumWithCompletion:(void (^)(PHAssetCollection *, NSError *))completion {
|
||||
PHFetchOptions *opts = [[PHFetchOptions alloc] init];
|
||||
opts.predicate = [NSPredicate predicateWithFormat:@"title = %@", [self albumName]];
|
||||
PHFetchResult<PHAssetCollection *> *result = [PHAssetCollection
|
||||
fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum
|
||||
subtype:PHAssetCollectionSubtypeAlbumRegular
|
||||
options:opts];
|
||||
if (result.count > 0) {
|
||||
if (completion) completion(result.firstObject, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
__block NSString *placeholderId = nil;
|
||||
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
|
||||
PHAssetCollectionChangeRequest *req = [PHAssetCollectionChangeRequest
|
||||
creationRequestForAssetCollectionWithTitle:[self albumName]];
|
||||
placeholderId = req.placeholderForCreatedAssetCollection.localIdentifier;
|
||||
} completionHandler:^(BOOL success, NSError *error) {
|
||||
if (!success || !placeholderId) {
|
||||
if (completion) completion(nil, error);
|
||||
return;
|
||||
}
|
||||
PHFetchResult<PHAssetCollection *> *fetched = [PHAssetCollection
|
||||
fetchAssetCollectionsWithLocalIdentifiers:@[placeholderId] options:nil];
|
||||
if (completion) completion(fetched.firstObject, nil);
|
||||
}];
|
||||
}
|
||||
|
||||
+ (void)saveFileToAlbum:(NSURL *)fileURL completion:(void (^)(BOOL, NSError *))completion {
|
||||
[self fetchOrCreateAlbumWithCompletion:^(PHAssetCollection *album, NSError *err) {
|
||||
if (!album) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (completion) completion(NO, err);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
__block NSString *assetId = nil;
|
||||
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
|
||||
NSString *ext = [[fileURL pathExtension] lowercaseString];
|
||||
BOOL isVideo = [@[@"mp4", @"mov", @"m4v"] containsObject:ext];
|
||||
|
||||
PHAssetCreationRequest *req = [PHAssetCreationRequest creationRequestForAsset];
|
||||
PHAssetResourceCreationOptions *opts = [[PHAssetResourceCreationOptions alloc] init];
|
||||
opts.shouldMoveFile = YES;
|
||||
[req addResourceWithType:(isVideo ? PHAssetResourceTypeVideo : PHAssetResourceTypePhoto)
|
||||
fileURL:fileURL options:opts];
|
||||
req.creationDate = [NSDate date];
|
||||
assetId = req.placeholderForCreatedAsset.localIdentifier;
|
||||
|
||||
PHAssetCollectionChangeRequest *albumReq =
|
||||
[PHAssetCollectionChangeRequest changeRequestForAssetCollection:album];
|
||||
[albumReq addAssets:@[req.placeholderForCreatedAsset]];
|
||||
} completionHandler:^(BOOL success, NSError *changeErr) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (completion) completion(success, changeErr);
|
||||
});
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
+ (void)watchForNextSavedAsset {
|
||||
// Replace any existing watcher — only the most recent share sheet matters
|
||||
if (sciActiveWatcher) {
|
||||
[[PHPhotoLibrary sharedPhotoLibrary] unregisterChangeObserver:sciActiveWatcher];
|
||||
[sciActiveWatcher.timeoutTimer invalidate];
|
||||
sciActiveWatcher = nil;
|
||||
}
|
||||
|
||||
if ([PHPhotoLibrary authorizationStatus] != PHAuthorizationStatusAuthorized &&
|
||||
[PHPhotoLibrary authorizationStatus] != PHAuthorizationStatusLimited) {
|
||||
return;
|
||||
}
|
||||
|
||||
SCIPhotoAlbumWatcher *watcher = [[SCIPhotoAlbumWatcher alloc] init];
|
||||
PHFetchOptions *opts = [[PHFetchOptions alloc] init];
|
||||
opts.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO]];
|
||||
watcher.baseline = [PHAsset fetchAssetsWithOptions:opts];
|
||||
[[PHPhotoLibrary sharedPhotoLibrary] registerChangeObserver:watcher];
|
||||
|
||||
// Auto-unregister after 60s in case the user dismisses without saving
|
||||
watcher.timeoutTimer = [NSTimer scheduledTimerWithTimeInterval:60.0
|
||||
repeats:NO
|
||||
block:^(NSTimer *t) {
|
||||
if (sciActiveWatcher == watcher) {
|
||||
[[PHPhotoLibrary sharedPhotoLibrary] unregisterChangeObserver:watcher];
|
||||
sciActiveWatcher = nil;
|
||||
}
|
||||
}];
|
||||
sciActiveWatcher = watcher;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation SCIPhotoAlbumWatcher
|
||||
|
||||
- (void)photoLibraryDidChange:(PHChange *)changeInstance {
|
||||
PHFetchResultChangeDetails *details = [changeInstance changeDetailsForFetchResult:self.baseline];
|
||||
if (!details || details.insertedObjects.count == 0) return;
|
||||
|
||||
NSArray<PHAsset *> *inserted = details.insertedObjects;
|
||||
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
|
||||
[SCIPhotoAlbum fetchOrCreateAlbumWithCompletion:^(PHAssetCollection *album, NSError *err) {}];
|
||||
} completionHandler:^(BOOL success, NSError *error) {
|
||||
// Add the inserted assets to the album in a separate transaction so the
|
||||
// album exists by the time we reference it.
|
||||
[SCIPhotoAlbum fetchOrCreateAlbumWithCompletion:^(PHAssetCollection *album, NSError *err) {
|
||||
if (!album) return;
|
||||
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
|
||||
PHAssetCollectionChangeRequest *req =
|
||||
[PHAssetCollectionChangeRequest changeRequestForAssetCollection:album];
|
||||
[req addAssets:inserted];
|
||||
} completionHandler:nil];
|
||||
}];
|
||||
}];
|
||||
|
||||
// One-shot
|
||||
[[PHPhotoLibrary sharedPhotoLibrary] unregisterChangeObserver:self];
|
||||
[self.timeoutTimer invalidate];
|
||||
self.timeoutTimer = nil;
|
||||
if (sciActiveWatcher == self) sciActiveWatcher = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user