mirror of
https://github.com/samsonjs/samhuri.net-ios.git
synced 2026-03-25 09:25:47 +00:00
use selected text as quote when sharing from Safari
This commit is contained in:
parent
ba08fdcfe4
commit
477a7939ed
8 changed files with 180 additions and 47 deletions
|
|
@ -57,6 +57,9 @@
|
|||
7BE3A0351AE461E700E45CCB /* PreviewViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BE3A0341AE461E700E45CCB /* PreviewViewController.m */; };
|
||||
7BF029331A27117200E42EDE /* ModelStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BF029321A27117200E42EDE /* ModelStore.m */; };
|
||||
7BF029381A280CB200E42EDE /* BlogController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7BF029371A280CB200E42EDE /* BlogController.m */; };
|
||||
7BF960261B0D802900A19A2B /* GrabSelectedText.js in Resources */ = {isa = PBXBuildFile; fileRef = 7BF960251B0D802900A19A2B /* GrabSelectedText.js */; };
|
||||
7BF960271B0E1B1000A19A2B /* SharedContent.m in Sources */ = {isa = PBXBuildFile; fileRef = 1BCFCC904E1195A3DA84D276 /* SharedContent.m */; };
|
||||
7BF960281B0E1B1500A19A2B /* ExtensionItemProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = 1BCFC0549FB7723D8893A91C /* ExtensionItemProcessor.m */; };
|
||||
848D1BC47C9F03BB91A24EB4 /* libPods-samhuri.net.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D351844AAA829A65456E283E /* libPods-samhuri.net.a */; };
|
||||
B8B8958B2AA40812EFE04FEF /* libPods-BlogTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9C36BC848680D4F831E4DE23 /* libPods-BlogTests.a */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
|
@ -94,8 +97,11 @@
|
|||
|
||||
/* Begin PBXFileReference section */
|
||||
1613DC56A86AFA7E50460A37 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
1BCFC0549FB7723D8893A91C /* ExtensionItemProcessor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExtensionItemProcessor.m; sourceTree = "<group>"; };
|
||||
1BCFC23988387A5CAE551C90 /* UIColor+Hex.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIColor+Hex.m"; sourceTree = "<group>"; };
|
||||
1BCFC27DA8A32D0484C273A0 /* ExtensionItemProcessor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExtensionItemProcessor.h; sourceTree = "<group>"; };
|
||||
1BCFC32EC1E5AE196D194D21 /* CommonUI.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CommonUI.m; sourceTree = "<group>"; };
|
||||
1BCFC384864D31C679683404 /* SharedContent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SharedContent.h; sourceTree = "<group>"; };
|
||||
1BCFC3B62AA92DB07923F7C1 /* PostCollection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PostCollection.m; sourceTree = "<group>"; };
|
||||
1BCFC40D1BFCFBF04BC79A35 /* MuseoSans-900.otf */ = {isa = PBXFileReference; lastKnownFileType = file.otf; path = "MuseoSans-900.otf"; sourceTree = "<group>"; };
|
||||
1BCFC462F5540C67B9D97299 /* MuseoSans-700.otf */ = {isa = PBXFileReference; lastKnownFileType = file.otf; path = "MuseoSans-700.otf"; sourceTree = "<group>"; };
|
||||
|
|
@ -106,6 +112,7 @@
|
|||
1BCFCB67571197A762B88624 /* MuseoSans-100-Italic.otf */ = {isa = PBXFileReference; lastKnownFileType = file.otf; path = "MuseoSans-100-Italic.otf"; sourceTree = "<group>"; };
|
||||
1BCFCB89F8C8583AE061757E /* MuseoSans-500.otf */ = {isa = PBXFileReference; lastKnownFileType = file.otf; path = "MuseoSans-500.otf"; sourceTree = "<group>"; };
|
||||
1BCFCC3154DB1D3B3C025211 /* MuseoSans-100.otf */ = {isa = PBXFileReference; lastKnownFileType = file.otf; path = "MuseoSans-100.otf"; sourceTree = "<group>"; };
|
||||
1BCFCC904E1195A3DA84D276 /* SharedContent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SharedContent.m; sourceTree = "<group>"; };
|
||||
1BCFCCF30594E0E2DCC32116 /* PostCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PostCell.h; sourceTree = "<group>"; };
|
||||
1BCFCD0E9504E1E8AB8C0275 /* MuseoSans-300-Italic.otf */ = {isa = PBXFileReference; lastKnownFileType = file.otf; path = "MuseoSans-300-Italic.otf"; sourceTree = "<group>"; };
|
||||
1BCFCDD73D4AE8F16A9C9E3D /* ChangeTitleViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ChangeTitleViewController.h; sourceTree = "<group>"; };
|
||||
|
|
@ -162,6 +169,7 @@
|
|||
7BF029321A27117200E42EDE /* ModelStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ModelStore.m; sourceTree = "<group>"; };
|
||||
7BF029361A280CB200E42EDE /* BlogController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlogController.h; sourceTree = "<group>"; };
|
||||
7BF029371A280CB200E42EDE /* BlogController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BlogController.m; sourceTree = "<group>"; };
|
||||
7BF960251B0D802900A19A2B /* GrabSelectedText.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = GrabSelectedText.js; sourceTree = "<group>"; };
|
||||
9C36BC848680D4F831E4DE23 /* libPods-BlogTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-BlogTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
A12E4260DF3BBC175C358446 /* Pods-samhuri.net.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-samhuri.net.debug.xcconfig"; path = "Pods/Target Support Files/Pods-samhuri.net/Pods-samhuri.net.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
A2EB178BEF4356711B2710AE /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
|
||||
|
|
@ -236,10 +244,15 @@
|
|||
7B08B3E81AF9CAD300435579 /* samhuri.net */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7B08B3EE1AF9CAD300435579 /* MainInterface.storyboard */,
|
||||
7B08B3EB1AF9CAD300435579 /* ShareViewController.h */,
|
||||
7B08B3EC1AF9CAD300435579 /* ShareViewController.m */,
|
||||
7B08B3EE1AF9CAD300435579 /* MainInterface.storyboard */,
|
||||
7BF960251B0D802900A19A2B /* GrabSelectedText.js */,
|
||||
7B08B3E91AF9CAD300435579 /* Supporting Files */,
|
||||
1BCFCC904E1195A3DA84D276 /* SharedContent.m */,
|
||||
1BCFC384864D31C679683404 /* SharedContent.h */,
|
||||
1BCFC0549FB7723D8893A91C /* ExtensionItemProcessor.m */,
|
||||
1BCFC27DA8A32D0484C273A0 /* ExtensionItemProcessor.h */,
|
||||
);
|
||||
path = samhuri.net;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -500,6 +513,7 @@
|
|||
files = (
|
||||
7B08B3EF1AF9CAD300435579 /* MainInterface.storyboard in Resources */,
|
||||
7B010BAF1AFB3EA900351D18 /* auth.json in Resources */,
|
||||
7BF960261B0D802900A19A2B /* GrabSelectedText.js in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -639,9 +653,11 @@
|
|||
7B08B3F71AF9CBBD00435579 /* NSDate+marshmallows.m in Sources */,
|
||||
7B08B3FB1AF9CBC500435579 /* BlogService.m in Sources */,
|
||||
7B08B3FD1AF9CBC500435579 /* ModelStore.m in Sources */,
|
||||
7BF960281B0E1B1500A19A2B /* ExtensionItemProcessor.m in Sources */,
|
||||
7B08B4021AF9D3EA00435579 /* SamhuriNet.m in Sources */,
|
||||
7B08B3ED1AF9CAD300435579 /* ShareViewController.m in Sources */,
|
||||
7B08B3F91AF9CBC500435579 /* BlogStatus.m in Sources */,
|
||||
7BF960271B0E1B1000A19A2B /* SharedContent.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
|||
14
samhuri.net/ExtensionItemProcessor.h
Normal file
14
samhuri.net/ExtensionItemProcessor.h
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
//
|
||||
// Created by Sami Samhuri on 15-05-20.
|
||||
// Copyright (c) 2015 Guru Logic Inc. All rights reserved.
|
||||
//
|
||||
@import Foundation;
|
||||
|
||||
@class PMKPromise;
|
||||
|
||||
@interface ExtensionItemProcessor : NSObject
|
||||
|
||||
- (PMKPromise *)sharedContentForPListItem:(NSExtensionItem *)item;
|
||||
- (PMKPromise *)sharedContentForURLItem:(NSExtensionItem *)item text:(NSString *)text;
|
||||
|
||||
@end
|
||||
54
samhuri.net/ExtensionItemProcessor.m
Normal file
54
samhuri.net/ExtensionItemProcessor.m
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// Created by Sami Samhuri on 15-05-20.
|
||||
// Copyright (c) 2015 Guru Logic Inc. All rights reserved.
|
||||
//
|
||||
#import <PromiseKit/Promise.h>
|
||||
#import <MobileCoreServices/MobileCoreServices.h>
|
||||
#import "ExtensionItemProcessor.h"
|
||||
#import "SharedContent.h"
|
||||
#import "NSString+marshmallows.h"
|
||||
|
||||
@implementation ExtensionItemProcessor
|
||||
|
||||
- (PMKPromise *)sharedContentForPListItem:(NSExtensionItem *)item {
|
||||
return [PMKPromise new:^(PMKFulfiller fulfill, PMKRejecter reject) {
|
||||
NSItemProvider *provider = [self providerForIdentifier:(NSString *)kUTTypePropertyList fromExtensionItem:item];
|
||||
if (!provider) {
|
||||
reject([NSError errorWithDomain:@"ShareViewControllerDomain" code:1 userInfo:@{NSLocalizedDescriptionKey: @"Cannot find PList provider"}]);
|
||||
return;
|
||||
}
|
||||
[provider loadItemForTypeIdentifier:(NSString *)kUTTypePropertyList options:nil completionHandler:^(NSDictionary *stuff, NSError *error) {
|
||||
NSDictionary *results = stuff[NSExtensionJavaScriptPreprocessingResultsKey];
|
||||
NSURL *url = [NSURL URLWithString:results[@"url"]];
|
||||
NSString *quotedText = [results[@"selectedText"] mm_stringByTrimmingWhitespace];
|
||||
NSString *quote = quotedText.length ? @"> " : @"";
|
||||
NSString *text = [NSString stringWithFormat:@"%@\n\n%@%@", results[@"title"], quote, quotedText];
|
||||
fulfill([SharedContent contentWithURL:url text:text]);
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (PMKPromise *)sharedContentForURLItem:(NSExtensionItem *)item text:(NSString *)text {
|
||||
return [PMKPromise new:^(PMKFulfiller fulfill, PMKRejecter reject) {
|
||||
NSItemProvider *provider = [self providerForIdentifier:(NSString *)kUTTypeURL fromExtensionItem:item];
|
||||
if (!provider) {
|
||||
reject([NSError errorWithDomain:@"ShareViewControllerDomain" code:1 userInfo:@{NSLocalizedDescriptionKey: @"Cannot find URL provider"}]);
|
||||
return;
|
||||
}
|
||||
[provider loadItemForTypeIdentifier:(NSString *)kUTTypeURL options:nil completionHandler:^(NSURL *url, NSError *error) {
|
||||
// TODO: fetch title?
|
||||
fulfill([SharedContent contentWithURL:url text:text]);
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSItemProvider *)providerForIdentifier:(NSString *)identifier fromExtensionItem:(NSExtensionItem *)item {
|
||||
for (NSItemProvider *provider in item.attachments) {
|
||||
if ([provider hasItemConformingToTypeIdentifier:identifier]) {
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
11
samhuri.net/GrabSelectedText.js
Normal file
11
samhuri.net/GrabSelectedText.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
var SelectedTextPreprocessor = function() {};
|
||||
SelectedTextPreprocessor.prototype = {
|
||||
run: function(args) {
|
||||
args.completionFunction({
|
||||
"url": document.URL,
|
||||
"title": document.title,
|
||||
"selectedText": window.getSelection().toString()
|
||||
});
|
||||
}
|
||||
};
|
||||
window.ExtensionPreprocessingJS = new SelectedTextPreprocessor();
|
||||
|
|
@ -26,6 +26,8 @@
|
|||
<dict>
|
||||
<key>NSExtensionAttributes</key>
|
||||
<dict>
|
||||
<key>NSExtensionJavaScriptPreprocessingFile</key>
|
||||
<string>GrabSelectedText</string>
|
||||
<key>NSExtensionActivationRule</key>
|
||||
<dict>
|
||||
<key>NSExtensionActivationSupportsText</key>
|
||||
|
|
|
|||
|
|
@ -6,24 +6,48 @@
|
|||
// Copyright (c) 2015 Guru Logic Inc. All rights reserved.
|
||||
//
|
||||
#import <PromiseKit/PromiseKit.h>
|
||||
#import <MobileCoreServices/MobileCoreServices.h>
|
||||
#import "ShareViewController.h"
|
||||
#import "SamhuriNet.h"
|
||||
#import "BlogController.h"
|
||||
#import "Post.h"
|
||||
#import "SharedContent.h"
|
||||
#import "ExtensionItemProcessor.h"
|
||||
|
||||
@interface ShareViewController ()
|
||||
|
||||
@property (nonatomic, readonly, strong) NSItemProvider *URLProvider;
|
||||
@property (nonatomic, assign) BOOL checkedForURLProvider;
|
||||
|
||||
@property(nonatomic, strong) SharedContent *content;
|
||||
@end
|
||||
|
||||
@implementation ShareViewController
|
||||
|
||||
@synthesize URLProvider = _URLProvider;
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
[self findSharedContent].then(^(SharedContent *content) {
|
||||
self.textView.text = content.text;
|
||||
self.content = content;
|
||||
}).catch(^(NSError *error) {
|
||||
[self displayError:error];
|
||||
[self.extensionContext completeRequestReturningItems:@[] completionHandler:nil];
|
||||
});
|
||||
}
|
||||
|
||||
- (PMKPromise *)findSharedContent {
|
||||
for (NSExtensionItem *item in self.extensionContext.inputItems) {
|
||||
for (NSItemProvider *provider in item.attachments) {
|
||||
if ([provider hasItemConformingToTypeIdentifier:(NSString *)kUTTypePropertyList]) {
|
||||
return [[ExtensionItemProcessor new] sharedContentForPListItem:item];
|
||||
}
|
||||
if ([provider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeURL]) {
|
||||
return [[ExtensionItemProcessor new] sharedContentForURLItem:item text:self.contentText];
|
||||
}
|
||||
}
|
||||
}
|
||||
NSDictionary *info = @{NSLocalizedDescriptionKey: @"Cannot find PList or URL extension item to share."};
|
||||
return [PMKPromise promiseWithValue:[NSError errorWithDomain:@"SharedViewControllerDomain" code:1 userInfo:info]];
|
||||
}
|
||||
|
||||
- (BOOL)isContentValid {
|
||||
return self.URLProvider != nil;
|
||||
return self.textView.text.length > 0;
|
||||
}
|
||||
|
||||
- (UIView *)loadPreviewView {
|
||||
|
|
@ -38,48 +62,14 @@
|
|||
NSRange titleEndRange = [self.contentText rangeOfString:@"\n\n"];
|
||||
NSString *title = titleEndRange.location == NSNotFound ? self.contentText : [self.contentText substringToIndex:titleEndRange.location];
|
||||
NSString *body = titleEndRange.location == NSNotFound ? @"" : [self.contentText substringFromIndex:titleEndRange.location + titleEndRange.length];
|
||||
NSLog(@"title = %@", title);
|
||||
NSLog(@"body = %@", body);
|
||||
NSItemProvider *urlProvider = [self firstURLProvider];
|
||||
BOOL reallyPost = YES;
|
||||
[urlProvider loadItemForTypeIdentifier:@"public.url" options:nil completionHandler:^(NSURL *url, NSError *error) {
|
||||
// TODO: image
|
||||
NSLog(@"url = %@", url);
|
||||
if (reallyPost) {
|
||||
Post *post = [Post newDraftWithTitle:title body:body url:url];
|
||||
[blogController requestCreateDraft:post publishImmediatelyToEnvironment:@"production" waitForCompilation:NO].catch(^(NSError *error) {
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Error" message:error.localizedDescription preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"Dismiss" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}]];
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
});
|
||||
}
|
||||
[self.extensionContext completeRequestReturningItems:@[] completionHandler:nil];
|
||||
}];
|
||||
}
|
||||
|
||||
- (NSItemProvider *)firstURLProvider {
|
||||
NSExtensionItem *item = self.extensionContext.inputItems.firstObject;
|
||||
NSLog(@"item = %@", item);
|
||||
for (NSItemProvider *provider in item.attachments) {
|
||||
NSLog(@"provider = %@", provider);
|
||||
if ([provider hasItemConformingToTypeIdentifier:@"public.url"]) {
|
||||
return provider;
|
||||
}
|
||||
if (reallyPost) {
|
||||
Post *post = [Post newDraftWithTitle:title body:body url:self.content.url];
|
||||
[blogController requestCreateDraft:post publishImmediatelyToEnvironment:@"production" waitForCompilation:NO].catch(^(NSError *error) {
|
||||
[self displayError:error];
|
||||
});
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSItemProvider *)URLProvider {
|
||||
if (!self.checkedForURLProvider) {
|
||||
_URLProvider = [self firstURLProvider];
|
||||
if (!_URLProvider) {
|
||||
NSLog(@"ERROR: No URL provider found");
|
||||
}
|
||||
self.checkedForURLProvider = YES;
|
||||
}
|
||||
return _URLProvider;
|
||||
[self.extensionContext completeRequestReturningItems:@[] completionHandler:nil];
|
||||
}
|
||||
|
||||
- (NSArray *)configurationItems {
|
||||
|
|
@ -87,4 +77,12 @@
|
|||
return @[];
|
||||
}
|
||||
|
||||
- (void)displayError:(NSError *)error {
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Error" message:error.localizedDescription preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"Dismiss" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}]];
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
|||
16
samhuri.net/SharedContent.h
Normal file
16
samhuri.net/SharedContent.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// Created by Sami Samhuri on 15-05-20.
|
||||
// Copyright (c) 2015 Guru Logic Inc. All rights reserved.
|
||||
//
|
||||
@import Foundation;
|
||||
|
||||
@interface SharedContent : NSObject
|
||||
|
||||
@property(nonatomic, readonly, copy) NSURL *url;
|
||||
@property(nonatomic, readonly, copy) NSString *text;
|
||||
|
||||
+ (instancetype)contentWithURL:(NSURL *)url text:(NSString *)text;
|
||||
- (instancetype)initWithURL:(NSURL *)url contentText:(NSString *)text;
|
||||
|
||||
|
||||
@end
|
||||
22
samhuri.net/SharedContent.m
Normal file
22
samhuri.net/SharedContent.m
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
//
|
||||
// Created by Sami Samhuri on 15-05-20.
|
||||
// Copyright (c) 2015 Guru Logic Inc. All rights reserved.
|
||||
//
|
||||
#import "SharedContent.h"
|
||||
|
||||
@implementation SharedContent
|
||||
|
||||
+ (instancetype)contentWithURL:(NSURL *)url text:(NSString *)text {
|
||||
return [[self alloc] initWithURL:url contentText:text];
|
||||
}
|
||||
|
||||
- (instancetype)initWithURL:(NSURL *)url contentText:(NSString *)text {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_url = [url copy];
|
||||
_text = [text copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
Loading…
Reference in a new issue