feat: You can now share files & text from other apps into SN on iOS (#2358)
This commit is contained in:
@@ -0,0 +1,14 @@
|
|||||||
|
#import <React/RCTBridgeModule.h>
|
||||||
|
#import <Photos/Photos.h>
|
||||||
|
|
||||||
|
|
||||||
|
@interface RCT_EXTERN_MODULE(ReceiveSharingIntent, NSObject)
|
||||||
|
|
||||||
|
RCT_EXTERN_METHOD(getFileNames:(NSString)url
|
||||||
|
resolver:(RCTPromiseResolveBlock)resolve
|
||||||
|
rejecter: (RCTPromiseRejectBlock)reject);
|
||||||
|
|
||||||
|
RCT_EXTERN_METHOD(clearFileNames)
|
||||||
|
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
import Foundation
|
||||||
|
import Photos
|
||||||
|
import MobileCoreServices
|
||||||
|
|
||||||
|
@objc(ReceiveSharingIntent)
|
||||||
|
class ReceiveSharingIntent: NSObject {
|
||||||
|
|
||||||
|
struct Share: Codable {
|
||||||
|
var media: [SharedMediaFile] = []
|
||||||
|
var text: [String] = []
|
||||||
|
var urls: [String] = []
|
||||||
|
}
|
||||||
|
|
||||||
|
private var share = Share()
|
||||||
|
|
||||||
|
@objc
|
||||||
|
func getFileNames(_ url: String,
|
||||||
|
resolver resolve: RCTPromiseResolveBlock,
|
||||||
|
rejecter reject: RCTPromiseRejectBlock
|
||||||
|
) -> Void {
|
||||||
|
let fileUrl = URL(string: url)
|
||||||
|
let json = handleUrl(url: fileUrl);
|
||||||
|
if(json == "error"){
|
||||||
|
let error = NSError(domain: "", code: 400, userInfo: nil)
|
||||||
|
reject("message", "file type is Invalid", error);
|
||||||
|
}else if(json == "invalid group name"){
|
||||||
|
let error = NSError(domain: "", code: 400, userInfo: nil)
|
||||||
|
reject("message", "invalid group name. Please check your share extention bundle name is same as `group.mainbundle name` ", error);
|
||||||
|
}else{
|
||||||
|
resolve(json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleUrl(url: URL?) -> String? {
|
||||||
|
if let url = url {
|
||||||
|
let appDomain = Bundle.main.bundleIdentifier!
|
||||||
|
let userDefaults = UserDefaults(suiteName: "group.\(appDomain)")
|
||||||
|
if let key = url.host?.components(separatedBy: "=").last {
|
||||||
|
if let mediaJson = userDefaults?.object(forKey: "\(key).media") as? Data {
|
||||||
|
let mediaSharedArray = decode(data: mediaJson)
|
||||||
|
let sharedMediaFiles: [SharedMediaFile] = mediaSharedArray.compactMap {
|
||||||
|
guard let path = getAbsolutePath(for: $0.path) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return SharedMediaFile.init(path: path, fileName: fileNameForPath(path: path), mimeType: mimeTypeForPath(path: path))
|
||||||
|
}
|
||||||
|
self.share.media = sharedMediaFiles
|
||||||
|
}
|
||||||
|
if let textSharedArray = userDefaults?.object(forKey: "\(key).text") as? [String] {
|
||||||
|
self.share.text = textSharedArray
|
||||||
|
}
|
||||||
|
if let textSharedArray = userDefaults?.object(forKey: "\(key).url") as? [String] {
|
||||||
|
self.share.urls = textSharedArray
|
||||||
|
}
|
||||||
|
let encodedData = try? JSONEncoder().encode(self.share)
|
||||||
|
let json = String(data: encodedData!, encoding: .utf8)!
|
||||||
|
return json
|
||||||
|
}
|
||||||
|
return "error"
|
||||||
|
}
|
||||||
|
return "invalid group name"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private func getAbsolutePath(for identifier: String) -> String? {
|
||||||
|
if (identifier.starts(with: "file://") || identifier.starts(with: "/var/mobile/Media") || identifier.starts(with: "/private/var/mobile")) {
|
||||||
|
return identifier;
|
||||||
|
}
|
||||||
|
let phAsset = PHAsset.fetchAssets(withLocalIdentifiers: [identifier], options: .none).firstObject
|
||||||
|
if(phAsset == nil) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let (url, _) = getFullSizeImageURLAndOrientation(for: phAsset!)
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
private func getFullSizeImageURLAndOrientation(for asset: PHAsset)-> (String?, Int) {
|
||||||
|
var url: String? = nil
|
||||||
|
var orientation: Int = 0
|
||||||
|
let semaphore = DispatchSemaphore(value: 0)
|
||||||
|
let options2 = PHContentEditingInputRequestOptions()
|
||||||
|
options2.isNetworkAccessAllowed = true
|
||||||
|
asset.requestContentEditingInput(with: options2){(input, info) in
|
||||||
|
orientation = Int(input?.fullSizeImageOrientation ?? 0)
|
||||||
|
url = input?.fullSizeImageURL?.path
|
||||||
|
semaphore.signal()
|
||||||
|
}
|
||||||
|
semaphore.wait()
|
||||||
|
return (url, orientation)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func decode(data: Data) -> [SharedMediaFile] {
|
||||||
|
let encodedData = try? JSONDecoder().decode([SharedMediaFile].self, from: data)
|
||||||
|
return encodedData!
|
||||||
|
}
|
||||||
|
|
||||||
|
private func toJson(data: [SharedMediaFile]?) -> String? {
|
||||||
|
if data == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let encodedData = try? JSONEncoder().encode(data)
|
||||||
|
let json = String(data: encodedData!, encoding: .utf8)!
|
||||||
|
return json
|
||||||
|
}
|
||||||
|
|
||||||
|
class SharedMediaFile: Codable {
|
||||||
|
var path: String;
|
||||||
|
var fileName: String?;
|
||||||
|
var mimeType: String?;
|
||||||
|
|
||||||
|
init(path: String, fileName: String?, mimeType: String?) {
|
||||||
|
self.path = path
|
||||||
|
self.fileName = fileName
|
||||||
|
self.mimeType = mimeType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc
|
||||||
|
func clearFileNames(){
|
||||||
|
print("clearFileNames");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@objc
|
||||||
|
static func requiresMainQueueSetup() -> Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileNameForPath(path: String) -> String? {
|
||||||
|
let url = NSURL(fileURLWithPath: path)
|
||||||
|
return url.lastPathComponent
|
||||||
|
}
|
||||||
|
|
||||||
|
func mimeTypeForPath(path: String) -> String {
|
||||||
|
let url = NSURL(fileURLWithPath: path)
|
||||||
|
let pathExtension = url.pathExtension
|
||||||
|
|
||||||
|
if let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension! as NSString, nil)?.takeRetainedValue() {
|
||||||
|
if let mimetype = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?.takeRetainedValue() {
|
||||||
|
return mimetype as String
|
||||||
|
}
|
||||||
|
return "application/octet-stream"
|
||||||
|
}
|
||||||
|
return "application/octet-stream"
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="j1y-V4-xli">
|
||||||
|
<dependencies>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
|
||||||
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--Share View Controller-->
|
||||||
|
<scene sceneID="ceB-am-kn3">
|
||||||
|
<objects>
|
||||||
|
<viewController id="j1y-V4-xli" customClass="ShareViewController" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" opaque="NO" contentMode="scaleToFill" id="wbc-yd-nQP">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<viewLayoutGuide key="safeArea" id="1Xd-am-t49"/>
|
||||||
|
</view>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="CEy-Cv-SGf" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
</document>
|
||||||
34
packages/mobile/ios/Share To SN/Info.plist
Normal file
34
packages/mobile/ios/Share To SN/Info.plist
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NSExtension</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionAttributes</key>
|
||||||
|
<dict>
|
||||||
|
<key>PHSupportedMediaTypes</key>
|
||||||
|
<array>
|
||||||
|
<string>Video</string>
|
||||||
|
<string>Image</string>
|
||||||
|
</array>
|
||||||
|
<key>NSExtensionActivationRule</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionActivationSupportsText</key>
|
||||||
|
<true/>
|
||||||
|
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
|
||||||
|
<integer>1</integer>
|
||||||
|
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
|
||||||
|
<integer>100</integer>
|
||||||
|
<key>NSExtensionActivationSupportsMovieWithMaxCount</key>
|
||||||
|
<integer>100</integer>
|
||||||
|
<key>NSExtensionActivationSupportsFileWithMaxCount</key>
|
||||||
|
<integer>100</integer>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>NSExtensionMainStoryboard</key>
|
||||||
|
<string>MainInterface</string>
|
||||||
|
<key>NSExtensionPointIdentifier</key>
|
||||||
|
<string>com.apple.share-services</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>group.com.standardnotes.standardnotes</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
336
packages/mobile/ios/Share To SN/ShareViewController.swift
Normal file
336
packages/mobile/ios/Share To SN/ShareViewController.swift
Normal file
@@ -0,0 +1,336 @@
|
|||||||
|
import UIKit
|
||||||
|
import Social
|
||||||
|
import MobileCoreServices
|
||||||
|
import Photos
|
||||||
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
|
let hostAppBundleIdentifier = "com.standardnotes.standardnotes"
|
||||||
|
let shareProtocol = hostAppBundleIdentifier
|
||||||
|
|
||||||
|
class ShareViewController: SLComposeServiceViewController {
|
||||||
|
let sharedKey = "ShareKey"
|
||||||
|
var sharedMedia: [SharedMediaFile] = []
|
||||||
|
var sharedText: [String] = []
|
||||||
|
var sharedURL: [String] = []
|
||||||
|
let imageContentType = UTType.image.identifier
|
||||||
|
let videoContentType = UTType.movie.identifier
|
||||||
|
let textContentType = UTType.text.identifier
|
||||||
|
let urlContentType = UTType.url.identifier
|
||||||
|
let fileURLType = UTType.fileURL.identifier;
|
||||||
|
|
||||||
|
override func isContentValid() -> Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidAppear(_ animated: Bool) {
|
||||||
|
super.viewDidAppear(animated)
|
||||||
|
|
||||||
|
if let content = extensionContext!.inputItems[0] as? NSExtensionItem {
|
||||||
|
if let contents = content.attachments {
|
||||||
|
for (index, attachment) in (contents).enumerated() {
|
||||||
|
if attachment.hasItemConformingToTypeIdentifier(fileURLType) {
|
||||||
|
handleFiles(content: content, attachment: attachment, index: index)
|
||||||
|
} else if attachment.hasItemConformingToTypeIdentifier(imageContentType) {
|
||||||
|
handleImages(content: content, attachment: attachment, index: index)
|
||||||
|
} else if attachment.hasItemConformingToTypeIdentifier(textContentType) {
|
||||||
|
handleText(content: content, attachment: attachment, index: index)
|
||||||
|
} else if attachment.hasItemConformingToTypeIdentifier(urlContentType) {
|
||||||
|
handleUrl(content: content, attachment: attachment, index: index)
|
||||||
|
} else if attachment.hasItemConformingToTypeIdentifier(videoContentType) {
|
||||||
|
handleVideos(content: content, attachment: attachment, index: index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func didSelectPost() {
|
||||||
|
print("didSelectPost");
|
||||||
|
}
|
||||||
|
|
||||||
|
override func configurationItems() -> [Any]! {
|
||||||
|
// To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here.
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleText (content: NSExtensionItem, attachment: NSItemProvider, index: Int) {
|
||||||
|
attachment.loadItem(forTypeIdentifier: textContentType, options: nil) { [weak self] data, error in
|
||||||
|
|
||||||
|
if error == nil, let item = data as? String, let this = self {
|
||||||
|
|
||||||
|
this.sharedText.append(item)
|
||||||
|
|
||||||
|
if index == (content.attachments?.count)! - 1 {
|
||||||
|
this.redirectToHostApp()
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
self?.dismissWithError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleUrl (content: NSExtensionItem, attachment: NSItemProvider, index: Int) {
|
||||||
|
attachment.loadItem(forTypeIdentifier: urlContentType, options: nil) { [weak self] data, error in
|
||||||
|
|
||||||
|
if error == nil, let item = data as? URL, let this = self {
|
||||||
|
|
||||||
|
this.sharedURL.append(item.absoluteString)
|
||||||
|
|
||||||
|
if index == (content.attachments?.count)! - 1 {
|
||||||
|
this.redirectToHostApp()
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
self?.dismissWithError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleImages (content: NSExtensionItem, attachment: NSItemProvider, index: Int) {
|
||||||
|
attachment.loadItem(forTypeIdentifier: imageContentType, options: nil) { [weak self] data, error in
|
||||||
|
|
||||||
|
if error == nil, let url = data as? URL, let this = self {
|
||||||
|
// this.redirectToHostApp(type: .media)
|
||||||
|
// Always copy
|
||||||
|
let fileExtension = this.getExtension(from: url, type: .video)
|
||||||
|
let newName = UUID().uuidString
|
||||||
|
let newPath = FileManager.default
|
||||||
|
.containerURL(forSecurityApplicationGroupIdentifier: "group.\(hostAppBundleIdentifier)")!
|
||||||
|
.appendingPathComponent("\(newName).\(fileExtension)")
|
||||||
|
let copied = this.copyFile(at: url, to: newPath)
|
||||||
|
if(copied) {
|
||||||
|
this.sharedMedia.append(SharedMediaFile(path: newPath.absoluteString, thumbnail: nil, duration: nil, type: .image))
|
||||||
|
}
|
||||||
|
|
||||||
|
if index == (content.attachments?.count)! - 1 {
|
||||||
|
this.redirectToHostApp()
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
self?.dismissWithError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleVideos (content: NSExtensionItem, attachment: NSItemProvider, index: Int) {
|
||||||
|
attachment.loadItem(forTypeIdentifier: videoContentType, options:nil) { [weak self] data, error in
|
||||||
|
|
||||||
|
if error == nil, let url = data as? URL, let this = self {
|
||||||
|
|
||||||
|
// Always copy
|
||||||
|
let fileExtension = this.getExtension(from: url, type: .video)
|
||||||
|
let newName = UUID().uuidString
|
||||||
|
let newPath = FileManager.default
|
||||||
|
.containerURL(forSecurityApplicationGroupIdentifier: "group.\(hostAppBundleIdentifier)")!
|
||||||
|
.appendingPathComponent("\(newName).\(fileExtension)")
|
||||||
|
let copied = this.copyFile(at: url, to: newPath)
|
||||||
|
if(copied) {
|
||||||
|
guard let sharedFile = this.getSharedMediaFile(forVideo: newPath) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.sharedMedia.append(sharedFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
if index == (content.attachments?.count)! - 1 {
|
||||||
|
this.redirectToHostApp()
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
self?.dismissWithError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleFiles (content: NSExtensionItem, attachment: NSItemProvider, index: Int) {
|
||||||
|
attachment.loadItem(forTypeIdentifier: fileURLType, options: nil) { [weak self] data, error in
|
||||||
|
|
||||||
|
if error == nil, let url = data as? URL, let this = self {
|
||||||
|
|
||||||
|
// Always copy
|
||||||
|
let newName = this.getFileName(from :url)
|
||||||
|
let newPath = FileManager.default
|
||||||
|
.containerURL(forSecurityApplicationGroupIdentifier: "group.\(hostAppBundleIdentifier)")!
|
||||||
|
.appendingPathComponent("\(newName)")
|
||||||
|
let copied = this.copyFile(at: url, to: newPath)
|
||||||
|
if (copied) {
|
||||||
|
this.sharedMedia.append(SharedMediaFile(path: newPath.absoluteString, thumbnail: nil, duration: nil, type: .file))
|
||||||
|
}
|
||||||
|
|
||||||
|
if index == (content.attachments?.count)! - 1 {
|
||||||
|
this.redirectToHostApp()
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
self?.dismissWithError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func redirectToHostApp() {
|
||||||
|
let userDefaults = UserDefaults(suiteName: "group.\(hostAppBundleIdentifier)")
|
||||||
|
userDefaults?.set(self.toData(data: self.sharedMedia), forKey: "\(self.sharedKey).media")
|
||||||
|
userDefaults?.set(self.sharedText, forKey: "\(self.sharedKey).text")
|
||||||
|
userDefaults?.set(self.sharedURL, forKey: "\(self.sharedKey).url")
|
||||||
|
userDefaults?.synchronize()
|
||||||
|
|
||||||
|
let url = URL(string: "\(shareProtocol)://dataUrl=\(sharedKey)")
|
||||||
|
var responder = self as UIResponder?
|
||||||
|
let selectorOpenURL = sel_registerName("openURL:")
|
||||||
|
|
||||||
|
while (responder != nil) {
|
||||||
|
if (responder?.responds(to: selectorOpenURL))! {
|
||||||
|
let _ = responder?.perform(selectorOpenURL, with: url)
|
||||||
|
}
|
||||||
|
responder = responder!.next
|
||||||
|
}
|
||||||
|
|
||||||
|
extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func dismissWithError() {
|
||||||
|
print("[ERROR] Error loading data!")
|
||||||
|
let alert = UIAlertController(title: "Error", message: "Error loading data", preferredStyle: .alert)
|
||||||
|
|
||||||
|
let action = UIAlertAction(title: "Error", style: .cancel) { _ in
|
||||||
|
self.dismiss(animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
alert.addAction(action)
|
||||||
|
present(alert, animated: true, completion: nil)
|
||||||
|
extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func alertLog(message: String) {
|
||||||
|
let alert = UIAlertController(title: "Log", message: message, preferredStyle: .alert)
|
||||||
|
|
||||||
|
let action = UIAlertAction(title: "OK", style: .default)
|
||||||
|
|
||||||
|
alert.addAction(action)
|
||||||
|
present(alert, animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum RedirectType {
|
||||||
|
case media
|
||||||
|
case text
|
||||||
|
case file
|
||||||
|
}
|
||||||
|
|
||||||
|
func getExtension(from url: URL, type: SharedMediaType) -> String {
|
||||||
|
let parts = url.lastPathComponent.components(separatedBy: ".")
|
||||||
|
var ex: String? = nil
|
||||||
|
if (parts.count > 1) {
|
||||||
|
ex = parts.last
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ex == nil) {
|
||||||
|
switch type {
|
||||||
|
case .image:
|
||||||
|
ex = "PNG"
|
||||||
|
case .video:
|
||||||
|
ex = "MP4"
|
||||||
|
case .file:
|
||||||
|
ex = "TXT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ex ?? "Unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFileName(from url: URL) -> String {
|
||||||
|
var name = url.lastPathComponent
|
||||||
|
|
||||||
|
if (name == "") {
|
||||||
|
name = UUID().uuidString + "." + getExtension(from: url, type: .file)
|
||||||
|
}
|
||||||
|
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyFile(at srcURL: URL, to dstURL: URL) -> Bool {
|
||||||
|
do {
|
||||||
|
if FileManager.default.fileExists(atPath: dstURL.path) {
|
||||||
|
try FileManager.default.removeItem(at: dstURL)
|
||||||
|
}
|
||||||
|
try FileManager.default.copyItem(at: srcURL, to: dstURL)
|
||||||
|
} catch (let error) {
|
||||||
|
print("Cannot copy item at \(srcURL) to \(dstURL): \(error)")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private func getSharedMediaFile(forVideo: URL) -> SharedMediaFile? {
|
||||||
|
let asset = AVAsset(url: forVideo)
|
||||||
|
let duration = (CMTimeGetSeconds(asset.duration) * 1000).rounded()
|
||||||
|
let thumbnailPath = getThumbnailPath(for: forVideo)
|
||||||
|
|
||||||
|
if FileManager.default.fileExists(atPath: thumbnailPath.path) {
|
||||||
|
return SharedMediaFile(path: forVideo.absoluteString, thumbnail: thumbnailPath.absoluteString, duration: duration, type: .video)
|
||||||
|
}
|
||||||
|
|
||||||
|
var saved = false
|
||||||
|
let assetImgGenerate = AVAssetImageGenerator(asset: asset)
|
||||||
|
assetImgGenerate.appliesPreferredTrackTransform = true
|
||||||
|
// let scale = UIScreen.main.scale
|
||||||
|
assetImgGenerate.maximumSize = CGSize(width: 360, height: 360)
|
||||||
|
do {
|
||||||
|
let img = try assetImgGenerate.copyCGImage(at: CMTimeMakeWithSeconds(600, preferredTimescale: Int32(1.0)), actualTime: nil)
|
||||||
|
try UIImage.pngData(UIImage(cgImage: img))()?.write(to: thumbnailPath)
|
||||||
|
saved = true
|
||||||
|
} catch {
|
||||||
|
saved = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return saved ? SharedMediaFile(path: forVideo.absoluteString, thumbnail: thumbnailPath.absoluteString, duration: duration, type: .video) : nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private func getThumbnailPath(for url: URL) -> URL {
|
||||||
|
let fileName = Data(url.lastPathComponent.utf8).base64EncodedString().replacingOccurrences(of: "==", with: "")
|
||||||
|
let path = FileManager.default
|
||||||
|
.containerURL(forSecurityApplicationGroupIdentifier: "group.\(hostAppBundleIdentifier)")!
|
||||||
|
.appendingPathComponent("\(fileName).jpg")
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
class SharedMediaFile: Codable {
|
||||||
|
var path: String; // can be image, video or url path. It can also be text content
|
||||||
|
var thumbnail: String?; // video thumbnail
|
||||||
|
var duration: Double?; // video duration in milliseconds
|
||||||
|
var type: SharedMediaType;
|
||||||
|
|
||||||
|
|
||||||
|
init(path: String, thumbnail: String?, duration: Double?, type: SharedMediaType) {
|
||||||
|
self.path = path
|
||||||
|
self.thumbnail = thumbnail
|
||||||
|
self.duration = duration
|
||||||
|
self.type = type
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug method to print out SharedMediaFile details in the console
|
||||||
|
func toString() {
|
||||||
|
print("[SharedMediaFile] \n\tpath: \(self.path)\n\tthumbnail: \(self.thumbnail)\n\tduration: \(self.duration)\n\ttype: \(self.type)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SharedMediaType: Int, Codable {
|
||||||
|
case image
|
||||||
|
case video
|
||||||
|
case file
|
||||||
|
}
|
||||||
|
|
||||||
|
func toData(data: [SharedMediaFile]) -> Data {
|
||||||
|
let encodedData = try? JSONEncoder().encode(data)
|
||||||
|
return encodedData!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Array {
|
||||||
|
subscript (safe index: UInt) -> Element? {
|
||||||
|
return Int(index) < count ? self[Int(index)] : nil
|
||||||
|
}
|
||||||
|
}
|
||||||
2
packages/mobile/ios/StandardNotes-Bridging-Header.h
Normal file
2
packages/mobile/ios/StandardNotes-Bridging-Header.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#import <React/RCTBridgeModule.h>
|
||||||
|
#import <React/RCTViewManager.h>
|
||||||
@@ -8,6 +8,11 @@
|
|||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
00E356F31AD99517003FC87E /* StandardNotesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* StandardNotesTests.m */; };
|
00E356F31AD99517003FC87E /* StandardNotesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* StandardNotesTests.m */; };
|
||||||
|
07CAB75E2A618128008FE1EF /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07CAB75D2A618128008FE1EF /* ShareViewController.swift */; };
|
||||||
|
07CAB7612A618128008FE1EF /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 07CAB75F2A618128008FE1EF /* MainInterface.storyboard */; };
|
||||||
|
07CAB7652A618128008FE1EF /* Share To SN.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 07CAB75B2A618128008FE1EF /* Share To SN.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
|
07CAB76E2A618353008FE1EF /* ReceiveSharingIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07CAB76D2A618353008FE1EF /* ReceiveSharingIntent.swift */; };
|
||||||
|
07CAB7702A618385008FE1EF /* ReceiveSharingIntent.m in Sources */ = {isa = PBXBuildFile; fileRef = 07CAB76F2A618385008FE1EF /* ReceiveSharingIntent.m */; };
|
||||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||||
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
||||||
1C2EEB3B45F4EB07AC795C77 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; };
|
1C2EEB3B45F4EB07AC795C77 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; };
|
||||||
@@ -34,13 +39,43 @@
|
|||||||
remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
|
remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
|
||||||
remoteInfo = StandardNotes;
|
remoteInfo = StandardNotes;
|
||||||
};
|
};
|
||||||
|
07CAB7632A618128008FE1EF /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = 07CAB75A2A618128008FE1EF;
|
||||||
|
remoteInfo = "Share To SN";
|
||||||
|
};
|
||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
07CAB7662A618128008FE1EF /* Embed Foundation Extensions */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "";
|
||||||
|
dstSubfolderSpec = 13;
|
||||||
|
files = (
|
||||||
|
07CAB7652A618128008FE1EF /* Share To SN.appex in Embed Foundation Extensions */,
|
||||||
|
);
|
||||||
|
name = "Embed Foundation Extensions";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
00E356EE1AD99517003FC87E /* StandardNotesTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StandardNotesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
00E356EE1AD99517003FC87E /* StandardNotesTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StandardNotesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
00E356F21AD99517003FC87E /* StandardNotesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StandardNotesTests.m; sourceTree = "<group>"; };
|
00E356F21AD99517003FC87E /* StandardNotesTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StandardNotesTests.m; sourceTree = "<group>"; };
|
||||||
04FCB5A3A3387CA3CFC82AA3 /* libPods-StandardNotes-StandardNotesTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-StandardNotes-StandardNotesTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
04FCB5A3A3387CA3CFC82AA3 /* libPods-StandardNotes-StandardNotesTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-StandardNotes-StandardNotesTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
07CAB75B2A618128008FE1EF /* Share To SN.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Share To SN.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
07CAB75D2A618128008FE1EF /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
|
||||||
|
07CAB7602A618128008FE1EF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
|
||||||
|
07CAB7622A618128008FE1EF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
07CAB76A2A6182C7008FE1EF /* StandardNotesDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = StandardNotesDebug.entitlements; path = StandardNotes/StandardNotesDebug.entitlements; sourceTree = "<group>"; };
|
||||||
|
07CAB76B2A6182D6008FE1EF /* Share To SNDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Share To SNDebug.entitlements"; sourceTree = "<group>"; };
|
||||||
|
07CAB76C2A618353008FE1EF /* StandardNotes-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "StandardNotes-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
|
07CAB76D2A618353008FE1EF /* ReceiveSharingIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ReceiveSharingIntent.swift; path = ReceiveSharingIntent/ReceiveSharingIntent.swift; sourceTree = "<group>"; };
|
||||||
|
07CAB76F2A618385008FE1EF /* ReceiveSharingIntent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = ReceiveSharingIntent.m; path = ReceiveSharingIntent/ReceiveSharingIntent.m; sourceTree = "<group>"; };
|
||||||
0BB10C8D896AFACECE748F6D /* Pods-StandardNotesDev.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-StandardNotesDev.release.xcconfig"; path = "Target Support Files/Pods-StandardNotesDev/Pods-StandardNotesDev.release.xcconfig"; sourceTree = "<group>"; };
|
0BB10C8D896AFACECE748F6D /* Pods-StandardNotesDev.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-StandardNotesDev.release.xcconfig"; path = "Target Support Files/Pods-StandardNotesDev/Pods-StandardNotesDev.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
13B07F961A680F5B00A75B9A /* StandardNotes.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = StandardNotes.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
13B07F961A680F5B00A75B9A /* StandardNotes.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = StandardNotes.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = StandardNotes/AppDelegate.h; sourceTree = "<group>"; };
|
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = StandardNotes/AppDelegate.h; sourceTree = "<group>"; };
|
||||||
@@ -75,6 +110,13 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
07CAB7582A618128008FE1EF /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
|
13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@@ -113,6 +155,17 @@
|
|||||||
name = "Supporting Files";
|
name = "Supporting Files";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
07CAB75C2A618128008FE1EF /* Share To SN */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
07CAB76B2A6182D6008FE1EF /* Share To SNDebug.entitlements */,
|
||||||
|
07CAB75D2A618128008FE1EF /* ShareViewController.swift */,
|
||||||
|
07CAB75F2A618128008FE1EF /* MainInterface.storyboard */,
|
||||||
|
07CAB7622A618128008FE1EF /* Info.plist */,
|
||||||
|
);
|
||||||
|
path = "Share To SN";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
0D0B9F98B8FBE9ED9C11F260 /* Pods */ = {
|
0D0B9F98B8FBE9ED9C11F260 /* Pods */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -129,6 +182,7 @@
|
|||||||
13B07FAE1A68108700A75B9A /* StandardNotes */ = {
|
13B07FAE1A68108700A75B9A /* StandardNotes */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
07CAB76A2A6182C7008FE1EF /* StandardNotesDebug.entitlements */,
|
||||||
D261494428699DCE00B17102 /* Web.bundle */,
|
D261494428699DCE00B17102 /* Web.bundle */,
|
||||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
|
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
|
||||||
CDC75795292552080019F4AF /* AppDelegate.mm */,
|
CDC75795292552080019F4AF /* AppDelegate.mm */,
|
||||||
@@ -137,6 +191,9 @@
|
|||||||
13B07FB71A68108700A75B9A /* main.m */,
|
13B07FB71A68108700A75B9A /* main.m */,
|
||||||
CD7D5EC8278005B6005FE1BF /* Info.plist */,
|
CD7D5EC8278005B6005FE1BF /* Info.plist */,
|
||||||
CD7D5EC927800608005FE1BF /* LaunchScreen.storyboard */,
|
CD7D5EC927800608005FE1BF /* LaunchScreen.storyboard */,
|
||||||
|
07CAB76D2A618353008FE1EF /* ReceiveSharingIntent.swift */,
|
||||||
|
07CAB76C2A618353008FE1EF /* StandardNotes-Bridging-Header.h */,
|
||||||
|
07CAB76F2A618385008FE1EF /* ReceiveSharingIntent.m */,
|
||||||
);
|
);
|
||||||
name = StandardNotes;
|
name = StandardNotes;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -166,6 +223,7 @@
|
|||||||
13B07FAE1A68108700A75B9A /* StandardNotes */,
|
13B07FAE1A68108700A75B9A /* StandardNotes */,
|
||||||
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
||||||
00E356EF1AD99517003FC87E /* StandardNotesTests */,
|
00E356EF1AD99517003FC87E /* StandardNotesTests */,
|
||||||
|
07CAB75C2A618128008FE1EF /* Share To SN */,
|
||||||
83CBBA001A601CBA00E9B192 /* Products */,
|
83CBBA001A601CBA00E9B192 /* Products */,
|
||||||
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
||||||
CD7D5EE127801645005FE1BF /* StandardNotesDev-Info.plist */,
|
CD7D5EE127801645005FE1BF /* StandardNotesDev-Info.plist */,
|
||||||
@@ -184,6 +242,7 @@
|
|||||||
13B07F961A680F5B00A75B9A /* StandardNotes.app */,
|
13B07F961A680F5B00A75B9A /* StandardNotes.app */,
|
||||||
00E356EE1AD99517003FC87E /* StandardNotesTests.xctest */,
|
00E356EE1AD99517003FC87E /* StandardNotesTests.xctest */,
|
||||||
CD7D5EDF278015D2005FE1BF /* StandardNotesDev.app */,
|
CD7D5EDF278015D2005FE1BF /* StandardNotesDev.app */,
|
||||||
|
07CAB75B2A618128008FE1EF /* Share To SN.appex */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -212,6 +271,23 @@
|
|||||||
productReference = 00E356EE1AD99517003FC87E /* StandardNotesTests.xctest */;
|
productReference = 00E356EE1AD99517003FC87E /* StandardNotesTests.xctest */;
|
||||||
productType = "com.apple.product-type.bundle.unit-test";
|
productType = "com.apple.product-type.bundle.unit-test";
|
||||||
};
|
};
|
||||||
|
07CAB75A2A618128008FE1EF /* Share To SN */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 07CAB7692A618128008FE1EF /* Build configuration list for PBXNativeTarget "Share To SN" */;
|
||||||
|
buildPhases = (
|
||||||
|
07CAB7572A618128008FE1EF /* Sources */,
|
||||||
|
07CAB7582A618128008FE1EF /* Frameworks */,
|
||||||
|
07CAB7592A618128008FE1EF /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = "Share To SN";
|
||||||
|
productName = "Share To SN";
|
||||||
|
productReference = 07CAB75B2A618128008FE1EF /* Share To SN.appex */;
|
||||||
|
productType = "com.apple.product-type.app-extension";
|
||||||
|
};
|
||||||
13B07F861A680F5B00A75B9A /* StandardNotes */ = {
|
13B07F861A680F5B00A75B9A /* StandardNotes */ = {
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "StandardNotes" */;
|
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "StandardNotes" */;
|
||||||
@@ -224,10 +300,12 @@
|
|||||||
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
|
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
|
||||||
ADF6EC52208176452ADF4217 /* [CP] Embed Pods Frameworks */,
|
ADF6EC52208176452ADF4217 /* [CP] Embed Pods Frameworks */,
|
||||||
BD9D24718C148A593389F498 /* [CP] Copy Pods Resources */,
|
BD9D24718C148A593389F498 /* [CP] Copy Pods Resources */,
|
||||||
|
07CAB7662A618128008FE1EF /* Embed Foundation Extensions */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
|
07CAB7642A618128008FE1EF /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
name = StandardNotes;
|
name = StandardNotes;
|
||||||
productName = StandardNotes;
|
productName = StandardNotes;
|
||||||
@@ -262,14 +340,18 @@
|
|||||||
83CBB9F71A601CBA00E9B192 /* Project object */ = {
|
83CBB9F71A601CBA00E9B192 /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
|
LastSwiftUpdateCheck = 1420;
|
||||||
LastUpgradeCheck = 1210;
|
LastUpgradeCheck = 1210;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
00E356ED1AD99517003FC87E = {
|
00E356ED1AD99517003FC87E = {
|
||||||
CreatedOnToolsVersion = 6.2;
|
CreatedOnToolsVersion = 6.2;
|
||||||
TestTargetID = 13B07F861A680F5B00A75B9A;
|
TestTargetID = 13B07F861A680F5B00A75B9A;
|
||||||
};
|
};
|
||||||
|
07CAB75A2A618128008FE1EF = {
|
||||||
|
CreatedOnToolsVersion = 14.2;
|
||||||
|
};
|
||||||
13B07F861A680F5B00A75B9A = {
|
13B07F861A680F5B00A75B9A = {
|
||||||
LastSwiftMigration = 1120;
|
LastSwiftMigration = 1420;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -289,6 +371,7 @@
|
|||||||
13B07F861A680F5B00A75B9A /* StandardNotes */,
|
13B07F861A680F5B00A75B9A /* StandardNotes */,
|
||||||
00E356ED1AD99517003FC87E /* StandardNotesTests */,
|
00E356ED1AD99517003FC87E /* StandardNotesTests */,
|
||||||
CD7D5ECB278015D2005FE1BF /* StandardNotesDev */,
|
CD7D5ECB278015D2005FE1BF /* StandardNotesDev */,
|
||||||
|
07CAB75A2A618128008FE1EF /* Share To SN */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
@@ -301,6 +384,14 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
07CAB7592A618128008FE1EF /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
07CAB7612A618128008FE1EF /* MainInterface.storyboard in Resources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
13B07F8E1A680F5B00A75B9A /* Resources */ = {
|
13B07F8E1A680F5B00A75B9A /* Resources */ = {
|
||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@@ -570,11 +661,21 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
07CAB7572A618128008FE1EF /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
07CAB75E2A618128008FE1EF /* ShareViewController.swift in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
13B07F871A680F5B00A75B9A /* Sources */ = {
|
13B07F871A680F5B00A75B9A /* Sources */ = {
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
CDC75796292552080019F4AF /* AppDelegate.mm in Sources */,
|
CDC75796292552080019F4AF /* AppDelegate.mm in Sources */,
|
||||||
|
07CAB76E2A618353008FE1EF /* ReceiveSharingIntent.swift in Sources */,
|
||||||
|
07CAB7702A618385008FE1EF /* ReceiveSharingIntent.m in Sources */,
|
||||||
13B07FC11A68108700A75B9A /* main.m in Sources */,
|
13B07FC11A68108700A75B9A /* main.m in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@@ -596,13 +697,30 @@
|
|||||||
target = 13B07F861A680F5B00A75B9A /* StandardNotes */;
|
target = 13B07F861A680F5B00A75B9A /* StandardNotes */;
|
||||||
targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
|
targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
|
||||||
};
|
};
|
||||||
|
07CAB7642A618128008FE1EF /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = 07CAB75A2A618128008FE1EF /* Share To SN */;
|
||||||
|
targetProxy = 07CAB7632A618128008FE1EF /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
/* End PBXTargetDependency section */
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
|
/* Begin PBXVariantGroup section */
|
||||||
|
07CAB75F2A618128008FE1EF /* MainInterface.storyboard */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
07CAB7602A618128008FE1EF /* Base */,
|
||||||
|
);
|
||||||
|
name = MainInterface.storyboard;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXVariantGroup section */
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
00E356F61AD99517003FC87E /* Debug */ = {
|
00E356F61AD99517003FC87E /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 5915B3231CE7C52662278306 /* Pods-StandardNotes-StandardNotesTests.debug.xcconfig */;
|
baseConfigurationReference = 5915B3231CE7C52662278306 /* Pods-StandardNotes-StandardNotesTests.debug.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
"DEBUG=1",
|
"DEBUG=1",
|
||||||
@@ -630,6 +748,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 321F15E603CF0AF8B1769447 /* Pods-StandardNotes-StandardNotesTests.release.xcconfig */;
|
baseConfigurationReference = 321F15E603CF0AF8B1769447 /* Pods-StandardNotes-StandardNotesTests.release.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
INFOPLIST_FILE = StandardNotesTests/Info.plist;
|
INFOPLIST_FILE = StandardNotesTests/Info.plist;
|
||||||
@@ -650,13 +769,93 @@
|
|||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
07CAB7672A618128008FE1EF /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = "Share To SN/Share To SNDebug.entitlements";
|
||||||
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
DEVELOPMENT_TEAM = HKF9BXSN95;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = "Share To SN/Info.plist";
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = "Share To SN";
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 16.2;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@executable_path/../../Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "com.standardnotes.standardnotes.Share-To-SN";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
07CAB7682A618128008FE1EF /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
DEVELOPMENT_TEAM = HKF9BXSN95;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = "Share To SN/Info.plist";
|
||||||
|
INFOPLIST_KEY_CFBundleDisplayName = "Share To SN";
|
||||||
|
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 16.2;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@executable_path/../../Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "com.standardnotes.standardnotes.Share-To-SN";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
13B07F941A680F5B00A75B9A /* Debug */ = {
|
13B07F941A680F5B00A75B9A /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 66417CEB7622E77D89928FCA /* Pods-StandardNotes.debug.xcconfig */;
|
baseConfigurationReference = 66417CEB7622E77D89928FCA /* Pods-StandardNotes.debug.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = Blue;
|
ASSETCATALOG_COMPILER_APPICON_NAME = Blue;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = StandardNotes/StandardNotes.entitlements;
|
CODE_SIGN_ENTITLEMENTS = StandardNotes/StandardNotesDebug.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
@@ -676,6 +875,7 @@
|
|||||||
PRODUCT_BUNDLE_IDENTIFIER = com.standardnotes.standardnotes;
|
PRODUCT_BUNDLE_IDENTIFIER = com.standardnotes.standardnotes;
|
||||||
PRODUCT_NAME = StandardNotes;
|
PRODUCT_NAME = StandardNotes;
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "StandardNotes-Bridging-Header.h";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
@@ -687,6 +887,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 948EE90E15EA48C27577820B /* Pods-StandardNotes.release.xcconfig */;
|
baseConfigurationReference = 948EE90E15EA48C27577820B /* Pods-StandardNotes.release.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = Blue;
|
ASSETCATALOG_COMPILER_APPICON_NAME = Blue;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = StandardNotes/StandardNotes.entitlements;
|
CODE_SIGN_ENTITLEMENTS = StandardNotes/StandardNotes.entitlements;
|
||||||
@@ -710,6 +911,7 @@
|
|||||||
PRODUCT_NAME = StandardNotes;
|
PRODUCT_NAME = StandardNotes;
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.standardnotes.standardnotes";
|
PROVISIONING_PROFILE_SPECIFIER = "match AppStore com.standardnotes.standardnotes";
|
||||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.standardnotes.standardnotes";
|
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.standardnotes.standardnotes";
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "StandardNotes-Bridging-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
VERSIONING_SYSTEM = "apple-generic";
|
VERSIONING_SYSTEM = "apple-generic";
|
||||||
@@ -916,6 +1118,15 @@
|
|||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
|
07CAB7692A618128008FE1EF /* Build configuration list for PBXNativeTarget "Share To SN" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
07CAB7672A618128008FE1EF /* Debug */,
|
||||||
|
07CAB7682A618128008FE1EF /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "StandardNotes" */ = {
|
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "StandardNotes" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
|
|||||||
@@ -1,28 +1,36 @@
|
|||||||
#import "AppDelegate.h"
|
#import "AppDelegate.h"
|
||||||
|
|
||||||
#import <React/RCTBundleURLProvider.h>
|
#import <React/RCTBundleURLProvider.h>
|
||||||
|
#import <React/RCTLinkingManager.h>
|
||||||
#import <WebKit/WKWebsiteDataStore.h>
|
#import <WebKit/WKWebsiteDataStore.h>
|
||||||
#import <TrustKit/TrustKit.h>
|
#import <TrustKit/TrustKit.h>
|
||||||
|
|
||||||
@implementation AppDelegate
|
@implementation AppDelegate
|
||||||
|
|
||||||
|
- (BOOL)application:(UIApplication *)application
|
||||||
|
openURL:(NSURL *)url
|
||||||
|
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
|
||||||
|
{
|
||||||
|
return [RCTLinkingManager application:application openURL:url options:options];
|
||||||
|
}
|
||||||
|
|
||||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||||
{
|
{
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||||
|
|
||||||
[self configurePinning];
|
[self configurePinning];
|
||||||
|
|
||||||
[self disableUrlCache];
|
[self disableUrlCache];
|
||||||
|
|
||||||
[self clearWebEditorCache];
|
[self clearWebEditorCache];
|
||||||
|
|
||||||
NSString *CFBundleIdentifier = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"];
|
NSString *CFBundleIdentifier = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"];
|
||||||
|
|
||||||
NSDictionary * initialProperties = @{@"env" : [CFBundleIdentifier isEqualToString:@"com.standardnotes.standardnotes.dev"] ? @"dev" : @"prod"};
|
NSDictionary * initialProperties = @{@"env" : [CFBundleIdentifier isEqualToString:@"com.standardnotes.standardnotes.dev"] ? @"dev" : @"prod"};
|
||||||
|
|
||||||
self.moduleName = @"StandardNotes";
|
self.moduleName = @"StandardNotes";
|
||||||
self.initialProps = @{};
|
self.initialProps = @{};
|
||||||
|
|
||||||
return [super application:application didFinishLaunchingWithOptions:launchOptions];
|
return [super application:application didFinishLaunchingWithOptions:launchOptions];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,12 +58,12 @@
|
|||||||
if(![currentVersion isEqualToString:lastVersionClear]) {
|
if(![currentVersion isEqualToString:lastVersionClear]) {
|
||||||
// UIWebView
|
// UIWebView
|
||||||
[[NSURLCache sharedURLCache] removeAllCachedResponses];
|
[[NSURLCache sharedURLCache] removeAllCachedResponses];
|
||||||
|
|
||||||
// WebKit
|
// WebKit
|
||||||
NSSet *websiteDataTypes = [WKWebsiteDataStore allWebsiteDataTypes];
|
NSSet *websiteDataTypes = [WKWebsiteDataStore allWebsiteDataTypes];
|
||||||
NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0];
|
NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0];
|
||||||
[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes modifiedSince:dateFrom completionHandler:^{}];
|
[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes modifiedSince:dateFrom completionHandler:^{}];
|
||||||
|
|
||||||
[[NSUserDefaults standardUserDefaults] setObject:currentVersion forKey:lastVersionClearKey];
|
[[NSUserDefaults standardUserDefaults] setObject:currentVersion forKey:lastVersionClearKey];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,17 +73,17 @@
|
|||||||
NSDictionary *trustKitConfig =
|
NSDictionary *trustKitConfig =
|
||||||
@{
|
@{
|
||||||
kTSKSwizzleNetworkDelegates: @YES,
|
kTSKSwizzleNetworkDelegates: @YES,
|
||||||
|
|
||||||
// The list of domains we want to pin and their configuration
|
// The list of domains we want to pin and their configuration
|
||||||
kTSKPinnedDomains: @{
|
kTSKPinnedDomains: @{
|
||||||
@"standardnotes.org" : @{
|
@"standardnotes.org" : @{
|
||||||
kTSKIncludeSubdomains:@YES,
|
kTSKIncludeSubdomains:@YES,
|
||||||
|
|
||||||
kTSKEnforcePinning:@YES,
|
kTSKEnforcePinning:@YES,
|
||||||
|
|
||||||
// Send reports for pin validation failures so we can track them
|
// Send reports for pin validation failures so we can track them
|
||||||
kTSKReportUris: @[@"https://standard.report-uri.com/r/d/hpkp/reportOnly"],
|
kTSKReportUris: @[@"https://standard.report-uri.com/r/d/hpkp/reportOnly"],
|
||||||
|
|
||||||
// The pinned public keys' Subject Public Key Info hashes
|
// The pinned public keys' Subject Public Key Info hashes
|
||||||
kTSKPublicKeyHashes : @[
|
kTSKPublicKeyHashes : @[
|
||||||
@"Vjs8r4z+80wjNcr1YKepWQboSIRi63WsWXhIMN+eWys=",
|
@"Vjs8r4z+80wjNcr1YKepWQboSIRi63WsWXhIMN+eWys=",
|
||||||
@@ -90,12 +98,12 @@
|
|||||||
},
|
},
|
||||||
@"standardnotes.com" : @{
|
@"standardnotes.com" : @{
|
||||||
kTSKIncludeSubdomains:@YES,
|
kTSKIncludeSubdomains:@YES,
|
||||||
|
|
||||||
kTSKEnforcePinning:@YES,
|
kTSKEnforcePinning:@YES,
|
||||||
|
|
||||||
// Send reports for pin validation failures so we can track them
|
// Send reports for pin validation failures so we can track them
|
||||||
kTSKReportUris: @[@"https://standard.report-uri.com/r/d/hpkp/reportOnly"],
|
kTSKReportUris: @[@"https://standard.report-uri.com/r/d/hpkp/reportOnly"],
|
||||||
|
|
||||||
// The pinned public keys' Subject Public Key Info hashes
|
// The pinned public keys' Subject Public Key Info hashes
|
||||||
kTSKPublicKeyHashes : @[
|
kTSKPublicKeyHashes : @[
|
||||||
@"Vjs8r4z+80wjNcr1YKepWQboSIRi63WsWXhIMN+eWys=",
|
@"Vjs8r4z+80wjNcr1YKepWQboSIRi63WsWXhIMN+eWys=",
|
||||||
@@ -110,7 +118,7 @@
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
[TrustKit initSharedInstanceWithConfiguration:trustKitConfig];
|
[TrustKit initSharedInstanceWithConfiguration:trustKitConfig];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,18 @@
|
|||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
|
<key>CFBundleURLTypes</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Editor</string>
|
||||||
|
<key>CFBundleURLSchemes</key>
|
||||||
|
<array>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
<dict/>
|
||||||
|
</array>
|
||||||
<key>LSApplicationQueriesSchemes</key>
|
<key>LSApplicationQueriesSchemes</key>
|
||||||
<array>
|
<array>
|
||||||
<string>http</string>
|
<string>http</string>
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.developer.associated-domains</key>
|
||||||
|
<array>
|
||||||
|
<string>webcredentials:app.standardnotes.com</string>
|
||||||
|
</array>
|
||||||
|
<key>com.apple.developer.default-data-protection</key>
|
||||||
|
<string>NSFileProtectionComplete</string>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>group.com.standardnotes.standardnotes</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ReactNativeToWebEvent } from '@standardnotes/snjs'
|
import { ReactNativeToWebEvent } from '@standardnotes/snjs'
|
||||||
import { RefObject } from 'react'
|
import { RefObject } from 'react'
|
||||||
import { AppState, NativeEventSubscription, NativeModules, Platform } from 'react-native'
|
import { AppState, Linking, NativeEventSubscription, NativeModules, Platform } from 'react-native'
|
||||||
import { readFile } from 'react-native-fs'
|
import { readFile } from 'react-native-fs'
|
||||||
import WebView from 'react-native-webview'
|
import WebView from 'react-native-webview'
|
||||||
const { ReceiveSharingIntent } = NativeModules
|
const { ReceiveSharingIntent } = NativeModules
|
||||||
@@ -13,13 +13,19 @@ type ReceivedItem = {
|
|||||||
text?: string | null
|
text?: string | null
|
||||||
weblink?: string | null
|
weblink?: string | null
|
||||||
subject?: string | null
|
subject?: string | null
|
||||||
|
path?: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReceivedFile = ReceivedItem & {
|
type ReceivedAndroidFile = ReceivedItem & {
|
||||||
contentUri: string
|
contentUri: string
|
||||||
mimeType: string
|
mimeType: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ReceivedIosFile = ReceivedItem & {
|
||||||
|
path: string
|
||||||
|
mimeType: string
|
||||||
|
}
|
||||||
|
|
||||||
type ReceivedWeblink = ReceivedItem & {
|
type ReceivedWeblink = ReceivedItem & {
|
||||||
weblink: string
|
weblink: string
|
||||||
}
|
}
|
||||||
@@ -28,10 +34,14 @@ type ReceivedText = ReceivedItem & {
|
|||||||
text: string
|
text: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const isReceivedFile = (item: ReceivedItem): item is ReceivedFile => {
|
const isReceivedAndroidFile = (item: ReceivedItem): item is ReceivedAndroidFile => {
|
||||||
return !!item.contentUri && !!item.mimeType
|
return !!item.contentUri && !!item.mimeType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isReceivedIosFile = (item: ReceivedItem): item is ReceivedIosFile => {
|
||||||
|
return !!item.path
|
||||||
|
}
|
||||||
|
|
||||||
const isReceivedWeblink = (item: ReceivedItem): item is ReceivedWeblink => {
|
const isReceivedWeblink = (item: ReceivedItem): item is ReceivedWeblink => {
|
||||||
return !!item.weblink
|
return !!item.weblink
|
||||||
}
|
}
|
||||||
@@ -40,15 +50,16 @@ const isReceivedText = (item: ReceivedItem): item is ReceivedText => {
|
|||||||
return !!item.text
|
return !!item.text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const BundleIdentifier = 'com.standardnotes.standardnotes'
|
||||||
|
const IosUrlToCheckFor = `${BundleIdentifier}://dataUrl`
|
||||||
|
|
||||||
export class ReceivedSharedItemsHandler {
|
export class ReceivedSharedItemsHandler {
|
||||||
private appStateEventSub: NativeEventSubscription | null = null
|
private eventSub: NativeEventSubscription | null = null
|
||||||
private receivedItemsQueue: ReceivedItem[] = []
|
private receivedItemsQueue: ReceivedItem[] = []
|
||||||
private isApplicationLaunched = false
|
private isApplicationLaunched = false
|
||||||
|
|
||||||
constructor(private webViewRef: RefObject<WebView>) {
|
constructor(private webViewRef: RefObject<WebView>) {
|
||||||
if (Platform.OS === 'android') {
|
this.registerNativeEventSub()
|
||||||
this.registerNativeEventSub()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsApplicationLaunched = (isApplicationLaunched: boolean) => {
|
setIsApplicationLaunched = (isApplicationLaunched: boolean) => {
|
||||||
@@ -61,23 +72,29 @@ export class ReceivedSharedItemsHandler {
|
|||||||
|
|
||||||
deinit() {
|
deinit() {
|
||||||
this.receivedItemsQueue = []
|
this.receivedItemsQueue = []
|
||||||
this.appStateEventSub?.remove()
|
this.eventSub?.remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerNativeEventSub = () => {
|
private registerNativeEventSub = () => {
|
||||||
this.appStateEventSub = AppState.addEventListener('change', (state) => {
|
if (Platform.OS === 'ios') {
|
||||||
if (state === 'active') {
|
Linking.getInitialURL()
|
||||||
ReceiveSharingIntent.getFileNames()
|
.then((url) => {
|
||||||
.then(async (filesObject: Record<string, ReceivedItem>) => {
|
if (url && url.startsWith(IosUrlToCheckFor)) {
|
||||||
const items = Object.values(filesObject)
|
this.addSharedItemsToQueue(url)
|
||||||
this.receivedItemsQueue.push(...items)
|
}
|
||||||
|
})
|
||||||
|
.catch(console.error)
|
||||||
|
this.eventSub = Linking.addEventListener('url', ({ url }) => {
|
||||||
|
if (url && url.startsWith(IosUrlToCheckFor)) {
|
||||||
|
this.addSharedItemsToQueue(url)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (this.isApplicationLaunched) {
|
this.eventSub = AppState.addEventListener('change', (state) => {
|
||||||
this.handleItemsQueue().catch(console.error)
|
if (state === 'active') {
|
||||||
}
|
this.addSharedItemsToQueue()
|
||||||
})
|
|
||||||
.then(() => ReceiveSharingIntent.clearFileNames())
|
|
||||||
.catch(console.error)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -92,7 +109,7 @@ export class ReceivedSharedItemsHandler {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isReceivedFile(item)) {
|
if (isReceivedAndroidFile(item)) {
|
||||||
const data = await readFile(item.contentUri, 'base64')
|
const data = await readFile(item.contentUri, 'base64')
|
||||||
const file = {
|
const file = {
|
||||||
name: item.fileName || item.contentUri,
|
name: item.fileName || item.contentUri,
|
||||||
@@ -106,6 +123,20 @@ export class ReceivedSharedItemsHandler {
|
|||||||
messageData: file,
|
messageData: file,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
} else if (isReceivedIosFile(item)) {
|
||||||
|
const data = await readFile(item.path, 'base64')
|
||||||
|
const file = {
|
||||||
|
name: item.fileName || item.path,
|
||||||
|
data,
|
||||||
|
mimeType: item.mimeType,
|
||||||
|
}
|
||||||
|
this.webViewRef.current?.postMessage(
|
||||||
|
JSON.stringify({
|
||||||
|
reactNativeEvent: ReactNativeToWebEvent.ReceivedFile,
|
||||||
|
messageType: 'event',
|
||||||
|
messageData: file,
|
||||||
|
}),
|
||||||
|
)
|
||||||
} else if (isReceivedWeblink(item)) {
|
} else if (isReceivedWeblink(item)) {
|
||||||
this.webViewRef.current?.postMessage(
|
this.webViewRef.current?.postMessage(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
@@ -131,4 +162,49 @@ export class ReceivedSharedItemsHandler {
|
|||||||
|
|
||||||
this.handleItemsQueue().catch(console.error)
|
this.handleItemsQueue().catch(console.error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private addSharedItemsToQueue = (url?: string) => {
|
||||||
|
ReceiveSharingIntent.getFileNames(url)
|
||||||
|
.then(async (received: unknown) => {
|
||||||
|
if (!received) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Platform.OS === 'android') {
|
||||||
|
const items = Object.values(received as Record<string, ReceivedItem>)
|
||||||
|
this.receivedItemsQueue.push(...items)
|
||||||
|
} else if (typeof received === 'string') {
|
||||||
|
const parsed: unknown = JSON.parse(received)
|
||||||
|
if (typeof parsed !== 'object') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!parsed) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ('media' in parsed && Array.isArray(parsed.media)) {
|
||||||
|
this.receivedItemsQueue.push(...parsed.media)
|
||||||
|
}
|
||||||
|
if ('text' in parsed && Array.isArray(parsed.text)) {
|
||||||
|
this.receivedItemsQueue.push(
|
||||||
|
...parsed.text.map((text: string) => ({
|
||||||
|
text: text,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if ('urls' in parsed && Array.isArray(parsed.urls)) {
|
||||||
|
this.receivedItemsQueue.push(
|
||||||
|
...parsed.urls.map((url: string) => ({
|
||||||
|
weblink: url,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isApplicationLaunched) {
|
||||||
|
this.handleItemsQueue().catch(console.error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(() => ReceiveSharingIntent.clearFileNames())
|
||||||
|
.catch(console.error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user