Share sheet extension

This commit is contained in:
Thomas Ricouard 2023-01-15 16:39:08 +01:00
parent 4fc6944360
commit 596f920603
12 changed files with 416 additions and 22 deletions

View file

@ -43,6 +43,12 @@
9F7335F22967608F00AFF0BA /* AddRemoteTimelineVIew.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F7335F12967608F00AFF0BA /* AddRemoteTimelineVIew.swift */; };
9F7335F92968576500AFF0BA /* DisplaySettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F7335F82968576500AFF0BA /* DisplaySettingsView.swift */; };
9FAD85832971BF7200496AB1 /* Secret.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9FAD85822971BF7200496AB1 /* Secret.plist */; };
9FAD858B29743F7400496AB1 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FAD858A29743F7400496AB1 /* ShareViewController.swift */; };
9FAD858E29743F7400496AB1 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9FAD858C29743F7400496AB1 /* MainInterface.storyboard */; };
9FAD859229743F7400496AB1 /* IceCubesShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 9FAD858829743F7400496AB1 /* IceCubesShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
9FAD85982974405D00496AB1 /* Status in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAD85972974405D00496AB1 /* Status */; };
9FAD859A297440CB00496AB1 /* KeychainSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAD8599297440CB00496AB1 /* KeychainSwift */; };
9FAD859C2974422700496AB1 /* AppAccount in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAD859B2974422700496AB1 /* AppAccount */; };
9FAE4ACB293783B000772766 /* SettingsTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FAE4ACA293783B000772766 /* SettingsTab.swift */; };
9FAE4ACE29379A5A00772766 /* KeychainSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 9FAE4ACD29379A5A00772766 /* KeychainSwift */; };
9FBFE63D292A715500C250E9 /* IceCubesApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FBFE63C292A715500C250E9 /* IceCubesApp.swift */; };
@ -61,6 +67,13 @@
remoteGlobalIDString = 9F2A5415296AB631009B2D7C;
remoteInfo = IceCubesNotifications;
};
9FAD859029743F7400496AB1 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 9FBFE631292A715500C250E9 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 9FAD858729743F7400496AB1;
remoteInfo = IceCubesShareExtension;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
@ -71,6 +84,7 @@
dstSubfolderSpec = 13;
files = (
9F2A541D296AB631009B2D7C /* IceCubesNotifications.appex in Embed Foundation Extensions */,
9FAD859229743F7400496AB1 /* IceCubesShareExtension.appex in Embed Foundation Extensions */,
);
name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0;
@ -114,6 +128,11 @@
9F7335F12967608F00AFF0BA /* AddRemoteTimelineVIew.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddRemoteTimelineVIew.swift; sourceTree = "<group>"; };
9F7335F82968576500AFF0BA /* DisplaySettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplaySettingsView.swift; sourceTree = "<group>"; };
9FAD85822971BF7200496AB1 /* Secret.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Secret.plist; sourceTree = "<group>"; };
9FAD858829743F7400496AB1 /* IceCubesShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = IceCubesShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
9FAD858A29743F7400496AB1 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
9FAD858D29743F7400496AB1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
9FAD858F29743F7400496AB1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
9FAD859629743F7E00496AB1 /* IceCubesShareExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = IceCubesShareExtension.entitlements; sourceTree = "<group>"; };
9FAE4AC8293774FF00772766 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
9FAE4ACA293783B000772766 /* SettingsTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTab.swift; sourceTree = "<group>"; };
9FBFE639292A715500C250E9 /* IceCubesApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = IceCubesApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
@ -136,6 +155,16 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
9FAD858529743F7400496AB1 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
9FAD859C2974422700496AB1 /* AppAccount in Frameworks */,
9FAD859A297440CB00496AB1 /* KeychainSwift in Frameworks */,
9FAD85982974405D00496AB1 /* Status in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
9FBFE636292A715500C250E9 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@ -204,6 +233,17 @@
path = Timeline;
sourceTree = "<group>";
};
9FAD858929743F7400496AB1 /* IceCubesShareExtension */ = {
isa = PBXGroup;
children = (
9FAD859629743F7E00496AB1 /* IceCubesShareExtension.entitlements */,
9FAD858A29743F7400496AB1 /* ShareViewController.swift */,
9FAD858C29743F7400496AB1 /* MainInterface.storyboard */,
9FAD858F29743F7400496AB1 /* Info.plist */,
);
path = IceCubesShareExtension;
sourceTree = "<group>";
};
9FAE4AC9293783A200772766 /* Tabs */ = {
isa = PBXGroup;
children = (
@ -222,6 +262,7 @@
children = (
9FBFE63B292A715500C250E9 /* IceCubesApp */,
9F2A5417296AB631009B2D7C /* IceCubesNotifications */,
9FAD858929743F7400496AB1 /* IceCubesShareExtension */,
9FBFE63A292A715500C250E9 /* Products */,
9FBFE64C292A72BD00C250E9 /* Frameworks */,
9FE3DB55296FEF5800628CB0 /* AppAccount */,
@ -244,6 +285,7 @@
children = (
9FBFE639292A715500C250E9 /* IceCubesApp.app */,
9F2A5416296AB631009B2D7C /* IceCubesNotifications.appex */,
9FAD858829743F7400496AB1 /* IceCubesShareExtension.appex */,
);
name = Products;
sourceTree = "<group>";
@ -311,6 +353,28 @@
productReference = 9F2A5416296AB631009B2D7C /* IceCubesNotifications.appex */;
productType = "com.apple.product-type.app-extension";
};
9FAD858729743F7400496AB1 /* IceCubesShareExtension */ = {
isa = PBXNativeTarget;
buildConfigurationList = 9FAD859329743F7400496AB1 /* Build configuration list for PBXNativeTarget "IceCubesShareExtension" */;
buildPhases = (
9FAD858429743F7400496AB1 /* Sources */,
9FAD858529743F7400496AB1 /* Frameworks */,
9FAD858629743F7400496AB1 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = IceCubesShareExtension;
packageProductDependencies = (
9FAD85972974405D00496AB1 /* Status */,
9FAD8599297440CB00496AB1 /* KeychainSwift */,
9FAD859B2974422700496AB1 /* AppAccount */,
);
productName = IceCubesShareExtension;
productReference = 9FAD858829743F7400496AB1 /* IceCubesShareExtension.appex */;
productType = "com.apple.product-type.app-extension";
};
9FBFE638292A715500C250E9 /* IceCubesApp */ = {
isa = PBXNativeTarget;
buildConfigurationList = 9FBFE648292A715600C250E9 /* Build configuration list for PBXNativeTarget "IceCubesApp" */;
@ -324,6 +388,7 @@
);
dependencies = (
9F2A541C296AB631009B2D7C /* PBXTargetDependency */,
9FAD859129743F7400496AB1 /* PBXTargetDependency */,
);
name = IceCubesApp;
packageProductDependencies = (
@ -359,6 +424,9 @@
9F2A5415296AB631009B2D7C = {
CreatedOnToolsVersion = 14.2;
};
9FAD858729743F7400496AB1 = {
CreatedOnToolsVersion = 14.2;
};
9FBFE638292A715500C250E9 = {
CreatedOnToolsVersion = 14.1;
};
@ -383,6 +451,7 @@
targets = (
9FBFE638292A715500C250E9 /* IceCubesApp */,
9F2A5415296AB631009B2D7C /* IceCubesNotifications */,
9FAD858729743F7400496AB1 /* IceCubesShareExtension */,
);
};
/* End PBXProject section */
@ -395,6 +464,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
9FAD858629743F7400496AB1 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9FAD858E29743F7400496AB1 /* MainInterface.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
9FBFE637292A715500C250E9 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@ -419,6 +496,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
9FAD858429743F7400496AB1 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9FAD858B29743F7400496AB1 /* ShareViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
9FBFE635292A715500C250E9 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@ -450,8 +535,24 @@
target = 9F2A5415296AB631009B2D7C /* IceCubesNotifications */;
targetProxy = 9F2A541B296AB631009B2D7C /* PBXContainerItemProxy */;
};
9FAD859129743F7400496AB1 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 9FAD858729743F7400496AB1 /* IceCubesShareExtension */;
targetProxy = 9FAD859029743F7400496AB1 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
9FAD858C29743F7400496AB1 /* MainInterface.storyboard */ = {
isa = PBXVariantGroup;
children = (
9FAD858D29743F7400496AB1 /* Base */,
);
name = MainInterface.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
9F2A541F296AB631009B2D7C /* Debug */ = {
isa = XCBuildConfiguration;
@ -514,6 +615,67 @@
};
name = Release;
};
9FAD859429743F7400496AB1 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = IceCubesShareExtension/IceCubesShareExtension.entitlements;
CODE_SIGN_IDENTITY = "-";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = Z6P74P6T99;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = IceCubesShareExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = IceCubesShareExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 16.1;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.IceCubesApp.IceCubesShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
9FAD859529743F7400496AB1 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = IceCubesShareExtension/IceCubesShareExtension.entitlements;
CODE_SIGN_IDENTITY = "-";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = Z6P74P6T99;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = IceCubesShareExtension/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = IceCubesShareExtension;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 16.1;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.IceCubesApp.IceCubesShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
9FBFE646292A715600C250E9 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -739,6 +901,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
9FAD859329743F7400496AB1 /* Build configuration list for PBXNativeTarget "IceCubesShareExtension" */ = {
isa = XCConfigurationList;
buildConfigurations = (
9FAD859429743F7400496AB1 /* Debug */,
9FAD859529743F7400496AB1 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
9FBFE634292A715500C250E9 /* Build configuration list for PBXProject "IceCubesApp" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@ -834,6 +1005,19 @@
isa = XCSwiftPackageProductDependency;
productName = Conversations;
};
9FAD85972974405D00496AB1 /* Status */ = {
isa = XCSwiftPackageProductDependency;
productName = Status;
};
9FAD8599297440CB00496AB1 /* KeychainSwift */ = {
isa = XCSwiftPackageProductDependency;
package = 9FAE4ACC29379A5A00772766 /* XCRemoteSwiftPackageReference "keychain-swift" */;
productName = KeychainSwift;
};
9FAD859B2974422700496AB1 /* AppAccount */ = {
isa = XCSwiftPackageProductDependency;
productName = AppAccount;
};
9FAE4ACD29379A5A00772766 /* KeychainSwift */ = {
isa = XCSwiftPackageProductDependency;
package = 9FAE4ACC29379A5A00772766 /* XCRemoteSwiftPackageReference "keychain-swift" */;

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="j1y-V4-xli">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
<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" customModule="IceCubesShareExtension" 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="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<viewLayoutGuide key="safeArea" id="1Xd-am-t49"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</view>
<connections>
<outlet property="container" destination="wbc-yd-nQP" id="olf-8h-43o"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="CEy-Cv-SGf" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="102" y="-2"/>
</scene>
</scenes>
</document>

View file

@ -0,0 +1,14 @@
<?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.icecubesapps</string>
</array>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)com.thomasricouard.IceCubesApp</string>
</array>
</dict>
</plist>

View file

@ -0,0 +1,18 @@
<?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>NSExtensionActivationRule</key>
<string>TRUEPREDICATE</string>
</dict>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.share-services</string>
</dict>
</dict>
</plist>

View file

@ -0,0 +1,57 @@
import SwiftUI
import UIKit
import Status
import DesignSystem
import Account
import Network
import Env
import AppAccount
class ShareViewController: UIViewController {
@IBOutlet var container: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let client = AppAccountsManager.shared.currentClient
let account = CurrentAccount()
let instance = CurrentInstance()
account.setClient(client: client)
instance.setClient(client: client)
let theme = Theme()
overrideUserInterfaceStyle = theme.selectedScheme == .dark ? .dark : .light
if let item = extensionContext?.inputItems.first as? NSExtensionItem {
if let attachments = item.attachments {
let view = StatusEditorView(mode: .shareExtension(items: attachments))
.environmentObject(UserPreferences())
.environmentObject(client)
.environmentObject(account)
.environmentObject(theme)
.environmentObject(instance)
.tint(theme.tintColor)
.preferredColorScheme(theme.selectedScheme == .dark ? .dark : .light)
let childView = UIHostingController(rootView: view)
self.addChild(childView)
childView.view.frame = self.container.bounds
self.container.addSubview(childView.view)
childView.didMove(toParent: self)
}
}
NotificationCenter.default.addObserver(forName: NotificationsName.shareSheetClose,
object: nil,
queue: nil) { _ in
self.close()
}
}
func close() {
extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
}

View file

@ -0,0 +1,5 @@
import UIKit
public enum NotificationsName {
public static let shareSheetClose = NSNotification.Name("shareSheetClose")
}

View file

@ -55,13 +55,15 @@ struct StatusEditorAccessoryView: View {
Image(systemName: viewModel.spoilerOn ? "exclamationmark.triangle.fill": "exclamationmark.triangle")
}
Button {
isDrafsSheetDisplayed = true
} label: {
Image(systemName: "archivebox")
if !viewModel.mode.isInShareExtension {
Button {
isDrafsSheetDisplayed = true
} label: {
Image(systemName: "archivebox")
}
}
Spacer()
characterCountView

View file

@ -5,6 +5,7 @@ import DesignSystem
import NukeUI
struct StatusEditorMediaView: View {
@EnvironmentObject private var theme: Theme
@ObservedObject var viewModel: StatusEditorViewModel
@State private var editingContainer: StatusEditorViewModel.ImageContainer?
@ -12,17 +13,17 @@ struct StatusEditorMediaView: View {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 8) {
ForEach(viewModel.mediasImages) { container in
if container.image != nil {
makeLocalImage(container: container)
} else if let url = container.mediaAttachement?.url {
Menu {
makeImageMenu(container: container)
} label: {
ZStack(alignment: .bottomTrailing) {
Menu {
makeImageMenu(container: container)
} label: {
ZStack(alignment: .bottomTrailing) {
if container.image != nil {
makeLocalImage(container: container)
} else if let url = container.mediaAttachement?.url {
makeLazyImage(url: url)
if container.mediaAttachement?.description?.isEmpty == false {
altMarker
}
}
if container.mediaAttachement?.description?.isEmpty == false {
altMarker
}
}
}
@ -32,6 +33,7 @@ struct StatusEditorMediaView: View {
}
.sheet(item: $editingContainer) { container in
StatusEditorMediaEditView(viewModel: viewModel, container: container)
.preferredColorScheme(theme.selectedScheme == .dark ? .dark : .light)
}
}
@ -39,7 +41,7 @@ struct StatusEditorMediaView: View {
ZStack(alignment: .center) {
Image(uiImage: container.image!)
.resizable()
.blur(radius: 20 )
.blur(radius: container.mediaAttachement == nil ? 20 : 0)
.aspectRatio(contentMode: .fill)
.frame(width: 150, height: 150)
.cornerRadius(8)
@ -67,7 +69,7 @@ struct StatusEditorMediaView: View {
}
.buttonStyle(.bordered)
}
} else {
} else if container.mediaAttachement == nil{
ProgressView()
}
}

View file

@ -0,0 +1,30 @@
import UIKit
import Foundation
@MainActor
enum StatusEditorUTTypeSupported: String, CaseIterable {
case url = "public.url"
case text = "public.text"
case image = "public.image"
case jpeg = "public.jpeg"
case png = "public.png"
func loadItemContent(item: NSItemProvider) async throws -> Any? {
let result = try await item.loadItem(forTypeIdentifier: rawValue)
if self == .jpeg || self == .png,
let imageURL = result as? URL,
let data = try? Data(contentsOf: imageURL),
let image = UIImage(data: data) {
return image
}
if let url = result as? URL {
return url.absoluteString
} else if let text = result as? String {
return text
} else if let image = result as? UIImage {
return image
} else {
return nil
}
}
}

View file

@ -8,6 +8,7 @@ import Network
import PhotosUI
import NukeUI
import EmojiText
import UIKit
public struct StatusEditorView: View {
@EnvironmentObject private var preferences: UserPreferences
@ -72,6 +73,8 @@ public struct StatusEditorView: View {
viewModel.prepareStatusText()
if !client.isAuth {
dismiss()
NotificationCenter.default.post(name: NotificationsName.shareSheetClose,
object: nil)
}
}
.background(theme.primaryBackgroundColor)
@ -88,6 +91,8 @@ public struct StatusEditorView: View {
let status = await viewModel.postStatus()
if status != nil {
dismiss()
NotificationCenter.default.post(name: NotificationsName.shareSheetClose,
object: nil)
}
}
} label: {
@ -101,10 +106,12 @@ public struct StatusEditorView: View {
}
ToolbarItem(placement: .navigationBarLeading) {
Button {
if !viewModel.statusText.string.isEmpty {
if !viewModel.statusText.string.isEmpty && !viewModel.mode.isInShareExtension {
isDismissAlertPresented = true
} else {
dismiss()
NotificationCenter.default.post(name: NotificationsName.shareSheetClose,
object: nil)
}
} label: {
Text("Cancel")
@ -117,10 +124,14 @@ public struct StatusEditorView: View {
actions: {
Button("Delete Draft", role: .destructive) {
dismiss()
NotificationCenter.default.post(name: NotificationsName.shareSheetClose,
object: nil)
}
Button("Save Draft") {
preferences.draftsPosts.insert(viewModel.statusText.string, at: 0)
dismiss()
NotificationCenter.default.post(name: NotificationsName.shareSheetClose,
object: nil)
}
Button("Cancel", role: .cancel) { }
})

View file

@ -118,7 +118,7 @@ public class StatusEditorViewModel: ObservableObject {
mediaIds: mediasImages.compactMap{ $0.mediaAttachement?.id },
poll: pollData)
switch mode {
case .new, .replyTo, .quote, .mention:
case .new, .replyTo, .quote, .mention, .shareExtension:
postStatus = try await client.post(endpoint: Statuses.postStatus(json: data))
case let .edit(status):
postStatus = try await client.put(endpoint: Statuses.editStatus(id: status.id, json: data))
@ -137,6 +137,9 @@ public class StatusEditorViewModel: ObservableObject {
switch mode {
case let .new(visibility):
self.visibility = visibility
case let .shareExtension(items):
self.visibility = .pub
self.processItemsProvider(items: items)
case let .replyTo(status):
var mentionString = ""
if (status.reblog?.account.acct ?? status.account.acct) != currentAccount?.acct {
@ -241,6 +244,32 @@ public class StatusEditorViewModel: ObservableObject {
}
}
private func processItemsProvider(items: [NSItemProvider]) {
Task {
var initalText: String = ""
for item in items {
if let identifiter = item.registeredTypeIdentifiers.first,
let handledItemType = StatusEditorUTTypeSupported(rawValue: identifiter) {
do {
let content = try await handledItemType.loadItemContent(item: item)
if let text = content as? String {
initalText += "\(text) "
} else if let image = content as? UIImage {
mediasImages.append(.init(image: image, mediaAttachement: nil, error: nil))
}
} catch { }
}
}
if !initalText.isEmpty {
statusText = .init(string: initalText)
selectedRange = .init(location: statusText.string.utf16.count, length: 0)
}
if !mediasImages.isEmpty {
processMediasToUpload()
}
}
}
func resetPollDefaults() {
pollOptions = ["", ""]
@ -373,7 +402,9 @@ public class StatusEditorViewModel: ObservableObject {
if let data = originalContainer.image?.jpegData(compressionQuality: 0.90) {
let uploadedMedia = try await uploadMedia(data: data)
if let index = indexOf(container: newContainer) {
mediasImages[index] = .init(image: nil, mediaAttachement: uploadedMedia, error: nil)
mediasImages[index] = .init(image: mode.isInShareExtension ? originalContainer.image : nil,
mediaAttachement: uploadedMedia,
error: nil)
}
}
} catch {

View file

@ -1,4 +1,5 @@
import Models
import UIKit
extension StatusEditorViewModel {
public enum Mode {
@ -7,6 +8,16 @@ extension StatusEditorViewModel {
case edit(status: Status)
case quote(status: Status)
case mention(account: Account, visibility: Visibility)
case shareExtension(items: [NSItemProvider])
var isInShareExtension: Bool {
switch self {
case .shareExtension:
return true
default:
return false
}
}
var isEditing: Bool {
switch self {
@ -28,7 +39,7 @@ extension StatusEditorViewModel {
var title: String {
switch self {
case .new, .mention:
case .new, .mention, .shareExtension:
return "New Post"
case .edit:
return "Editing your post"