Initial share extension implementation
This commit is contained in:
parent
06855420da
commit
6c371f868f
|
@ -12,16 +12,24 @@ import PencilKit
|
|||
import TuskerComponents
|
||||
|
||||
public struct ComposeUIConfig {
|
||||
// Config
|
||||
public var allowSwitchingDrafts = true
|
||||
public var textSelectionStartsAtBeginning = false
|
||||
|
||||
// Style
|
||||
public var backgroundColor = Color(uiColor: .systemBackground)
|
||||
public var groupedBackgroundColor = Color(uiColor: .systemGroupedBackground)
|
||||
public var groupedCellBackgroundColor = Color(uiColor: .systemBackground)
|
||||
public var fillColor = Color(uiColor: .systemFill)
|
||||
public var avatarStyle = AvatarImageView.Style.roundRect
|
||||
|
||||
// Preferences
|
||||
public var useTwitterKeyboard = false
|
||||
public var contentType = StatusContentType.plain
|
||||
public var automaticallySaveDrafts = false
|
||||
public var requireAttachmentDescriptions = false
|
||||
|
||||
// Host callbacks
|
||||
public var dismiss: @MainActor (DismissMode) -> Void = { _ in }
|
||||
public var presentAssetPicker: ((@MainActor @escaping ([PHPickerResult]) -> Void) -> Void)?
|
||||
public var presentDrawing: ((PKDrawing, @escaping (PKDrawing) -> Void) -> Void)?
|
||||
|
|
|
@ -351,7 +351,7 @@ public final class ComposeController: ViewController {
|
|||
|
||||
@ViewBuilder
|
||||
private var postButton: some View {
|
||||
if draft.hasContent {
|
||||
if draft.hasContent || !controller.config.allowSwitchingDrafts {
|
||||
Button(action: controller.postStatus) {
|
||||
Text("Post")
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ struct MainTextView: View {
|
|||
|
||||
@State private var hasFirstAppeared = false
|
||||
@State private var height: CGFloat?
|
||||
@State private var updateSelection: ((UITextView) -> Void)?
|
||||
private let minHeight: CGFloat = 150
|
||||
private var effectiveHeight: CGFloat { height ?? minHeight }
|
||||
|
||||
|
@ -34,7 +35,7 @@ struct MainTextView: View {
|
|||
.accessibilityHidden(true)
|
||||
}
|
||||
|
||||
MainWrappedTextViewRepresentable(text: $draft.text, becomeFirstResponder: $controller.mainComposeTextViewBecomeFirstResponder, textDidChange: textDidChange)
|
||||
MainWrappedTextViewRepresentable(text: $draft.text, becomeFirstResponder: $controller.mainComposeTextViewBecomeFirstResponder, updateSelection: $updateSelection, textDidChange: textDidChange)
|
||||
}
|
||||
.frame(height: effectiveHeight)
|
||||
.onAppear(perform: becomeFirstResponderOnFirstAppearance)
|
||||
|
@ -44,6 +45,11 @@ struct MainTextView: View {
|
|||
if !hasFirstAppeared {
|
||||
hasFirstAppeared = true
|
||||
controller.mainComposeTextViewBecomeFirstResponder = true
|
||||
if config.textSelectionStartsAtBeginning {
|
||||
updateSelection = { textView in
|
||||
textView.selectedTextRange = textView.textRange(from: textView.beginningOfDocument, to: textView.beginningOfDocument)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,6 +63,7 @@ fileprivate struct MainWrappedTextViewRepresentable: UIViewRepresentable {
|
|||
|
||||
@Binding var text: String
|
||||
@Binding var becomeFirstResponder: Bool
|
||||
@Binding var updateSelection: ((UITextView) -> Void)?
|
||||
let textDidChange: (UITextView) -> Void
|
||||
|
||||
@EnvironmentObject private var controller: ComposeController
|
||||
|
@ -85,6 +92,11 @@ fileprivate struct MainWrappedTextViewRepresentable: UIViewRepresentable {
|
|||
|
||||
context.coordinator.text = $text
|
||||
|
||||
if let updateSelection {
|
||||
updateSelection(uiView)
|
||||
self.updateSelection = nil
|
||||
}
|
||||
|
||||
// wait until the next runloop iteration so that SwiftUI view updates have finished and
|
||||
// the text view knows its new content size
|
||||
DispatchQueue.main.async {
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.space.vaccor.Tusker</string>
|
||||
</array>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,154 @@
|
|||
//
|
||||
// ShareHostingController.swift
|
||||
// ShareExtension
|
||||
//
|
||||
// Created by Shadowfacts on 4/17/23.
|
||||
// Copyright © 2023 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import ComposeUI
|
||||
import TuskerComponents
|
||||
import WebURLFoundationExtras
|
||||
import Combine
|
||||
import TuskerPreferences
|
||||
|
||||
class ShareHostingController: UIHostingController<ShareHostingController.View> {
|
||||
private static func fetchAvatar(_ url: URL) async -> UIImage? {
|
||||
guard let (data, _) = try? await URLSession.shared.data(from: url),
|
||||
let image = UIImage(data: data) else {
|
||||
return nil
|
||||
}
|
||||
return await image.byPreparingThumbnail(ofSize: CGSize(width: 50, height: 50)) ?? image
|
||||
}
|
||||
|
||||
private let controller: ComposeController
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
init(draft: Draft, mastodonContext: ShareMastodonContext) {
|
||||
controller = ComposeController(
|
||||
draft: draft,
|
||||
config: ComposeUIConfig(),
|
||||
mastodonController: mastodonContext,
|
||||
fetchAvatar: Self.fetchAvatar,
|
||||
fetchStatus: { _ in fatalError("replies aren't allowed in share sheet") },
|
||||
displayNameLabel: { account, style, _ in AnyView(Text(account.displayName).font(.system(style))) },
|
||||
replyContentView: { _, _ in fatalError("replies aren't allowed in share sheet") },
|
||||
emojiImageView: {
|
||||
AnyView(AsyncImage(url: URL($0.url)!) {
|
||||
$0
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
} placeholder: {
|
||||
Image(systemName: "smiley.fill")
|
||||
})
|
||||
}
|
||||
)
|
||||
super.init(rootView: View(controller: controller))
|
||||
|
||||
updateConfig()
|
||||
|
||||
mastodonContext.$ownAccount
|
||||
.sink { [unowned self] in self.controller.currentAccount = $0 }
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
private func updateConfig() {
|
||||
var config = ComposeUIConfig()
|
||||
config.allowSwitchingDrafts = false
|
||||
config.textSelectionStartsAtBeginning = true
|
||||
// note: in the share sheet, we ignore this preference
|
||||
config.automaticallySaveDrafts = false
|
||||
|
||||
config.backgroundColor = Color(uiColor: .appBackground)
|
||||
config.groupedBackgroundColor = Color(uiColor: .appGroupedBackground)
|
||||
config.groupedCellBackgroundColor = Color(uiColor: .appGroupedCellBackground)
|
||||
config.fillColor = Color(uiColor: .appFill)
|
||||
switch Preferences.shared.avatarStyle {
|
||||
case .roundRect:
|
||||
config.avatarStyle = .roundRect
|
||||
case .circle:
|
||||
config.avatarStyle = .circle
|
||||
}
|
||||
config.useTwitterKeyboard = Preferences.shared.useTwitterKeyboard
|
||||
config.contentType = Preferences.shared.statusContentType
|
||||
config.requireAttachmentDescriptions = Preferences.shared.requireAttachmentDescriptions
|
||||
|
||||
config.dismiss = { [unowned self] in self.dismiss(mode: $0) }
|
||||
|
||||
controller.config = config
|
||||
}
|
||||
|
||||
private func dismiss(mode: DismissMode) {
|
||||
guard let extensionContext else { return }
|
||||
switch mode {
|
||||
case .cancel:
|
||||
extensionContext.cancelRequest(withError: Error.cancelled)
|
||||
case .post:
|
||||
extensionContext.completeRequest(returningItems: nil)
|
||||
}
|
||||
}
|
||||
|
||||
struct View: SwiftUI.View {
|
||||
let controller: ComposeController
|
||||
|
||||
var body: some SwiftUI.View {
|
||||
ControllerView(controller: { controller })
|
||||
}
|
||||
}
|
||||
|
||||
enum Error: Swift.Error {
|
||||
case cancelled
|
||||
}
|
||||
}
|
||||
|
||||
// todo: shouldn't just copy this from the main Colors.swift
|
||||
extension UIColor {
|
||||
static let appBackground = UIColor { traitCollection in
|
||||
if case .dark = traitCollection.userInterfaceStyle,
|
||||
!Preferences.shared.pureBlackDarkMode {
|
||||
return UIColor(hue: 230/360, saturation: 23/100, brightness: 10/100, alpha: 1)
|
||||
} else {
|
||||
return .systemBackground
|
||||
}
|
||||
}
|
||||
|
||||
static let appGroupedBackground = UIColor { traitCollection in
|
||||
if case .dark = traitCollection.userInterfaceStyle,
|
||||
!Preferences.shared.pureBlackDarkMode {
|
||||
if traitCollection.userInterfaceLevel == .elevated {
|
||||
return UIColor(hue: 230/360, saturation: 23/100, brightness: 10/100, alpha: 1)
|
||||
} else {
|
||||
return UIColor(hue: 230/360, saturation: 23/100, brightness: 5/100, alpha: 1)
|
||||
}
|
||||
} else {
|
||||
return .systemGroupedBackground
|
||||
}
|
||||
}
|
||||
|
||||
static let appGroupedCellBackground = UIColor { traitCollection in
|
||||
if case .dark = traitCollection.userInterfaceStyle {
|
||||
if Preferences.shared.pureBlackDarkMode {
|
||||
return .secondarySystemBackground
|
||||
} else {
|
||||
return .appFill
|
||||
}
|
||||
} else {
|
||||
return .systemBackground
|
||||
}
|
||||
}
|
||||
|
||||
static let appFill = UIColor { traitCollection in
|
||||
if case .dark = traitCollection.userInterfaceStyle,
|
||||
!Preferences.shared.pureBlackDarkMode {
|
||||
return UIColor(hue: 230/360, saturation: 20/100, brightness: 17/100, alpha: 1)
|
||||
} else {
|
||||
return .systemFill
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// ShareMastodonContext.swift
|
||||
// ShareExtension
|
||||
//
|
||||
// Created by Shadowfacts on 4/17/23.
|
||||
// Copyright © 2023 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import Pachyderm
|
||||
import ComposeUI
|
||||
import UserAccounts
|
||||
import InstanceFeatures
|
||||
import Combine
|
||||
|
||||
class ShareMastodonContext: ComposeMastodonContext, ObservableObject {
|
||||
let accountInfo: UserAccountInfo?
|
||||
let client: Client
|
||||
let instanceFeatures: InstanceFeatures
|
||||
|
||||
@MainActor
|
||||
private var customEmojis: [Emoji]?
|
||||
|
||||
@Published var ownAccount: Account?
|
||||
|
||||
init(accountInfo: UserAccountInfo) {
|
||||
self.accountInfo = accountInfo
|
||||
self.client = Client(baseURL: accountInfo.instanceURL, accessToken: accountInfo.accessToken)
|
||||
self.instanceFeatures = InstanceFeatures()
|
||||
|
||||
Task { @MainActor in
|
||||
async let instance = try? await run(Client.getInstance()).0
|
||||
async let nodeInfo: NodeInfo? = await withCheckedContinuation({ continuation in
|
||||
self.client.nodeInfo { response in
|
||||
switch response {
|
||||
case .success(let nodeInfo, _):
|
||||
continuation.resume(returning: nodeInfo)
|
||||
case .failure(_):
|
||||
continuation.resume(returning: nil)
|
||||
}
|
||||
}
|
||||
})
|
||||
guard let instance = await instance else { return }
|
||||
self.instanceFeatures.update(instance: instance, nodeInfo: await nodeInfo)
|
||||
}
|
||||
|
||||
Task { @MainActor in
|
||||
if let account = try? await run(Client.getSelfAccount()).0 {
|
||||
self.ownAccount = account
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: CompmoseMastodonContext
|
||||
|
||||
func run<Result: Decodable & Sendable>(_ request: Request<Result>) async throws -> (Result, Pagination?) {
|
||||
return try await withCheckedThrowingContinuation({ continuation in
|
||||
client.run(request) { response in
|
||||
switch response {
|
||||
case .success(let result, let pagination):
|
||||
continuation.resume(returning: (result, pagination))
|
||||
case .failure(let error):
|
||||
continuation.resume(throwing: error)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@MainActor
|
||||
func getCustomEmojis(completion: @escaping ([Emoji]) -> Void) {
|
||||
if let customEmojis {
|
||||
completion(customEmojis)
|
||||
} else {
|
||||
Task.detached { @MainActor in
|
||||
let emojis = (try? await self.run(Client.getCustomEmoji()).0) ?? []
|
||||
self.customEmojis = emojis
|
||||
completion(emojis)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func searchCachedAccounts(query: String) -> [AccountProtocol] {
|
||||
return []
|
||||
}
|
||||
|
||||
func cachedRelationship(for accountID: String) -> RelationshipProtocol? {
|
||||
return nil
|
||||
}
|
||||
|
||||
func searchCachedHashtags(query: String) -> [Hashtag] {
|
||||
return []
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
//
|
||||
// ShareViewController.swift
|
||||
// ShareExtension
|
||||
//
|
||||
// Created by Shadowfacts on 4/17/23.
|
||||
// Copyright © 2023 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import UserAccounts
|
||||
import ComposeUI
|
||||
import UniformTypeIdentifiers
|
||||
import TuskerPreferences
|
||||
|
||||
class ShareViewController: UIViewController {
|
||||
|
||||
private var state: State = .loading
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
view.tintColor = Preferences.shared.accentColor.color
|
||||
|
||||
if let account = UserAccountsManager.shared.getMostRecentAccount() {
|
||||
Task { @MainActor in
|
||||
let draft = await createDraft(account: account)
|
||||
state = .ok
|
||||
|
||||
let context = ShareMastodonContext(accountInfo: account)
|
||||
let host = ShareHostingController(draft: draft, mastodonContext: context)
|
||||
let nav = UINavigationController(rootViewController: host)
|
||||
self.addChild(nav)
|
||||
nav.view.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.view.addSubview(nav.view)
|
||||
NSLayoutConstraint.activate([
|
||||
nav.view.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
|
||||
nav.view.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
|
||||
nav.view.topAnchor.constraint(equalTo: self.view.topAnchor),
|
||||
nav.view.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
|
||||
])
|
||||
nav.didMove(toParent: self)
|
||||
}
|
||||
} else {
|
||||
state = .notLoggedIn
|
||||
}
|
||||
}
|
||||
|
||||
private func createDraft(account: UserAccountInfo) async -> Draft {
|
||||
let (text, attachments) = await getDraftConfigurationFromExtensionContext()
|
||||
let draft = Draft(
|
||||
accountID: account.id,
|
||||
text: text,
|
||||
contentWarning: "",
|
||||
inReplyToID: nil,
|
||||
// TODO: get the default visibility from preferences
|
||||
visibility: .public,
|
||||
localOnly: false
|
||||
)
|
||||
draft.attachments = attachments
|
||||
return draft
|
||||
}
|
||||
|
||||
private func getDraftConfigurationFromExtensionContext() async -> (String, [DraftAttachment]) {
|
||||
guard let extensionContext,
|
||||
let inputItem = (extensionContext.inputItems as? [NSExtensionItem])?.first,
|
||||
let itemProvider = inputItem.attachments?.first else {
|
||||
return ("", [])
|
||||
}
|
||||
if let url: NSURL = await getObject(from: itemProvider) {
|
||||
if let title = inputItem.attributedTitle ?? inputItem.attributedContentText {
|
||||
return ("\n\n\(title.string)\n\(url.absoluteString ?? "")", [])
|
||||
} else {
|
||||
return ("\n\n\(url.absoluteString ?? "")", [])
|
||||
}
|
||||
} else if let text: NSString = await getObject(from: itemProvider) {
|
||||
return ("\n\n\(text)", [])
|
||||
} else if let attachment: DraftAttachment = await getObject(from: itemProvider) {
|
||||
return ("", [attachment])
|
||||
} else if let attributedContent = inputItem.attributedContentText {
|
||||
return ("\n\n\(attributedContent.string)", [])
|
||||
} else {
|
||||
return ("", [])
|
||||
}
|
||||
}
|
||||
|
||||
private func getObject<T: NSItemProviderReading>(from itemProvider: NSItemProvider) async -> T? {
|
||||
guard itemProvider.canLoadObject(ofClass: T.self) else {
|
||||
return nil
|
||||
}
|
||||
return await withCheckedContinuation({ continuation in
|
||||
itemProvider.loadObject(ofClass: T.self) { object, error in
|
||||
continuation.resume(returning: object as? T)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
if case .notLoggedIn = state {
|
||||
let alert = UIAlertController(title: "Not Logged In", message: "You need to log in to an account through the app before you can post.", preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { [unowned self] _ in
|
||||
self.extensionContext!.cancelRequest(withError: Error.notLoggedIn)
|
||||
}))
|
||||
present(alert, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
enum State {
|
||||
case loading
|
||||
case notLoggedIn
|
||||
case ok
|
||||
}
|
||||
|
||||
enum Error: Swift.Error {
|
||||
case notLoggedIn
|
||||
}
|
||||
|
||||
}
|
|
@ -220,6 +220,16 @@
|
|||
D6A3BC812321B7E600FD64D5 /* FollowNotificationGroupTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6A3BC7F2321B7E600FD64D5 /* FollowNotificationGroupTableViewCell.xib */; };
|
||||
D6A3BC8A2321F79B00FD64D5 /* AccountTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A3BC882321F79B00FD64D5 /* AccountTableViewCell.swift */; };
|
||||
D6A3BC8B2321F79B00FD64D5 /* AccountTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6A3BC892321F79B00FD64D5 /* AccountTableViewCell.xib */; };
|
||||
D6A4531629EF64BA00032932 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A4531529EF64BA00032932 /* ShareViewController.swift */; };
|
||||
D6A4531929EF64BA00032932 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D6A4531729EF64BA00032932 /* MainInterface.storyboard */; };
|
||||
D6A4531D29EF64BA00032932 /* ShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = D6A4531329EF64BA00032932 /* ShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
D6A4532429EF665200032932 /* ComposeUI in Frameworks */ = {isa = PBXBuildFile; productRef = D6A4532329EF665200032932 /* ComposeUI */; };
|
||||
D6A4532629EF665600032932 /* InstanceFeatures in Frameworks */ = {isa = PBXBuildFile; productRef = D6A4532529EF665600032932 /* InstanceFeatures */; };
|
||||
D6A4532829EF665800032932 /* Pachyderm in Frameworks */ = {isa = PBXBuildFile; productRef = D6A4532729EF665800032932 /* Pachyderm */; };
|
||||
D6A4532A29EF665A00032932 /* TuskerPreferences in Frameworks */ = {isa = PBXBuildFile; productRef = D6A4532929EF665A00032932 /* TuskerPreferences */; };
|
||||
D6A4532C29EF665D00032932 /* UserAccounts in Frameworks */ = {isa = PBXBuildFile; productRef = D6A4532B29EF665D00032932 /* UserAccounts */; };
|
||||
D6A4532E29EF7DDD00032932 /* ShareHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A4532D29EF7DDD00032932 /* ShareHostingController.swift */; };
|
||||
D6A4533029EF7DEE00032932 /* ShareMastodonContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A4532F29EF7DEE00032932 /* ShareMastodonContext.swift */; };
|
||||
D6A4DCCC2553667800D9DE31 /* FastAccountSwitcherViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A4DCCA2553667800D9DE31 /* FastAccountSwitcherViewController.swift */; };
|
||||
D6A4DCCD2553667800D9DE31 /* FastAccountSwitcherViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6A4DCCB2553667800D9DE31 /* FastAccountSwitcherViewController.xib */; };
|
||||
D6A4DCE525537C7A00D9DE31 /* FastSwitchingAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A4DCE425537C7A00D9DE31 /* FastSwitchingAccountView.swift */; };
|
||||
|
@ -335,6 +345,13 @@
|
|||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
D6A4531B29EF64BA00032932 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = D6D4DDC4212518A000E1C4BB /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = D6A4531229EF64BA00032932;
|
||||
remoteInfo = ShareExtension;
|
||||
};
|
||||
D6D4DDE1212518A200E1C4BB /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = D6D4DDC4212518A000E1C4BB /* Project object */;
|
||||
|
@ -365,6 +382,7 @@
|
|||
dstPath = "";
|
||||
dstSubfolderSpec = 13;
|
||||
files = (
|
||||
D6A4531D29EF64BA00032932 /* ShareExtension.appex in Embed Foundation Extensions */,
|
||||
D6E343B4265AAD6B00C4AA01 /* OpenInTusker.appex in Embed Foundation Extensions */,
|
||||
);
|
||||
name = "Embed Foundation Extensions";
|
||||
|
@ -597,6 +615,13 @@
|
|||
D6A3BC7F2321B7E600FD64D5 /* FollowNotificationGroupTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FollowNotificationGroupTableViewCell.xib; sourceTree = "<group>"; };
|
||||
D6A3BC882321F79B00FD64D5 /* AccountTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountTableViewCell.swift; sourceTree = "<group>"; };
|
||||
D6A3BC892321F79B00FD64D5 /* AccountTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AccountTableViewCell.xib; sourceTree = "<group>"; };
|
||||
D6A4531329EF64BA00032932 /* ShareExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D6A4531529EF64BA00032932 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
|
||||
D6A4531829EF64BA00032932 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
|
||||
D6A4531A29EF64BA00032932 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
D6A4531E29EF64BA00032932 /* ShareExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ShareExtension.entitlements; sourceTree = "<group>"; };
|
||||
D6A4532D29EF7DDD00032932 /* ShareHostingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareHostingController.swift; sourceTree = "<group>"; };
|
||||
D6A4532F29EF7DEE00032932 /* ShareMastodonContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareMastodonContext.swift; sourceTree = "<group>"; };
|
||||
D6A4DCCA2553667800D9DE31 /* FastAccountSwitcherViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FastAccountSwitcherViewController.swift; sourceTree = "<group>"; };
|
||||
D6A4DCCB2553667800D9DE31 /* FastAccountSwitcherViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FastAccountSwitcherViewController.xib; sourceTree = "<group>"; };
|
||||
D6A4DCE425537C7A00D9DE31 /* FastSwitchingAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FastSwitchingAccountView.swift; sourceTree = "<group>"; };
|
||||
|
@ -723,6 +748,18 @@
|
|||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
D6A4531029EF64BA00032932 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D6A4532829EF665800032932 /* Pachyderm in Frameworks */,
|
||||
D6A4532A29EF665A00032932 /* TuskerPreferences in Frameworks */,
|
||||
D6A4532629EF665600032932 /* InstanceFeatures in Frameworks */,
|
||||
D6A4532C29EF665D00032932 /* UserAccounts in Frameworks */,
|
||||
D6A4532429EF665200032932 /* ComposeUI in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
D6D4DDC9212518A000E1C4BB /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -1264,6 +1301,19 @@
|
|||
path = "Status Action Account List";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D6A4531429EF64BA00032932 /* ShareExtension */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D6A4531E29EF64BA00032932 /* ShareExtension.entitlements */,
|
||||
D6A4532F29EF7DEE00032932 /* ShareMastodonContext.swift */,
|
||||
D6A4531529EF64BA00032932 /* ShareViewController.swift */,
|
||||
D6A4532D29EF7DDD00032932 /* ShareHostingController.swift */,
|
||||
D6A4531729EF64BA00032932 /* MainInterface.storyboard */,
|
||||
D6A4531A29EF64BA00032932 /* Info.plist */,
|
||||
);
|
||||
path = ShareExtension;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D6A4DCC92553666600D9DE31 /* Fast Account Switcher */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -1408,6 +1458,7 @@
|
|||
D6D4DDE3212518A200E1C4BB /* TuskerTests */,
|
||||
D6D4DDEE212518A200E1C4BB /* TuskerUITests */,
|
||||
D6E343A9265AAD6B00C4AA01 /* OpenInTusker */,
|
||||
D6A4531429EF64BA00032932 /* ShareExtension */,
|
||||
D6D4DDCD212518A000E1C4BB /* Products */,
|
||||
D65A37F221472F300087646E /* Frameworks */,
|
||||
);
|
||||
|
@ -1420,6 +1471,7 @@
|
|||
D6D4DDE0212518A200E1C4BB /* TuskerTests.xctest */,
|
||||
D6D4DDEB212518A200E1C4BB /* TuskerUITests.xctest */,
|
||||
D6E343A8265AAD6B00C4AA01 /* OpenInTusker.appex */,
|
||||
D6A4531329EF64BA00032932 /* ShareExtension.appex */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1578,6 +1630,30 @@
|
|||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
D6A4531229EF64BA00032932 /* ShareExtension */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = D6A4532229EF64BA00032932 /* Build configuration list for PBXNativeTarget "ShareExtension" */;
|
||||
buildPhases = (
|
||||
D6A4530F29EF64BA00032932 /* Sources */,
|
||||
D6A4531029EF64BA00032932 /* Frameworks */,
|
||||
D6A4531129EF64BA00032932 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = ShareExtension;
|
||||
packageProductDependencies = (
|
||||
D6A4532329EF665200032932 /* ComposeUI */,
|
||||
D6A4532529EF665600032932 /* InstanceFeatures */,
|
||||
D6A4532729EF665800032932 /* Pachyderm */,
|
||||
D6A4532929EF665A00032932 /* TuskerPreferences */,
|
||||
D6A4532B29EF665D00032932 /* UserAccounts */,
|
||||
);
|
||||
productName = ShareExtension;
|
||||
productReference = D6A4531329EF64BA00032932 /* ShareExtension.appex */;
|
||||
productType = "com.apple.product-type.app-extension";
|
||||
};
|
||||
D6D4DDCB212518A000E1C4BB /* Tusker */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = D6D4DDF4212518A200E1C4BB /* Build configuration list for PBXNativeTarget "Tusker" */;
|
||||
|
@ -1594,6 +1670,7 @@
|
|||
);
|
||||
dependencies = (
|
||||
D6E343B3265AAD6B00C4AA01 /* PBXTargetDependency */,
|
||||
D6A4531C29EF64BA00032932 /* PBXTargetDependency */,
|
||||
);
|
||||
name = Tusker;
|
||||
packageProductDependencies = (
|
||||
|
@ -1676,10 +1753,13 @@
|
|||
D6D4DDC4212518A000E1C4BB /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 1250;
|
||||
LastSwiftUpdateCheck = 1430;
|
||||
LastUpgradeCheck = 1400;
|
||||
ORGANIZATIONNAME = Shadowfacts;
|
||||
TargetAttributes = {
|
||||
D6A4531229EF64BA00032932 = {
|
||||
CreatedOnToolsVersion = 14.3;
|
||||
};
|
||||
D6D4DDCB212518A000E1C4BB = {
|
||||
CreatedOnToolsVersion = 10.0;
|
||||
LastSwiftMigration = 1420;
|
||||
|
@ -1728,11 +1808,20 @@
|
|||
D6D4DDDF212518A200E1C4BB /* TuskerTests */,
|
||||
D6D4DDEA212518A200E1C4BB /* TuskerUITests */,
|
||||
D6E343A7265AAD6B00C4AA01 /* OpenInTusker */,
|
||||
D6A4531229EF64BA00032932 /* ShareExtension */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
D6A4531129EF64BA00032932 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D6A4531929EF64BA00032932 /* MainInterface.storyboard in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
D6D4DDCA212518A000E1C4BB /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -1832,6 +1921,16 @@
|
|||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
D6A4530F29EF64BA00032932 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D6A4532E29EF7DDD00032932 /* ShareHostingController.swift in Sources */,
|
||||
D6A4531629EF64BA00032932 /* ShareViewController.swift in Sources */,
|
||||
D6A4533029EF7DEE00032932 /* ShareMastodonContext.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
D6D4DDC8212518A000E1C4BB /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -2150,6 +2249,11 @@
|
|||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
D6A4531C29EF64BA00032932 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = D6A4531229EF64BA00032932 /* ShareExtension */;
|
||||
targetProxy = D6A4531B29EF64BA00032932 /* PBXContainerItemProxy */;
|
||||
};
|
||||
D6D4DDE2212518A200E1C4BB /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = D6D4DDCB212518A000E1C4BB /* Tusker */;
|
||||
|
@ -2169,6 +2273,14 @@
|
|||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
D6A4531729EF64BA00032932 /* MainInterface.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
D6A4531829EF64BA00032932 /* Base */,
|
||||
);
|
||||
name = MainInterface.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D6D4DDD8212518A200E1C4BB /* LaunchScreen.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
|
@ -2349,6 +2461,93 @@
|
|||
};
|
||||
name = Dist;
|
||||
};
|
||||
D6A4531F29EF64BA00032932 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Shadowfacts. All rights reserved.";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.ShareExtension;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
D6A4532029EF64BA00032932 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Shadowfacts. All rights reserved.";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.ShareExtension;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
D6A4532129EF64BA00032932 /* Dist */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = ShareExtension;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Shadowfacts. All rights reserved.";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.4;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.ShareExtension;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTS_MACCATALYST = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Dist;
|
||||
};
|
||||
D6D4DDF2212518A200E1C4BB /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = D6D706A829498C82000827ED /* Tusker.xcconfig */;
|
||||
|
@ -2667,6 +2866,16 @@
|
|||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
D6A4532229EF64BA00032932 /* Build configuration list for PBXNativeTarget "ShareExtension" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
D6A4531F29EF64BA00032932 /* Debug */,
|
||||
D6A4532029EF64BA00032932 /* Release */,
|
||||
D6A4532129EF64BA00032932 /* Dist */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
D6D4DDC7212518A000E1C4BB /* Build configuration list for PBXProject "Tusker" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
|
@ -2791,6 +3000,26 @@
|
|||
isa = XCSwiftPackageProductDependency;
|
||||
productName = Pachyderm;
|
||||
};
|
||||
D6A4532329EF665200032932 /* ComposeUI */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = ComposeUI;
|
||||
};
|
||||
D6A4532529EF665600032932 /* InstanceFeatures */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = InstanceFeatures;
|
||||
};
|
||||
D6A4532729EF665800032932 /* Pachyderm */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = Pachyderm;
|
||||
};
|
||||
D6A4532929EF665A00032932 /* TuskerPreferences */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = TuskerPreferences;
|
||||
};
|
||||
D6A4532B29EF665D00032932 /* UserAccounts */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = UserAccounts;
|
||||
};
|
||||
D6B0026D29B5248800C70BE2 /* UserAccounts */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = UserAccounts;
|
||||
|
|
Loading…
Reference in New Issue