mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-22 21:15:55 +03:00
Merge pull request #6614 from nextcloud/feature/file-provider-sharing
File sharing for macOS VFS (File Provider Module)
This commit is contained in:
commit
3afa861f91
22 changed files with 2392 additions and 5 deletions
|
@ -35,7 +35,20 @@ if(APPLE)
|
|||
COMMENT building macOS File Provider extension
|
||||
VERBATIM)
|
||||
|
||||
add_dependencies(mac_overlayplugin mac_fileproviderplugin nextcloud) # for the ownCloud.icns to be generated
|
||||
add_custom_target( mac_fileprovideruiplugin ALL
|
||||
xcodebuild ARCHS=${CMAKE_OSX_ARCHITECTURES} ONLY_ACTIVE_ARCH=NO
|
||||
-project ${CMAKE_SOURCE_DIR}/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj
|
||||
-target FileProviderUIExt -configuration ${XCODE_TARGET_CONFIGURATION} "SYMROOT=${CMAKE_CURRENT_BINARY_DIR}"
|
||||
"OC_APPLICATION_EXECUTABLE_NAME=${APPLICATION_EXECUTABLE}"
|
||||
"OC_APPLICATION_VENDOR=${APPLICATION_VENDOR}"
|
||||
"OC_APPLICATION_NAME=${APPLICATION_NAME}"
|
||||
"OC_APPLICATION_REV_DOMAIN=${APPLICATION_REV_DOMAIN}"
|
||||
"OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX=${SOCKETAPI_TEAM_IDENTIFIER_PREFIX}"
|
||||
DEPENDS mac_fileproviderplugin
|
||||
COMMENT building macOS File Provider UI extension
|
||||
VERBATIM)
|
||||
|
||||
add_dependencies(mac_overlayplugin mac_fileproviderplugin mac_fileprovideruiplugin nextcloud) # for the ownCloud.icns to be generated
|
||||
else()
|
||||
add_dependencies(mac_overlayplugin nextcloud) # for the ownCloud.icns to be generated
|
||||
endif()
|
||||
|
@ -55,6 +68,10 @@ if(APPLE)
|
|||
install(DIRECTORY ${OSX_PLUGINS_BINARY_DIR}/FileProviderExt.appex
|
||||
DESTINATION ${OSX_PLUGINS_INSTALL_DIR}
|
||||
USE_SOURCE_PERMISSIONS)
|
||||
|
||||
install(DIRECTORY ${OSX_PLUGINS_BINARY_DIR}/FileProviderUIExt.appex
|
||||
DESTINATION ${OSX_PLUGINS_INSTALL_DIR}
|
||||
USE_SOURCE_PERMISSIONS)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
|
|
@ -19,12 +19,15 @@ extension Logger {
|
|||
|
||||
static let desktopClientConnection = Logger(
|
||||
subsystem: subsystem, category: "desktopclientconnection")
|
||||
static let fpUiExtensionService = Logger(subsystem: subsystem, category: "fpUiExtensionService")
|
||||
static let enumeration = Logger(subsystem: subsystem, category: "enumeration")
|
||||
static let fileProviderExtension = Logger(
|
||||
subsystem: subsystem, category: "fileproviderextension")
|
||||
static let fileTransfer = Logger(subsystem: subsystem, category: "filetransfer")
|
||||
static let localFileOps = Logger(subsystem: subsystem, category: "localfileoperations")
|
||||
static let ncFilesDatabase = Logger(subsystem: subsystem, category: "nextcloudfilesdatabase")
|
||||
static let shares = Logger(subsystem: subsystem, category: "shares")
|
||||
static let ncAccount = Logger(subsystem: subsystem, category: "ncAccount")
|
||||
static let materialisedFileHandling = Logger(
|
||||
subsystem: subsystem, category: "materialisedfilehandling"
|
||||
)
|
||||
|
|
|
@ -39,7 +39,8 @@ extension FileProviderExtension: NSFileProviderServicing {
|
|||
) -> Progress {
|
||||
Logger.desktopClientConnection.debug("Serving supported service sources")
|
||||
let clientCommService = ClientCommunicationService(fpExtension: self)
|
||||
let services = [clientCommService]
|
||||
let fpuiExtService = FPUIExtensionServiceSource(fpExtension: self)
|
||||
let services: [NSFileProviderServiceSource] = [clientCommService, fpuiExtService]
|
||||
completionHandler(services, nil)
|
||||
let progress = Progress()
|
||||
progress.cancellationHandler = {
|
||||
|
|
|
@ -15,6 +15,12 @@
|
|||
import FileProvider
|
||||
import Foundation
|
||||
|
||||
let ncAccountDictUsernameKey = "usernameKey"
|
||||
let ncAccountDictPasswordKey = "passwordKey"
|
||||
let ncAccountDictNcKitAccountKey = "ncKitAccountKey"
|
||||
let ncAccountDictServerUrlKey = "serverUrlKey"
|
||||
let ncAccountDictDavFilesUrlKey = "davFilesUrlKey"
|
||||
|
||||
struct NextcloudAccount: Equatable {
|
||||
static let webDavFilesUrlSuffix: String = "/remote.php/dav/files/"
|
||||
let username, password, ncKitAccount, serverUrl, davFilesUrl: String
|
||||
|
@ -26,4 +32,31 @@ struct NextcloudAccount: Equatable {
|
|||
self.serverUrl = serverUrl
|
||||
davFilesUrl = serverUrl + NextcloudAccount.webDavFilesUrlSuffix + user
|
||||
}
|
||||
|
||||
init?(dictionary: Dictionary<String, String>) {
|
||||
guard let username = dictionary[ncAccountDictUsernameKey],
|
||||
let password = dictionary[ncAccountDictPasswordKey],
|
||||
let ncKitAccount = dictionary[ncAccountDictNcKitAccountKey],
|
||||
let serverUrl = dictionary[ncAccountDictServerUrlKey],
|
||||
let davFilesUrl = dictionary[ncAccountDictDavFilesUrlKey]
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.ncKitAccount = ncKitAccount
|
||||
self.serverUrl = serverUrl
|
||||
self.davFilesUrl = davFilesUrl
|
||||
}
|
||||
|
||||
func dictionary() -> Dictionary<String, String> {
|
||||
return [
|
||||
ncAccountDictUsernameKey: username,
|
||||
ncAccountDictPasswordKey: password,
|
||||
ncAccountDictNcKitAccountKey: ncKitAccount,
|
||||
ncAccountDictServerUrlKey: serverUrl,
|
||||
ncAccountDictDavFilesUrlKey: davFilesUrl
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
//
|
||||
// FPUIExtensionCommunicationProtocol.swift
|
||||
// FileProviderExt
|
||||
//
|
||||
// Created by Claudio Cambra on 21/2/24.
|
||||
//
|
||||
|
||||
import FileProvider
|
||||
import NextcloudKit
|
||||
|
||||
let fpUiExtensionServiceName = NSFileProviderServiceName(
|
||||
"com.nextcloud.desktopclient.FPUIExtensionService"
|
||||
)
|
||||
|
||||
@objc protocol FPUIExtensionService {
|
||||
func credentials() async -> NSDictionary
|
||||
func itemServerPath(identifier: NSFileProviderItemIdentifier) async -> NSString?
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
//
|
||||
// FPUIExtensionCommunicationService.swift
|
||||
// FileProviderExt
|
||||
//
|
||||
// Created by Claudio Cambra on 21/2/24.
|
||||
//
|
||||
|
||||
import FileProvider
|
||||
import Foundation
|
||||
import NextcloudKit
|
||||
import OSLog
|
||||
|
||||
class FPUIExtensionServiceSource: NSObject, NSFileProviderServiceSource, NSXPCListenerDelegate, FPUIExtensionService {
|
||||
let listener = NSXPCListener.anonymous()
|
||||
let serviceName = fpUiExtensionServiceName
|
||||
let fpExtension: FileProviderExtension
|
||||
|
||||
init(fpExtension: FileProviderExtension) {
|
||||
Logger.fpUiExtensionService.debug("Instantiating FPUIExtensionService service")
|
||||
self.fpExtension = fpExtension
|
||||
super.init()
|
||||
}
|
||||
|
||||
func makeListenerEndpoint() throws -> NSXPCListenerEndpoint {
|
||||
listener.delegate = self
|
||||
listener.resume()
|
||||
return listener.endpoint
|
||||
}
|
||||
|
||||
func listener(
|
||||
_ listener: NSXPCListener,
|
||||
shouldAcceptNewConnection newConnection: NSXPCConnection
|
||||
) -> Bool {
|
||||
newConnection.exportedInterface = NSXPCInterface(with: FPUIExtensionService.self)
|
||||
newConnection.exportedObject = self
|
||||
newConnection.resume()
|
||||
return true
|
||||
}
|
||||
|
||||
//MARK: - FPUIExtensionService protocol methods
|
||||
|
||||
func credentials() async -> NSDictionary {
|
||||
return (fpExtension.ncAccount?.dictionary() ?? [:]) as NSDictionary
|
||||
}
|
||||
|
||||
func itemServerPath(identifier: NSFileProviderItemIdentifier) async -> NSString? {
|
||||
let rawIdentifier = identifier.rawValue
|
||||
Logger.shares.info("Fetching shares for item \(rawIdentifier, privacy: .public)")
|
||||
|
||||
guard let baseUrl = fpExtension.ncAccount?.davFilesUrl else {
|
||||
Logger.shares.error("Could not fetch shares as ncAccount on parent extension is nil")
|
||||
return nil
|
||||
}
|
||||
|
||||
let dbManager = NextcloudFilesDatabaseManager.shared
|
||||
guard let item = dbManager.itemMetadataFromFileProviderItemIdentifier(identifier) else {
|
||||
Logger.shares.error("No item \(rawIdentifier, privacy: .public) in db, no shares.")
|
||||
return nil
|
||||
}
|
||||
|
||||
let completePath = item.serverUrl + "/" + item.fileName
|
||||
return completePath.replacingOccurrences(of: baseUrl, with: "") as NSString
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// DocumentActionViewController.swift
|
||||
// FileProviderUIExt
|
||||
//
|
||||
// Created by Claudio Cambra on 20/2/24.
|
||||
//
|
||||
|
||||
import FileProviderUI
|
||||
import OSLog
|
||||
|
||||
class DocumentActionViewController: FPUIActionExtensionViewController {
|
||||
var domain: NSFileProviderDomain {
|
||||
guard let identifier = extensionContext.domainIdentifier else {
|
||||
fatalError("not expected to be called with default domain")
|
||||
}
|
||||
return NSFileProviderDomain(
|
||||
identifier: NSFileProviderDomainIdentifier(rawValue: identifier.rawValue),
|
||||
displayName: ""
|
||||
)
|
||||
}
|
||||
|
||||
func prepare(childViewController: NSViewController) {
|
||||
addChild(childViewController)
|
||||
view.addSubview(childViewController.view)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
view.leadingAnchor.constraint(equalTo: childViewController.view.leadingAnchor),
|
||||
view.trailingAnchor.constraint(equalTo: childViewController.view.trailingAnchor),
|
||||
view.topAnchor.constraint(equalTo: childViewController.view.topAnchor),
|
||||
view.bottomAnchor.constraint(equalTo: childViewController.view.bottomAnchor)
|
||||
])
|
||||
}
|
||||
|
||||
override func prepare(
|
||||
forAction actionIdentifier: String, itemIdentifiers: [NSFileProviderItemIdentifier]
|
||||
) {
|
||||
Logger.actionViewController.info("Preparing for action: \(actionIdentifier)")
|
||||
|
||||
if actionIdentifier == "com.nextcloud.desktopclient.FileProviderUIExt.ShareAction" {
|
||||
prepare(childViewController: ShareViewController(itemIdentifiers))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override func prepare(forError error: Error) {
|
||||
Logger.actionViewController.info("Preparing for error: \(error.localizedDescription)")
|
||||
}
|
||||
|
||||
override public func loadView() {
|
||||
self.view = NSView()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// Logger+Extensions.swift
|
||||
// FileProviderUIExt
|
||||
//
|
||||
// Created by Claudio Cambra on 21/2/24.
|
||||
//
|
||||
|
||||
import OSLog
|
||||
|
||||
extension Logger {
|
||||
private static var subsystem = Bundle.main.bundleIdentifier!
|
||||
|
||||
static let actionViewController = Logger(subsystem: subsystem, category: "actionViewController")
|
||||
static let shareCapabilities = Logger(subsystem: subsystem, category: "shareCapabilities")
|
||||
static let shareController = Logger(subsystem: subsystem, category: "shareController")
|
||||
static let shareeDataSource = Logger(subsystem: subsystem, category: "shareeDataSource")
|
||||
static let sharesDataSource = Logger(subsystem: subsystem, category: "sharesDataSource")
|
||||
static let shareOptionsView = Logger(subsystem: subsystem, category: "shareOptionsView")
|
||||
static let shareViewController = Logger(subsystem: subsystem, category: "shareViewController")
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
//
|
||||
// NKShare+Extensions.swift
|
||||
// FileProviderUIExt
|
||||
//
|
||||
// Created by Claudio Cambra on 28/2/24.
|
||||
//
|
||||
|
||||
import AppKit
|
||||
import NextcloudKit
|
||||
|
||||
extension NKShare {
|
||||
enum ShareType: Int {
|
||||
case user = 0
|
||||
case group = 1
|
||||
case publicLink = 3
|
||||
case email = 4
|
||||
case federatedCloud = 6
|
||||
case circle = 7
|
||||
case talkConversation = 10
|
||||
}
|
||||
|
||||
enum PermissionValues: Int {
|
||||
case readShare = 1
|
||||
case updateShare = 2
|
||||
case createShare = 4
|
||||
case deleteShare = 8
|
||||
case shareShare = 16
|
||||
case all = 31
|
||||
}
|
||||
|
||||
var typeImage: NSImage? {
|
||||
var image: NSImage?
|
||||
switch shareType {
|
||||
case ShareType.user.rawValue:
|
||||
image = NSImage(
|
||||
systemSymbolName: "person.circle.fill",
|
||||
accessibilityDescription: "User share icon"
|
||||
)
|
||||
case ShareType.group.rawValue:
|
||||
image = NSImage(
|
||||
systemSymbolName: "person.2.circle.fill",
|
||||
accessibilityDescription: "Group share icon"
|
||||
)
|
||||
case ShareType.publicLink.rawValue:
|
||||
image = NSImage(
|
||||
systemSymbolName: "link.circle.fill",
|
||||
accessibilityDescription: "Public link share icon"
|
||||
)
|
||||
case ShareType.email.rawValue:
|
||||
image = NSImage(
|
||||
systemSymbolName: "envelope.circle.fill",
|
||||
accessibilityDescription: "Email share icon"
|
||||
)
|
||||
case ShareType.federatedCloud.rawValue:
|
||||
image = NSImage(
|
||||
systemSymbolName: "cloud.circle.fill",
|
||||
accessibilityDescription: "Federated cloud share icon"
|
||||
)
|
||||
case ShareType.circle.rawValue:
|
||||
image = NSImage(
|
||||
systemSymbolName: "circle.circle.fill",
|
||||
accessibilityDescription: "Circle share icon"
|
||||
)
|
||||
case ShareType.talkConversation.rawValue:
|
||||
image = NSImage(
|
||||
systemSymbolName: "message.circle.fill",
|
||||
accessibilityDescription: "Talk conversation share icon"
|
||||
)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
var config = NSImage.SymbolConfiguration(textStyle: .body, scale: .large)
|
||||
if #available(macOS 12.0, *) {
|
||||
config = config.applying(
|
||||
.init(paletteColors: [.controlBackgroundColor, .controlAccentColor])
|
||||
)
|
||||
}
|
||||
return image?.withSymbolConfiguration(config)
|
||||
}
|
||||
|
||||
var displayString: String {
|
||||
if label != "" {
|
||||
return label
|
||||
}
|
||||
|
||||
switch shareType {
|
||||
case ShareType.user.rawValue:
|
||||
return "User share (\(shareWith))"
|
||||
case ShareType.group.rawValue:
|
||||
return "Group share (\(shareWith))"
|
||||
case ShareType.publicLink.rawValue:
|
||||
return "Public link share"
|
||||
case ShareType.email.rawValue:
|
||||
return "Email share (\(shareWith))"
|
||||
case ShareType.federatedCloud.rawValue:
|
||||
return "Federated cloud share (\(shareWith))"
|
||||
case ShareType.circle.rawValue:
|
||||
return "Circle share (\(shareWith))"
|
||||
case ShareType.talkConversation.rawValue:
|
||||
return "Talk conversation share (\(shareWith))"
|
||||
default:
|
||||
return "Unknown share"
|
||||
}
|
||||
}
|
||||
|
||||
var expirationDateString: String? {
|
||||
guard let date = expirationDate else { return nil }
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = "YYYY-MM-dd HH:mm:ss"
|
||||
return dateFormatter.string(from: date as Date)
|
||||
}
|
||||
|
||||
var shareesCanEdit: Bool {
|
||||
get { (permissions & PermissionValues.updateShare.rawValue) != 0 }
|
||||
set {
|
||||
if newValue {
|
||||
permissions |= NKShare.PermissionValues.updateShare.rawValue
|
||||
} else {
|
||||
permissions &= ~NKShare.PermissionValues.updateShare.rawValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func formattedDateString(date: Date) -> String {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = "YYYY-MM-dd HH:mm:ss"
|
||||
return dateFormatter.string(from: date)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?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>$(OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX)$(OC_APPLICATION_REV_DOMAIN)</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
|
@ -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.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>$(OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX)$(OC_APPLICATION_REV_DOMAIN)</string>
|
||||
</array>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,24 @@
|
|||
<?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>NSExtensionFileProviderActions</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSExtensionFileProviderActionActivationRule</key>
|
||||
<string>TRUEPREDICATE</string>
|
||||
<key>NSExtensionFileProviderActionIdentifier</key>
|
||||
<string>com.nextcloud.desktopclient.FileProviderUIExt.ShareAction</string>
|
||||
<key>NSExtensionFileProviderActionName</key>
|
||||
<string>Share options</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>NSExtensionPrincipalClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).DocumentActionViewController</string>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.fileprovider-actionsui</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,131 @@
|
|||
//
|
||||
// ShareController.swift
|
||||
// FileProviderUIExt
|
||||
//
|
||||
// Created by Claudio Cambra on 4/3/24.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
import NextcloudKit
|
||||
import OSLog
|
||||
|
||||
class ShareController: ObservableObject {
|
||||
@Published private(set) var share: NKShare
|
||||
private let kit: NextcloudKit
|
||||
|
||||
static func create(
|
||||
kit: NextcloudKit,
|
||||
shareType: NKShare.ShareType,
|
||||
itemServerRelativePath: String,
|
||||
shareWith: String?,
|
||||
password: String? = nil,
|
||||
expireDate: String? = nil,
|
||||
permissions: Int = 1,
|
||||
publicUpload: Bool = false,
|
||||
note: String? = nil,
|
||||
label: String? = nil,
|
||||
hideDownload: Bool,
|
||||
attributes: String? = nil,
|
||||
options: NKRequestOptions = NKRequestOptions()
|
||||
) async -> NKError? {
|
||||
Logger.shareController.info("Creating share: \(itemServerRelativePath)")
|
||||
return await withCheckedContinuation { continuation in
|
||||
if shareType == .publicLink {
|
||||
kit.createShareLink(
|
||||
path: itemServerRelativePath,
|
||||
hideDownload: hideDownload,
|
||||
publicUpload: publicUpload,
|
||||
password: password,
|
||||
permissions: permissions,
|
||||
options: options
|
||||
) { account, share, data, error in
|
||||
defer { continuation.resume(returning: error) }
|
||||
guard error == .success else {
|
||||
Logger.shareController.error("Error creating link share: \(error)")
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
guard let shareWith = shareWith else {
|
||||
let errorString = "No recipient for share!"
|
||||
Logger.shareController.error("\(errorString)")
|
||||
let error = NKError(statusCode: 0, fallbackDescription: errorString)
|
||||
continuation.resume(returning: error)
|
||||
return
|
||||
}
|
||||
|
||||
kit.createShare(
|
||||
path: itemServerRelativePath,
|
||||
shareType: shareType.rawValue,
|
||||
shareWith: shareWith,
|
||||
password: password,
|
||||
permissions: permissions,
|
||||
options: options,
|
||||
attributes: attributes
|
||||
) { account, share, data, error in
|
||||
defer { continuation.resume(returning: error) }
|
||||
guard error == .success else {
|
||||
Logger.shareController.error("Error creating share: \(error)")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(share: NKShare, kit: NextcloudKit) {
|
||||
self.share = share
|
||||
self.kit = kit
|
||||
}
|
||||
|
||||
func save(
|
||||
password: String? = nil,
|
||||
expireDate: String? = nil,
|
||||
permissions: Int = 1,
|
||||
publicUpload: Bool = false,
|
||||
note: String? = nil,
|
||||
label: String? = nil,
|
||||
hideDownload: Bool,
|
||||
attributes: String? = nil,
|
||||
options: NKRequestOptions = NKRequestOptions()
|
||||
) async -> NKError? {
|
||||
Logger.shareController.info("Saving share: \(self.share.url)")
|
||||
return await withCheckedContinuation { continuation in
|
||||
kit.updateShare(
|
||||
idShare: share.idShare,
|
||||
password: password,
|
||||
expireDate: expireDate,
|
||||
permissions: permissions,
|
||||
publicUpload: publicUpload,
|
||||
note: note,
|
||||
label: label,
|
||||
hideDownload: hideDownload,
|
||||
attributes: attributes,
|
||||
options: options
|
||||
) { account, share, data, error in
|
||||
Logger.shareController.info("Received update response: \(share?.url ?? "")")
|
||||
defer { continuation.resume(returning: error) }
|
||||
guard error == .success, let share = share else {
|
||||
Logger.shareController.error("Error updating save: \(error.errorDescription)")
|
||||
return
|
||||
}
|
||||
self.share = share
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func delete() async -> NKError? {
|
||||
Logger.shareController.info("Deleting share: \(self.share.url)")
|
||||
return await withCheckedContinuation { continuation in
|
||||
kit.deleteShare(idShare: share.idShare) { account, error in
|
||||
Logger.shareController.info("Received delete response: \(self.share.url)")
|
||||
defer { continuation.resume(returning: error) }
|
||||
guard error == .success else {
|
||||
Logger.shareController.error("Error deleting save: \(error.errorDescription)")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,353 @@
|
|||
//
|
||||
// ShareOptionsView.swift
|
||||
// FileProviderUIExt
|
||||
//
|
||||
// Created by Claudio Cambra on 28/2/24.
|
||||
//
|
||||
|
||||
import AppKit
|
||||
import Combine
|
||||
import NextcloudKit
|
||||
import OSLog
|
||||
import SuggestionsTextFieldKit
|
||||
|
||||
class ShareOptionsView: NSView {
|
||||
@IBOutlet private weak var optionsTitleTextField: NSTextField!
|
||||
@IBOutlet private weak var shareRecipientTextField: NSTextField! // Hide if public link share
|
||||
@IBOutlet private weak var labelTextField: NSTextField!
|
||||
@IBOutlet private weak var uploadEditPermissionCheckbox: NSButton!
|
||||
@IBOutlet private weak var hideDownloadCheckbox: NSButton!
|
||||
@IBOutlet private weak var passwordProtectCheckbox: NSButton!
|
||||
@IBOutlet private weak var passwordSecureField: NSSecureTextField!
|
||||
@IBOutlet private weak var expirationDateCheckbox: NSButton!
|
||||
@IBOutlet private weak var expirationDatePicker: NSDatePicker!
|
||||
@IBOutlet private weak var noteForRecipientCheckbox: NSButton!
|
||||
@IBOutlet private weak var noteTextField: NSTextField!
|
||||
@IBOutlet private weak var saveButton: NSButton!
|
||||
@IBOutlet private weak var deleteButton: NSButton!
|
||||
@IBOutlet private weak var shareTypePicker: NSPopUpButton!
|
||||
@IBOutlet private weak var publicLinkShareMenuItem: NSMenuItem!
|
||||
@IBOutlet private weak var userShareMenuItem: NSMenuItem!
|
||||
@IBOutlet private weak var groupShareMenuItem: NSMenuItem!
|
||||
@IBOutlet private weak var emailShareMenuItem: NSMenuItem!
|
||||
@IBOutlet private weak var federatedCloudShareMenuItem: NSMenuItem!
|
||||
@IBOutlet private weak var circleShare: NSMenuItem!
|
||||
@IBOutlet private weak var talkConversationShare: NSMenuItem!
|
||||
|
||||
var kit: NextcloudKit? {
|
||||
didSet {
|
||||
Logger.shareOptionsView.info("Setting up the kit.")
|
||||
guard let kit = kit else {
|
||||
Logger.shareOptionsView.error("Could not configure suggestions data source.")
|
||||
return
|
||||
}
|
||||
|
||||
suggestionsTextFieldDelegate.suggestionsDataSource = ShareeSuggestionsDataSource(
|
||||
kit: kit
|
||||
)
|
||||
suggestionsTextFieldDelegate.confirmationHandler = { suggestion in
|
||||
guard let sharee = suggestion?.data as? NKSharee else { return }
|
||||
self.shareRecipientTextField.stringValue = sharee.shareWith
|
||||
Logger.shareOptionsView.debug("Chose sharee \(sharee.shareWith, privacy: .public)")
|
||||
}
|
||||
suggestionsTextFieldDelegate.targetTextField = shareRecipientTextField
|
||||
}
|
||||
}
|
||||
var dataSource: ShareTableViewDataSource?
|
||||
var controller: ShareController? {
|
||||
didSet {
|
||||
guard controller != nil else { return }
|
||||
optionsTitleTextField.stringValue = "Share options"
|
||||
deleteButton.title = "Delete"
|
||||
deleteButton.image = NSImage(
|
||||
systemSymbolName: "trash", accessibilityDescription: "Delete trash icon"
|
||||
)
|
||||
deleteButton.bezelColor = NSColor.systemRed
|
||||
cancellable?.cancel()
|
||||
createMode = false
|
||||
update()
|
||||
cancellable = controller.publisher.sink { _ in self.update() }
|
||||
}
|
||||
}
|
||||
var createMode = false {
|
||||
didSet {
|
||||
Logger.shareOptionsView.info("Create mode set: \(self.createMode)")
|
||||
shareTypePicker.isHidden = !createMode
|
||||
shareRecipientTextField.isHidden = !createMode
|
||||
labelTextField.isHidden = createMode // Cannot set label on create API call
|
||||
guard createMode else { return }
|
||||
optionsTitleTextField.stringValue = "Create new share"
|
||||
deleteButton.title = "Cancel"
|
||||
deleteButton.image = NSImage(
|
||||
systemSymbolName: "xmark.bin", accessibilityDescription: "Cancel create icon"
|
||||
)
|
||||
deleteButton.bezelColor = NSColor.controlColor
|
||||
cancellable?.cancel()
|
||||
cancellable = nil
|
||||
controller = nil
|
||||
reset()
|
||||
setupCreateForm()
|
||||
}
|
||||
}
|
||||
private var cancellable: AnyCancellable?
|
||||
private var suggestionsWindowController = SuggestionsWindowController()
|
||||
private var suggestionsTextFieldDelegate = SuggestionsTextFieldDelegate()
|
||||
|
||||
private func update() {
|
||||
guard let share = controller?.share else {
|
||||
reset()
|
||||
setAllFields(enabled: false)
|
||||
saveButton.isEnabled = false
|
||||
deleteButton.isEnabled = false
|
||||
return
|
||||
}
|
||||
|
||||
deleteButton.isEnabled = share.canDelete
|
||||
saveButton.isEnabled = share.canEdit
|
||||
|
||||
if share.canEdit {
|
||||
setAllFields(enabled: true)
|
||||
labelTextField.stringValue = share.label
|
||||
uploadEditPermissionCheckbox.state = share.shareesCanEdit ? .on : .off
|
||||
hideDownloadCheckbox.state = share.hideDownload ? .on : .off
|
||||
passwordProtectCheckbox.state = share.password.isEmpty ? .off : .on
|
||||
passwordSecureField.isHidden = passwordProtectCheckbox.state == .off
|
||||
expirationDateCheckbox.state = share.expirationDate == nil ? .off : .on
|
||||
expirationDatePicker.isHidden = expirationDateCheckbox.state == .off
|
||||
noteForRecipientCheckbox.state = share.note.isEmpty ? .off : .on
|
||||
noteTextField.isHidden = noteForRecipientCheckbox.state == .off
|
||||
} else {
|
||||
setAllFields(enabled: false)
|
||||
reset()
|
||||
}
|
||||
}
|
||||
|
||||
private func reset() {
|
||||
shareRecipientTextField.stringValue = ""
|
||||
labelTextField.stringValue = ""
|
||||
uploadEditPermissionCheckbox.state = .off
|
||||
hideDownloadCheckbox.state = .off
|
||||
passwordProtectCheckbox.state = .off
|
||||
passwordSecureField.isHidden = true
|
||||
passwordSecureField.stringValue = ""
|
||||
expirationDateCheckbox.state = .off
|
||||
expirationDatePicker.isHidden = true
|
||||
expirationDatePicker.dateValue = NSDate.now
|
||||
expirationDatePicker.minDate = NSDate.now
|
||||
expirationDatePicker.maxDate = nil
|
||||
noteForRecipientCheckbox.state = .off
|
||||
noteTextField.isHidden = true
|
||||
noteTextField.stringValue = ""
|
||||
}
|
||||
|
||||
private func setupCreateForm() {
|
||||
guard createMode else { return }
|
||||
|
||||
setAllFields(enabled: true)
|
||||
|
||||
let type = pickedShareType()
|
||||
shareRecipientTextField.isHidden = type == .publicLink
|
||||
|
||||
if let caps = dataSource?.capabilities?.filesSharing {
|
||||
uploadEditPermissionCheckbox.state =
|
||||
caps.defaultPermissions & NKShare.PermissionValues.updateShare.rawValue != 0
|
||||
? .on : .off
|
||||
|
||||
switch type {
|
||||
case .publicLink:
|
||||
passwordProtectCheckbox.isHidden = false
|
||||
passwordProtectCheckbox.state = caps.publicLink?.passwordEnforced == true ? .on : .off
|
||||
passwordProtectCheckbox.isEnabled = caps.publicLink?.passwordEnforced == false
|
||||
expirationDateCheckbox.state = caps.publicLink?.expireDateEnforced == true ? .on : .off
|
||||
expirationDateCheckbox.isEnabled = caps.publicLink?.expireDateEnforced == false
|
||||
expirationDatePicker.dateValue = Date(
|
||||
timeIntervalSinceNow:
|
||||
TimeInterval((caps.publicLink?.expireDateDays ?? 1) * 24 * 60 * 60)
|
||||
)
|
||||
if caps.publicLink?.expireDateEnforced == true {
|
||||
expirationDatePicker.maxDate = expirationDatePicker.dateValue
|
||||
}
|
||||
case .email:
|
||||
passwordProtectCheckbox.isHidden = caps.email?.passwordEnabled == false
|
||||
passwordProtectCheckbox.state = caps.email?.passwordEnforced == true ? .on : .off
|
||||
default:
|
||||
passwordProtectCheckbox.isHidden = true
|
||||
passwordProtectCheckbox.state = .off
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
passwordSecureField.isHidden = passwordProtectCheckbox.state == .off
|
||||
expirationDatePicker.isHidden = expirationDateCheckbox.state == .off
|
||||
}
|
||||
|
||||
private func setAllFields(enabled: Bool) {
|
||||
shareTypePicker.isEnabled = enabled
|
||||
shareRecipientTextField.isEnabled = enabled
|
||||
labelTextField.isEnabled = enabled
|
||||
uploadEditPermissionCheckbox.isEnabled = enabled
|
||||
hideDownloadCheckbox.isEnabled = enabled
|
||||
passwordProtectCheckbox.isEnabled = enabled
|
||||
passwordSecureField.isEnabled = enabled
|
||||
expirationDateCheckbox.isEnabled = enabled
|
||||
expirationDatePicker.isEnabled = enabled
|
||||
noteForRecipientCheckbox.isEnabled = enabled
|
||||
noteTextField.isEnabled = enabled
|
||||
saveButton.isEnabled = enabled
|
||||
deleteButton.isEnabled = enabled
|
||||
}
|
||||
|
||||
private func pickedShareType() -> NKShare.ShareType {
|
||||
let selectedShareTypeItem = shareTypePicker.selectedItem
|
||||
var selectedShareType = NKShare.ShareType.publicLink
|
||||
if selectedShareTypeItem == publicLinkShareMenuItem {
|
||||
selectedShareType = .publicLink
|
||||
} else if selectedShareTypeItem == userShareMenuItem {
|
||||
selectedShareType = .user
|
||||
} else if selectedShareTypeItem == groupShareMenuItem {
|
||||
selectedShareType = .group
|
||||
} else if selectedShareTypeItem == emailShareMenuItem {
|
||||
selectedShareType = .email
|
||||
} else if selectedShareTypeItem == federatedCloudShareMenuItem {
|
||||
selectedShareType = .federatedCloud
|
||||
} else if selectedShareTypeItem == circleShare {
|
||||
selectedShareType = .circle
|
||||
} else if selectedShareTypeItem == talkConversationShare {
|
||||
selectedShareType = .talkConversation
|
||||
}
|
||||
return selectedShareType
|
||||
}
|
||||
|
||||
@IBAction func shareTypePickerAction(_ sender: Any) {
|
||||
if createMode {
|
||||
setupCreateForm()
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func passwordCheckboxAction(_ sender: Any) {
|
||||
passwordSecureField.isHidden = passwordProtectCheckbox.state == .off
|
||||
}
|
||||
|
||||
@IBAction func expirationDateCheckboxAction(_ sender: Any) {
|
||||
expirationDatePicker.isHidden = expirationDateCheckbox.state == .off
|
||||
}
|
||||
|
||||
@IBAction func noteForRecipientCheckboxAction(_ sender: Any) {
|
||||
noteTextField.isHidden = noteForRecipientCheckbox.state == .off
|
||||
}
|
||||
|
||||
@IBAction func save(_ sender: Any) {
|
||||
Task { @MainActor in
|
||||
let password = passwordProtectCheckbox.state == .on
|
||||
? passwordSecureField.stringValue
|
||||
: ""
|
||||
let expireDate = expirationDateCheckbox.state == .on
|
||||
? NKShare.formattedDateString(date: expirationDatePicker.dateValue)
|
||||
: ""
|
||||
let note = noteForRecipientCheckbox.state == .on
|
||||
? noteTextField.stringValue
|
||||
: ""
|
||||
let label = labelTextField.stringValue
|
||||
let hideDownload = hideDownloadCheckbox.state == .on
|
||||
let uploadAndEdit = uploadEditPermissionCheckbox.state == .on
|
||||
|
||||
guard !createMode else {
|
||||
Logger.shareOptionsView.info("Creating new share!")
|
||||
|
||||
guard let dataSource = dataSource,
|
||||
let kit = kit,
|
||||
let itemServerRelativePath = dataSource.itemServerRelativePath
|
||||
else {
|
||||
Logger.shareOptionsView.error("Cannot create new share due to missing data.")
|
||||
Logger.shareOptionsView.error("dataSource: \(self.dataSource)")
|
||||
Logger.shareOptionsView.error("kit: \(self.kit)")
|
||||
Logger.shareOptionsView.error(
|
||||
"path: \(self.dataSource?.itemServerRelativePath ?? "")"
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
let selectedShareType = pickedShareType()
|
||||
let shareWith = shareRecipientTextField.stringValue
|
||||
|
||||
var permissions = NKShare.PermissionValues.all.rawValue
|
||||
permissions = uploadAndEdit
|
||||
? permissions | NKShare.PermissionValues.updateShare.rawValue
|
||||
: permissions & ~NKShare.PermissionValues.updateShare.rawValue
|
||||
|
||||
setAllFields(enabled: false)
|
||||
deleteButton.isEnabled = false
|
||||
saveButton.isEnabled = false
|
||||
let error = await ShareController.create(
|
||||
kit: kit,
|
||||
shareType: selectedShareType,
|
||||
itemServerRelativePath: itemServerRelativePath,
|
||||
shareWith: shareWith,
|
||||
password: password,
|
||||
expireDate: expireDate,
|
||||
permissions: permissions,
|
||||
note: note,
|
||||
label: label,
|
||||
hideDownload: hideDownload
|
||||
)
|
||||
if let error = error, error != .success {
|
||||
dataSource.uiDelegate?.showError("Error creating: \(error.errorDescription)")
|
||||
setAllFields(enabled: true)
|
||||
} else {
|
||||
dataSource.uiDelegate?.hideOptions(self)
|
||||
await dataSource.reload()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
Logger.shareOptionsView.info("Editing existing share!")
|
||||
|
||||
guard let controller = controller else {
|
||||
Logger.shareOptionsView.error("No valid share controller, cannot edit share.")
|
||||
return
|
||||
}
|
||||
let share = controller.share
|
||||
let permissions = uploadAndEdit
|
||||
? share.permissions | NKShare.PermissionValues.updateShare.rawValue
|
||||
: share.permissions & ~NKShare.PermissionValues.updateShare.rawValue
|
||||
|
||||
setAllFields(enabled: false)
|
||||
deleteButton.isEnabled = false
|
||||
saveButton.isEnabled = false
|
||||
let error = await controller.save(
|
||||
password: password,
|
||||
expireDate: expireDate,
|
||||
permissions: permissions,
|
||||
note: note,
|
||||
label: label,
|
||||
hideDownload: hideDownload
|
||||
)
|
||||
if let error = error, error != .success {
|
||||
dataSource?.uiDelegate?.showError("Error updating share: \(error.errorDescription)")
|
||||
setAllFields(enabled: true)
|
||||
} else {
|
||||
dataSource?.uiDelegate?.hideOptions(self)
|
||||
await dataSource?.reload()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func delete(_ sender: Any) {
|
||||
Task { @MainActor in
|
||||
guard !createMode else {
|
||||
dataSource?.uiDelegate?.hideOptions(self)
|
||||
reset()
|
||||
return
|
||||
}
|
||||
|
||||
setAllFields(enabled: false)
|
||||
deleteButton.isEnabled = false
|
||||
saveButton.isEnabled = false
|
||||
let error = await controller?.delete()
|
||||
if let error = error, error != .success {
|
||||
dataSource?.uiDelegate?.showError("Error deleting share: \(error.errorDescription)")
|
||||
}
|
||||
await dataSource?.reload()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
//
|
||||
// ShareTableItemView.swift
|
||||
// FileProviderUIExt
|
||||
//
|
||||
// Created by Claudio Cambra on 28/2/24.
|
||||
//
|
||||
|
||||
import AppKit
|
||||
import NextcloudKit
|
||||
|
||||
class ShareTableItemView: NSTableCellView {
|
||||
@IBOutlet private weak var typeImageView: NSImageView!
|
||||
@IBOutlet private weak var label: NSTextField!
|
||||
@IBOutlet private weak var copyLinkButton: NSButton!
|
||||
private var originalCopyImage: NSImage?
|
||||
private var copiedButtonImage: NSImage?
|
||||
private var tempButtonTimer: Timer?
|
||||
|
||||
var share: NKShare? {
|
||||
didSet {
|
||||
guard let share = share else {
|
||||
prepareForReuse()
|
||||
return
|
||||
}
|
||||
typeImageView.image = share.typeImage
|
||||
label.stringValue = share.displayString
|
||||
copyLinkButton.isHidden = share.shareType != NKShare.ShareType.publicLink.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
override func prepareForReuse() {
|
||||
typeImageView.image = nil
|
||||
label.stringValue = ""
|
||||
copyLinkButton.isHidden = false
|
||||
super.prepareForReuse()
|
||||
}
|
||||
|
||||
@IBAction func copyShareLink(sender: Any) {
|
||||
guard let share = share else { return }
|
||||
let pasteboard = NSPasteboard.general
|
||||
pasteboard.declareTypes([.string], owner: nil)
|
||||
pasteboard.setString(share.url, forType: .string)
|
||||
|
||||
guard tempButtonTimer == nil else { return }
|
||||
|
||||
originalCopyImage = copyLinkButton.image
|
||||
copiedButtonImage = NSImage(
|
||||
systemSymbolName: "checkmark.circle.fill",
|
||||
accessibilityDescription: "Public link has been copied icon"
|
||||
)
|
||||
var config = NSImage.SymbolConfiguration(scale: .medium)
|
||||
if #available(macOS 12.0, *) {
|
||||
config = config.applying(.init(hierarchicalColor: .systemGreen))
|
||||
}
|
||||
copiedButtonImage = copiedButtonImage?.withSymbolConfiguration(config)
|
||||
copyLinkButton.image = copiedButtonImage
|
||||
tempButtonTimer = Timer.scheduledTimer(withTimeInterval: 3.0, repeats: false) { timer in
|
||||
self.copyLinkButton.image = self.originalCopyImage
|
||||
self.copiedButtonImage = nil
|
||||
self.tempButtonTimer?.invalidate()
|
||||
self.tempButtonTimer = nil
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22505" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22505"/>
|
||||
<capability name="Image references" minToolsVersion="12.0"/>
|
||||
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner"/>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<tableCellView id="WWf-Il-fKw" customClass="ShareTableItemView" customModule="FileProviderUIExt" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="322" height="42"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<stackView distribution="fill" orientation="horizontal" alignment="top" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wdu-Dj-FUn">
|
||||
<rect key="frame" x="5" y="5" width="312" height="32"/>
|
||||
<subviews>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Ex8-CB-iNc">
|
||||
<rect key="frame" x="0.0" y="-5" width="32" height="43"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="Ex8-CB-iNc" secondAttribute="height" multiplier="1:1" id="5ak-dc-HzR"/>
|
||||
<constraint firstAttribute="height" constant="32" id="Cus-qI-uen"/>
|
||||
</constraints>
|
||||
<imageCell key="cell" controlSize="large" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" id="0Xf-Au-QjJ">
|
||||
<imageReference key="image" image="link.circle.fill" catalog="system" symbolScale="large"/>
|
||||
</imageCell>
|
||||
<color key="contentTintColor" name="AccentColor"/>
|
||||
</imageView>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="MwZ-er-vB4">
|
||||
<rect key="frame" x="38" y="8" width="236" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="left" title="Share link" id="Bcz-ws-5yW">
|
||||
<font key="font" metaFont="systemMedium" size="13"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button horizontalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="y6i-wm-BtQ">
|
||||
<rect key="frame" x="280" y="0.0" width="32" height="32"/>
|
||||
<buttonCell key="cell" type="inline" title="Copy share link" bezelStyle="inline" image="doc.on.doc.fill" catalog="system" imagePosition="only" alignment="center" controlSize="large" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="ram-fe-8Pt">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="smallSystemBold"/>
|
||||
</buttonCell>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="y6i-wm-BtQ" secondAttribute="height" multiplier="1:1" id="BlJ-WU-1y5"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<action selector="copyShareLinkWithSender:" target="WWf-Il-fKw" id="dgW-8v-wfd"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="Ex8-CB-iNc" firstAttribute="top" secondItem="wdu-Dj-FUn" secondAttribute="top" id="2x8-EU-gHs"/>
|
||||
<constraint firstItem="y6i-wm-BtQ" firstAttribute="top" secondItem="wdu-Dj-FUn" secondAttribute="top" id="BjV-lg-nyL"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Ex8-CB-iNc" secondAttribute="bottom" id="dd0-Vh-Pbi"/>
|
||||
<constraint firstAttribute="bottom" secondItem="y6i-wm-BtQ" secondAttribute="bottom" id="ha0-oR-OxV"/>
|
||||
<constraint firstItem="MwZ-er-vB4" firstAttribute="centerY" secondItem="Ex8-CB-iNc" secondAttribute="centerY" id="m3A-Tu-qMJ"/>
|
||||
<constraint firstItem="Ex8-CB-iNc" firstAttribute="leading" secondItem="wdu-Dj-FUn" secondAttribute="leading" id="o1t-TB-uhe"/>
|
||||
</constraints>
|
||||
<visibilityPriorities>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
</visibilityPriorities>
|
||||
<customSpacing>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="wdu-Dj-FUn" firstAttribute="top" secondItem="WWf-Il-fKw" secondAttribute="top" constant="5" id="HIY-8X-dM2"/>
|
||||
<constraint firstItem="wdu-Dj-FUn" firstAttribute="leading" secondItem="WWf-Il-fKw" secondAttribute="leading" constant="5" id="g7a-hl-t5g"/>
|
||||
<constraint firstAttribute="bottom" secondItem="wdu-Dj-FUn" secondAttribute="bottom" constant="5" id="gAZ-x8-C4K"/>
|
||||
<constraint firstAttribute="trailing" secondItem="wdu-Dj-FUn" secondAttribute="trailing" constant="5" id="grc-5X-tMi"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="copyLinkButton" destination="y6i-wm-BtQ" id="8bc-yO-Txo"/>
|
||||
<outlet property="label" destination="MwZ-er-vB4" id="Gba-jf-C8H"/>
|
||||
<outlet property="typeImageView" destination="Ex8-CB-iNc" id="T7o-zd-qOo"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="128" y="-22"/>
|
||||
</tableCellView>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="doc.on.doc.fill" catalog="system" width="17" height="19"/>
|
||||
<image name="link.circle.fill" catalog="system" width="20" height="20"/>
|
||||
<namedColor name="AccentColor">
|
||||
<color red="0.0" green="0.46000000000000002" blue="0.89000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</namedColor>
|
||||
</resources>
|
||||
</document>
|
|
@ -0,0 +1,254 @@
|
|||
//
|
||||
// ShareTableViewDataSource.swift
|
||||
// FileProviderUIExt
|
||||
//
|
||||
// Created by Claudio Cambra on 27/2/24.
|
||||
//
|
||||
|
||||
import AppKit
|
||||
import FileProvider
|
||||
import NextcloudKit
|
||||
import NextcloudCapabilitiesKit
|
||||
import OSLog
|
||||
|
||||
class ShareTableViewDataSource: NSObject, NSTableViewDataSource, NSTableViewDelegate {
|
||||
private let shareItemViewIdentifier = NSUserInterfaceItemIdentifier("ShareTableItemView")
|
||||
private let shareItemViewNib = NSNib(nibNamed: "ShareTableItemView", bundle: nil)
|
||||
private let reattemptInterval: TimeInterval = 3.0
|
||||
|
||||
var uiDelegate: ShareViewDataSourceUIDelegate?
|
||||
var sharesTableView: NSTableView? {
|
||||
didSet {
|
||||
sharesTableView?.register(shareItemViewNib, forIdentifier: shareItemViewIdentifier)
|
||||
sharesTableView?.rowHeight = 42.0 // Height of view in ShareTableItemView XIB
|
||||
sharesTableView?.dataSource = self
|
||||
sharesTableView?.delegate = self
|
||||
sharesTableView?.reloadData()
|
||||
}
|
||||
}
|
||||
var capabilities: Capabilities?
|
||||
var itemMetadata: NKFile?
|
||||
|
||||
private(set) var kit: NextcloudKit?
|
||||
private(set) var itemURL: URL?
|
||||
private(set) var itemServerRelativePath: String?
|
||||
private(set) var shares: [NKShare] = [] {
|
||||
didSet { Task { @MainActor in sharesTableView?.reloadData() } }
|
||||
}
|
||||
private var account: NextcloudAccount? {
|
||||
didSet {
|
||||
guard let account = account else { return }
|
||||
kit = NextcloudKit()
|
||||
kit?.setup(
|
||||
user: account.username,
|
||||
userId: account.username,
|
||||
password: account.password,
|
||||
urlBase: account.serverUrl
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func loadItem(url: URL) {
|
||||
itemServerRelativePath = nil
|
||||
itemURL = url
|
||||
Task {
|
||||
await reload()
|
||||
}
|
||||
}
|
||||
|
||||
func reattempt() {
|
||||
DispatchQueue.main.async {
|
||||
Timer.scheduledTimer(withTimeInterval: self.reattemptInterval, repeats: false) { _ in
|
||||
Task { await self.reload() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func reload() async {
|
||||
guard let itemURL = itemURL else { return }
|
||||
guard let itemIdentifier = await withCheckedContinuation({
|
||||
(continuation: CheckedContinuation<NSFileProviderItemIdentifier?, Never>) -> Void in
|
||||
NSFileProviderManager.getIdentifierForUserVisibleFile(
|
||||
at: itemURL
|
||||
) { identifier, domainIdentifier, error in
|
||||
defer { continuation.resume(returning: identifier) }
|
||||
guard error == nil else {
|
||||
self.presentError("No item with identifier: \(error.debugDescription)")
|
||||
return
|
||||
}
|
||||
}
|
||||
}) else {
|
||||
presentError("Could not get identifier for item, no shares can be acquired.")
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
let connection = try await serviceConnection(url: itemURL)
|
||||
guard let serverPath = await connection.itemServerPath(identifier: itemIdentifier),
|
||||
let credentials = await connection.credentials() as? Dictionary<String, String>,
|
||||
let convertedAccount = NextcloudAccount(dictionary: credentials),
|
||||
!convertedAccount.password.isEmpty
|
||||
else {
|
||||
presentError("Failed to get details from File Provider Extension. Retrying.")
|
||||
reattempt()
|
||||
return
|
||||
}
|
||||
let serverPathString = serverPath as String
|
||||
itemServerRelativePath = serverPathString
|
||||
account = convertedAccount
|
||||
await sharesTableView?.deselectAll(self)
|
||||
capabilities = await fetchCapabilities()
|
||||
guard capabilities?.filesSharing?.apiEnabled == true else {
|
||||
presentError("Server does not support shares.")
|
||||
return
|
||||
}
|
||||
itemMetadata = await fetchItemMetadata(itemRelativePath: serverPathString)
|
||||
guard itemMetadata?.permissions.contains("R") == true else {
|
||||
presentError("This file cannot be shared.")
|
||||
return
|
||||
}
|
||||
shares = await fetch(
|
||||
itemIdentifier: itemIdentifier, itemRelativePath: serverPathString
|
||||
)
|
||||
} catch let error {
|
||||
presentError("Could not reload data: \(error), will try again.")
|
||||
reattempt()
|
||||
}
|
||||
}
|
||||
|
||||
private func serviceConnection(url: URL) async throws -> FPUIExtensionService {
|
||||
let services = try await FileManager().fileProviderServicesForItem(at: url)
|
||||
guard let service = services[fpUiExtensionServiceName] else {
|
||||
Logger.sharesDataSource.error("Couldn't get service, required service not present")
|
||||
throw NSFileProviderError(.providerNotFound)
|
||||
}
|
||||
let connection: NSXPCConnection
|
||||
connection = try await service.fileProviderConnection()
|
||||
connection.remoteObjectInterface = NSXPCInterface(with: FPUIExtensionService.self)
|
||||
connection.interruptionHandler = {
|
||||
Logger.sharesDataSource.error("Service connection interrupted")
|
||||
}
|
||||
connection.resume()
|
||||
guard let proxy = connection.remoteObjectProxy as? FPUIExtensionService else {
|
||||
throw NSFileProviderError(.serverUnreachable)
|
||||
}
|
||||
return proxy
|
||||
}
|
||||
|
||||
private func fetch(
|
||||
itemIdentifier: NSFileProviderItemIdentifier, itemRelativePath: String
|
||||
) async -> [NKShare] {
|
||||
Task { @MainActor in uiDelegate?.fetchStarted() }
|
||||
defer { Task { @MainActor in uiDelegate?.fetchFinished() } }
|
||||
|
||||
let rawIdentifier = itemIdentifier.rawValue
|
||||
Logger.sharesDataSource.info("Fetching shares for item \(rawIdentifier, privacy: .public)")
|
||||
|
||||
guard let kit = kit else {
|
||||
self.presentError("NextcloudKit instance is unavailable, cannot fetch shares!")
|
||||
return []
|
||||
}
|
||||
|
||||
let parameter = NKShareParameter(path: itemRelativePath)
|
||||
|
||||
return await withCheckedContinuation { continuation in
|
||||
kit.readShares(parameters: parameter) { account, shares, data, error in
|
||||
let shareCount = shares?.count ?? 0
|
||||
Logger.sharesDataSource.info("Received \(shareCount, privacy: .public) shares")
|
||||
defer { continuation.resume(returning: shares ?? []) }
|
||||
guard error == .success else {
|
||||
self.presentError("Error fetching shares: \(error.errorDescription)")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func fetchCapabilities() async -> Capabilities? {
|
||||
return await withCheckedContinuation { continuation in
|
||||
kit?.getCapabilities { account, capabilitiesJson, error in
|
||||
guard error == .success, let capabilitiesJson = capabilitiesJson else {
|
||||
self.presentError("Error getting server caps: \(error.errorDescription)")
|
||||
continuation.resume(returning: nil)
|
||||
return
|
||||
}
|
||||
Logger.sharesDataSource.info("Successfully retrieved server share capabilities")
|
||||
continuation.resume(returning: Capabilities(data: capabilitiesJson))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func fetchItemMetadata(itemRelativePath: String) async -> NKFile? {
|
||||
guard let kit = kit else {
|
||||
presentError("Could not fetch item metadata as NextcloudKit instance is unavailable")
|
||||
return nil
|
||||
}
|
||||
|
||||
func slashlessPath(_ string: String) -> String {
|
||||
var strCopy = string
|
||||
if strCopy.hasPrefix("/") {
|
||||
strCopy.removeFirst()
|
||||
}
|
||||
if strCopy.hasSuffix("/") {
|
||||
strCopy.removeLast()
|
||||
}
|
||||
return strCopy
|
||||
}
|
||||
|
||||
let nkCommon = kit.nkCommonInstance
|
||||
let urlBase = slashlessPath(nkCommon.urlBase)
|
||||
let davSuffix = slashlessPath(nkCommon.dav)
|
||||
let userId = nkCommon.userId
|
||||
let itemRelPath = slashlessPath(itemRelativePath)
|
||||
|
||||
let itemFullServerPath = "\(urlBase)/\(davSuffix)/files/\(userId)/\(itemRelPath)"
|
||||
return await withCheckedContinuation { continuation in
|
||||
kit.readFileOrFolder(serverUrlFileName: itemFullServerPath, depth: "0") {
|
||||
account, files, data, error in
|
||||
guard error == .success else {
|
||||
self.presentError("Error getting item metadata: \(error.errorDescription)")
|
||||
continuation.resume(returning: nil)
|
||||
return
|
||||
}
|
||||
Logger.sharesDataSource.info("Successfully retrieved item metadata")
|
||||
continuation.resume(returning: files.first)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func presentError(_ errorString: String) {
|
||||
Logger.sharesDataSource.error("\(errorString, privacy: .public)")
|
||||
Task { @MainActor in self.uiDelegate?.showError(errorString) }
|
||||
}
|
||||
|
||||
// MARK: - NSTableViewDataSource protocol methods
|
||||
|
||||
@objc func numberOfRows(in tableView: NSTableView) -> Int {
|
||||
shares.count
|
||||
}
|
||||
|
||||
// MARK: - NSTableViewDelegate protocol methods
|
||||
|
||||
@objc func tableView(
|
||||
_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int
|
||||
) -> NSView? {
|
||||
let share = shares[row]
|
||||
guard let view = tableView.makeView(
|
||||
withIdentifier: shareItemViewIdentifier, owner: self
|
||||
) as? ShareTableItemView else {
|
||||
Logger.sharesDataSource.error("Acquired item view from table is not a share item view!")
|
||||
return nil
|
||||
}
|
||||
view.share = share
|
||||
return view
|
||||
}
|
||||
|
||||
@objc func tableViewSelectionDidChange(_ notification: Notification) {
|
||||
guard let selectedRow = sharesTableView?.selectedRow, selectedRow >= 0 else {
|
||||
Task { @MainActor in uiDelegate?.hideOptions(self) }
|
||||
return
|
||||
}
|
||||
let share = shares[selectedRow]
|
||||
Task { @MainActor in uiDelegate?.showOptions(share: share) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
//
|
||||
// ShareViewController.swift
|
||||
// FileProviderUIExt
|
||||
//
|
||||
// Created by Claudio Cambra on 21/2/24.
|
||||
//
|
||||
|
||||
import AppKit
|
||||
import FileProvider
|
||||
import NextcloudKit
|
||||
import OSLog
|
||||
import QuickLookThumbnailing
|
||||
|
||||
class ShareViewController: NSViewController, ShareViewDataSourceUIDelegate {
|
||||
let shareDataSource = ShareTableViewDataSource()
|
||||
let itemIdentifiers: [NSFileProviderItemIdentifier]
|
||||
|
||||
@IBOutlet weak var fileNameIcon: NSImageView!
|
||||
@IBOutlet weak var fileNameLabel: NSTextField!
|
||||
@IBOutlet weak var descriptionLabel: NSTextField!
|
||||
@IBOutlet weak var createButton: NSButton!
|
||||
@IBOutlet weak var closeButton: NSButton!
|
||||
@IBOutlet weak var tableView: NSTableView!
|
||||
@IBOutlet weak var optionsView: ShareOptionsView!
|
||||
@IBOutlet weak var splitView: NSSplitView!
|
||||
@IBOutlet weak var loadingEffectView: NSVisualEffectView!
|
||||
@IBOutlet weak var loadingIndicator: NSProgressIndicator!
|
||||
@IBOutlet weak var errorMessageStackView: NSStackView!
|
||||
@IBOutlet weak var errorTextLabel: NSTextField!
|
||||
@IBOutlet weak var noSharesLabel: NSTextField!
|
||||
|
||||
public override var nibName: NSNib.Name? {
|
||||
return NSNib.Name(self.className)
|
||||
}
|
||||
|
||||
var actionViewController: DocumentActionViewController! {
|
||||
return parent as? DocumentActionViewController
|
||||
}
|
||||
|
||||
init(_ itemIdentifiers: [NSFileProviderItemIdentifier]) {
|
||||
self.itemIdentifiers = itemIdentifiers
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
||||
guard let firstItem = itemIdentifiers.first else {
|
||||
Logger.shareViewController.error("called without items")
|
||||
closeAction(self)
|
||||
return
|
||||
}
|
||||
|
||||
Task {
|
||||
await processItemIdentifier(firstItem)
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
dismissError(self)
|
||||
hideOptions(self)
|
||||
}
|
||||
|
||||
@IBAction func closeAction(_ sender: Any) {
|
||||
actionViewController.extensionContext.completeRequest()
|
||||
}
|
||||
|
||||
private func processItemIdentifier(_ itemIdentifier: NSFileProviderItemIdentifier) async {
|
||||
guard let manager = NSFileProviderManager(for: actionViewController.domain) else {
|
||||
fatalError("NSFileProviderManager isn't expected to fail")
|
||||
}
|
||||
|
||||
do {
|
||||
let itemUrl = try await manager.getUserVisibleURL(for: itemIdentifier)
|
||||
await updateDisplay(itemUrl: itemUrl)
|
||||
shareDataSource.uiDelegate = self
|
||||
shareDataSource.sharesTableView = tableView
|
||||
shareDataSource.loadItem(url: itemUrl)
|
||||
optionsView.dataSource = shareDataSource
|
||||
} catch let error {
|
||||
let errorString = "Error processing item: \(error)"
|
||||
Logger.shareViewController.error("\(errorString)")
|
||||
fileNameLabel.stringValue = "Unknown item"
|
||||
descriptionLabel.stringValue = errorString
|
||||
}
|
||||
}
|
||||
|
||||
private func updateDisplay(itemUrl: URL) async {
|
||||
fileNameLabel.stringValue = itemUrl.lastPathComponent
|
||||
|
||||
let request = QLThumbnailGenerator.Request(
|
||||
fileAt: itemUrl,
|
||||
size: CGSize(width: 128, height: 128),
|
||||
scale: 1.0,
|
||||
representationTypes: .icon
|
||||
)
|
||||
|
||||
let generator = QLThumbnailGenerator.shared
|
||||
let fileThumbnail = await withCheckedContinuation { continuation in
|
||||
generator.generateRepresentations(for: request) { thumbnail, type, error in
|
||||
if thumbnail == nil || error != nil {
|
||||
Logger.shareViewController.error("Could not get thumbnail: \(error)")
|
||||
}
|
||||
continuation.resume(returning: thumbnail)
|
||||
}
|
||||
}
|
||||
fileNameIcon.image = fileThumbnail?.nsImage
|
||||
|
||||
let resourceValues = try? itemUrl.resourceValues(
|
||||
forKeys: [.fileSizeKey, .contentModificationDateKey]
|
||||
)
|
||||
var sizeDesc = "Unknown size"
|
||||
var modDesc = "Unknown modification date"
|
||||
if let fileSize = resourceValues?.fileSize {
|
||||
sizeDesc = ByteCountFormatter().string(fromByteCount: Int64(fileSize))
|
||||
}
|
||||
if let modificationDate = resourceValues?.contentModificationDate {
|
||||
let modDateString = DateFormatter.localizedString(
|
||||
from: modificationDate, dateStyle: .short, timeStyle: .short
|
||||
)
|
||||
modDesc = "Last modified: \(modDateString)"
|
||||
}
|
||||
descriptionLabel.stringValue = "\(sizeDesc) · \(modDesc)"
|
||||
}
|
||||
|
||||
@IBAction func dismissError(_ sender: Any) {
|
||||
errorMessageStackView.isHidden = true
|
||||
}
|
||||
|
||||
@IBAction func createShare(_ sender: Any) {
|
||||
guard let kit = shareDataSource.kit else { return }
|
||||
optionsView.kit = kit
|
||||
optionsView.createMode = true
|
||||
tableView.deselectAll(self)
|
||||
if !splitView.arrangedSubviews.contains(optionsView) {
|
||||
splitView.addArrangedSubview(optionsView)
|
||||
optionsView.isHidden = false
|
||||
}
|
||||
}
|
||||
|
||||
func fetchStarted() {
|
||||
loadingEffectView.isHidden = false
|
||||
loadingIndicator.startAnimation(self)
|
||||
}
|
||||
|
||||
func fetchFinished() {
|
||||
noSharesLabel.isHidden = !shareDataSource.shares.isEmpty
|
||||
loadingEffectView.isHidden = true
|
||||
loadingIndicator.stopAnimation(self)
|
||||
}
|
||||
|
||||
func hideOptions(_ sender: Any) {
|
||||
if sender as? ShareTableViewDataSource == shareDataSource, optionsView.createMode {
|
||||
// Do not hide options if the table view has had everything deselected when we set the
|
||||
// options view to be in create mode
|
||||
return
|
||||
}
|
||||
splitView.removeArrangedSubview(optionsView)
|
||||
optionsView.isHidden = true
|
||||
}
|
||||
|
||||
func showOptions(share: NKShare) {
|
||||
guard let kit = shareDataSource.kit else { return }
|
||||
optionsView.kit = kit
|
||||
optionsView.controller = ShareController(share: share, kit: kit)
|
||||
if !splitView.arrangedSubviews.contains(optionsView) {
|
||||
splitView.addArrangedSubview(optionsView)
|
||||
optionsView.isHidden = false
|
||||
}
|
||||
}
|
||||
|
||||
func showError(_ errorString: String) {
|
||||
errorMessageStackView.isHidden = false
|
||||
errorTextLabel.stringValue = errorString
|
||||
}
|
||||
}
|
|
@ -0,0 +1,532 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22689"/>
|
||||
<capability name="Image references" minToolsVersion="12.0"/>
|
||||
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="ShareViewController" customModule="FileProviderUIExt" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="closeButton" destination="aDA-n9-Zly" id="niR-Ad-FEa"/>
|
||||
<outlet property="createButton" destination="BMA-BP-wHc" id="HEq-bN-i9V"/>
|
||||
<outlet property="descriptionLabel" destination="gX0-nE-MrU" id="RoY-u1-1on"/>
|
||||
<outlet property="errorMessageStackView" destination="dFs-Gh-2WQ" id="kkQ-Uq-xk7"/>
|
||||
<outlet property="errorTextLabel" destination="770-HW-oC7" id="gfn-SV-TNM"/>
|
||||
<outlet property="fileNameIcon" destination="zSV-DV-Ray" id="336-e0-CEo"/>
|
||||
<outlet property="fileNameLabel" destination="slV-H6-zJ3" id="DPp-sN-Yff"/>
|
||||
<outlet property="loadingEffectView" destination="1ud-mC-gQV" id="HMT-Sb-Axl"/>
|
||||
<outlet property="loadingIndicator" destination="acb-Yu-Zpm" id="9Jf-dE-7LE"/>
|
||||
<outlet property="noSharesLabel" destination="K3D-6U-Cbr" id="zDP-E4-9bg"/>
|
||||
<outlet property="optionsView" destination="EXb-m8-yzj" id="uAb-lv-EZ4"/>
|
||||
<outlet property="splitView" destination="91w-SP-6sl" id="20T-gQ-SPY"/>
|
||||
<outlet property="tableView" destination="vb0-a6-eeH" id="KQo-eg-dba"/>
|
||||
<outlet property="view" destination="Jw6-da-U8j" id="5Ek-F1-w7C"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<view translatesAutoresizingMaskIntoConstraints="NO" id="Jw6-da-U8j">
|
||||
<rect key="frame" x="0.0" y="0.0" width="500" height="829"/>
|
||||
<subviews>
|
||||
<splitView arrangesAllSubviews="NO" dividerStyle="thin" translatesAutoresizingMaskIntoConstraints="NO" id="91w-SP-6sl">
|
||||
<rect key="frame" x="10" y="10" width="480" height="809"/>
|
||||
<subviews>
|
||||
<stackView distribution="fill" orientation="vertical" alignment="leading" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wGI-UV-bB3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
|
||||
<subviews>
|
||||
<stackView distribution="fill" orientation="horizontal" alignment="top" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9kj-aB-aJh">
|
||||
<rect key="frame" x="0.0" y="437" width="480" height="43"/>
|
||||
<subviews>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="zSV-DV-Ray">
|
||||
<rect key="frame" x="0.0" y="-3.5" width="43" height="50"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="zSV-DV-Ray" secondAttribute="height" multiplier="1:1" id="NSH-gA-7lL"/>
|
||||
</constraints>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="doc" catalog="system" id="laO-OA-5sJ"/>
|
||||
</imageView>
|
||||
<stackView distribution="fill" orientation="vertical" alignment="leading" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="oSY-fV-uws">
|
||||
<rect key="frame" x="51" y="0.0" width="375" height="43"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="slV-H6-zJ3">
|
||||
<rect key="frame" x="-2" y="24" width="78" height="19"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="File name" id="Uuo-1j-to8">
|
||||
<font key="font" metaFont="systemBold" size="16"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gX0-nE-MrU">
|
||||
<rect key="frame" x="-2" y="0.0" width="146" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="File size · Last modified" id="1GC-Gr-x29">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<visibilityPriorities>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
</visibilityPriorities>
|
||||
<customSpacing>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
<button translatesAutoresizingMaskIntoConstraints="NO" id="BMA-BP-wHc">
|
||||
<rect key="frame" x="434" y="-3.5" width="18.5" height="51"/>
|
||||
<buttonCell key="cell" type="bevel" title="Create share" bezelStyle="regularSquare" imagePosition="only" alignment="center" imageScaling="proportionallyDown" inset="2" id="cdh-AC-lt4">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<imageReference key="image" image="plus" catalog="system" symbolScale="large"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="createShare:" target="-2" id="NLV-ZM-y1w"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button translatesAutoresizingMaskIntoConstraints="NO" id="aDA-n9-Zly">
|
||||
<rect key="frame" x="460" y="-5" width="20" height="54"/>
|
||||
<buttonCell key="cell" type="bevel" title="Close" bezelStyle="rounded" imagePosition="only" alignment="center" imageScaling="proportionallyDown" inset="2" id="zNR-DC-3xZ">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<imageReference key="image" image="xmark.circle.fill" catalog="system" symbolScale="large"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="closeAction:" target="-2" id="D9k-gc-AcN"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="43" id="C79-af-CZw"/>
|
||||
<constraint firstItem="oSY-fV-uws" firstAttribute="height" secondItem="9kj-aB-aJh" secondAttribute="height" id="I6w-n6-pgZ"/>
|
||||
<constraint firstAttribute="bottom" secondItem="BMA-BP-wHc" secondAttribute="bottom" id="RE6-Rg-yv9"/>
|
||||
<constraint firstItem="zSV-DV-Ray" firstAttribute="height" secondItem="9kj-aB-aJh" secondAttribute="height" id="X4V-Vr-Q7m"/>
|
||||
<constraint firstItem="BMA-BP-wHc" firstAttribute="top" secondItem="9kj-aB-aJh" secondAttribute="top" id="hrc-la-YmY"/>
|
||||
<constraint firstItem="aDA-n9-Zly" firstAttribute="height" secondItem="9kj-aB-aJh" secondAttribute="height" id="qCh-8Z-jcN"/>
|
||||
</constraints>
|
||||
<visibilityPriorities>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
</visibilityPriorities>
|
||||
<customSpacing>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
<stackView distribution="fillProportionally" orientation="horizontal" alignment="top" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" verticalCompressionResistancePriority="1000" detachesHiddenViews="YES" id="dFs-Gh-2WQ">
|
||||
<rect key="frame" x="0.0" y="308" width="480" height="40"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="770-HW-oC7">
|
||||
<rect key="frame" x="0.0" y="0.0" width="411" height="40"/>
|
||||
<textFieldCell key="cell" selectable="YES" borderStyle="border" alignment="left" title="A long error message that provides detail about why some operations with the shares failed" drawsBackground="YES" id="Jvl-L9-x8K">
|
||||
<font key="font" metaFont="systemSemibold" size="13"/>
|
||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="displayP3"/>
|
||||
<color key="backgroundColor" name="systemRedColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button translatesAutoresizingMaskIntoConstraints="NO" id="qfq-F1-w0b">
|
||||
<rect key="frame" x="416" y="-4" width="67" height="47"/>
|
||||
<buttonCell key="cell" type="bevel" title="Dismiss" bezelStyle="regularSquare" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="dLP-yX-dyk">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<color key="bezelColor" name="systemRedColor" catalog="System" colorSpace="catalog"/>
|
||||
<connections>
|
||||
<action selector="dismissError:" target="-2" id="qLa-KM-cYK"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="770-HW-oC7" firstAttribute="top" secondItem="dFs-Gh-2WQ" secondAttribute="top" id="2Y8-H2-wTQ"/>
|
||||
<constraint firstItem="qfq-F1-w0b" firstAttribute="centerY" secondItem="dFs-Gh-2WQ" secondAttribute="centerY" id="e6n-1Y-kUh"/>
|
||||
<constraint firstAttribute="bottom" secondItem="770-HW-oC7" secondAttribute="bottom" id="fHi-My-Qyc"/>
|
||||
</constraints>
|
||||
<visibilityPriorities>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
</visibilityPriorities>
|
||||
<customSpacing>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
<scrollView autohidesScrollers="YES" horizontalLineScroll="17" horizontalPageScroll="10" verticalLineScroll="17" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ZzY-1Z-3xa">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="300"/>
|
||||
<clipView key="contentView" drawsBackground="NO" id="Ixg-th-Nw0">
|
||||
<rect key="frame" x="1" y="1" width="478" height="298"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="firstColumnOnly" columnReordering="NO" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" typeSelect="NO" rowSizeStyle="automatic" viewBased="YES" id="vb0-a6-eeH">
|
||||
<rect key="frame" x="0.0" y="0.0" width="478" height="298"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<size key="intercellSpacing" width="17" height="0.0"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
||||
<tableColumns>
|
||||
<tableColumn identifier="AutomaticTableColumnIdentifier.0" width="466" minWidth="40" maxWidth="1000" id="3Eb-aD-Ueu">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
</tableHeaderCell>
|
||||
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="mEe-4r-5cx">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
</tableColumn>
|
||||
</tableColumns>
|
||||
</tableView>
|
||||
</subviews>
|
||||
<nil key="backgroundColor"/>
|
||||
</clipView>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="300" id="FAW-Ws-zy7"/>
|
||||
</constraints>
|
||||
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="hQs-hA-bDq">
|
||||
<rect key="frame" x="1" y="284" width="478" height="15"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="ryi-jk-XFM">
|
||||
<rect key="frame" x="224" y="17" width="15" height="102"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="ZzY-1Z-3xa" firstAttribute="leading" secondItem="wGI-UV-bB3" secondAttribute="leading" id="528-fv-ZfM"/>
|
||||
<constraint firstItem="9kj-aB-aJh" firstAttribute="top" secondItem="wGI-UV-bB3" secondAttribute="top" id="EOP-84-2ZH"/>
|
||||
<constraint firstAttribute="trailing" secondItem="ZzY-1Z-3xa" secondAttribute="trailing" id="Kt7-wJ-gb5"/>
|
||||
<constraint firstItem="9kj-aB-aJh" firstAttribute="leading" secondItem="wGI-UV-bB3" secondAttribute="leading" id="UyT-D1-Awv"/>
|
||||
<constraint firstAttribute="trailing" secondItem="9kj-aB-aJh" secondAttribute="trailing" id="kua-OU-nbq"/>
|
||||
</constraints>
|
||||
<visibilityPriorities>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
</visibilityPriorities>
|
||||
<customSpacing>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
<view translatesAutoresizingMaskIntoConstraints="NO" id="EXb-m8-yzj" customClass="ShareOptionsView" customModule="FileProviderUIExt" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="481" width="480" height="328"/>
|
||||
<subviews>
|
||||
<stackView distribution="fill" orientation="vertical" alignment="leading" spacing="5" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="T4D-9c-PyA">
|
||||
<rect key="frame" x="10" y="10" width="460" height="308"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" verticalCompressionResistancePriority="749" translatesAutoresizingMaskIntoConstraints="NO" id="AWy-Qo-wHH">
|
||||
<rect key="frame" x="-2" y="292" width="464" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Share options" id="fzf-0v-uHo">
|
||||
<font key="font" metaFont="systemBold"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="aRB-vw-b4r">
|
||||
<rect key="frame" x="-3" y="263" width="467" height="25"/>
|
||||
<popUpButtonCell key="cell" type="push" title="Public link share" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="JhA-rv-1xy" id="S60-Qi-URJ">
|
||||
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="menu"/>
|
||||
<menu key="menu" id="yag-Vc-J7Y">
|
||||
<items>
|
||||
<menuItem title="Public link share" state="on" id="JhA-rv-1xy"/>
|
||||
<menuItem title="User share" id="CpL-qc-lAA"/>
|
||||
<menuItem title="Group share" id="bnp-aV-ZvE"/>
|
||||
<menuItem title="Email share" id="5DB-JD-Ij0">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Federated cloud share" id="RZP-ME-baz">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Circle share" id="yDE-lS-rJZ">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Talk conversation share" id="aHo-Mr-vTn">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</popUpButtonCell>
|
||||
<connections>
|
||||
<action selector="shareTypePickerAction:" target="EXb-m8-yzj" id="LN7-TC-RvV"/>
|
||||
</connections>
|
||||
</popUpButton>
|
||||
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="f8k-Ae-oQc">
|
||||
<rect key="frame" x="0.0" y="240" width="460" height="22"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="Share recipient" bezelStyle="round" id="Ahi-gU-lmO">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="CXW-ZO-B2f">
|
||||
<rect key="frame" x="0.0" y="213" width="460" height="22"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="Share label" bezelStyle="round" id="ZsJ-zc-mFT">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="luZ-Vf-V24">
|
||||
<rect key="frame" x="-2" y="191" width="175" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Allow upload and editing" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="vOP-1k-c75">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="2TN-96-apv">
|
||||
<rect key="frame" x="-2" y="170" width="117" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Hide download" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="6Eu-NS-uZ7">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ylD-hi-7Oq">
|
||||
<rect key="frame" x="-2" y="149" width="132" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Password protect" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="MWC-hf-0pc">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="passwordCheckboxAction:" target="EXb-m8-yzj" id="QMn-RM-jdf"/>
|
||||
</connections>
|
||||
</button>
|
||||
<secureTextField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pkv-9L-nhv">
|
||||
<rect key="frame" x="0.0" y="123" width="460" height="22"/>
|
||||
<secureTextFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="Enter a new password" usesSingleLineMode="YES" bezelStyle="round" id="hcA-we-oYG">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<allowedInputSourceLocales>
|
||||
<string>NSAllRomanInputSourcesLocaleIdentifier</string>
|
||||
</allowedInputSourceLocales>
|
||||
</secureTextFieldCell>
|
||||
</secureTextField>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="HaL-4Z-csA">
|
||||
<rect key="frame" x="-2" y="101" width="117" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Expiration date" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="r1a-iX-8Xo">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="expirationDateCheckboxAction:" target="EXb-m8-yzj" id="KHG-U0-rsG"/>
|
||||
</connections>
|
||||
</button>
|
||||
<datePicker verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="46m-5F-ME5">
|
||||
<rect key="frame" x="0.0" y="73" width="463" height="28"/>
|
||||
<datePickerCell key="cell" borderStyle="bezel" alignment="left" useCurrentDate="YES" id="EHU-Gu-Dfh">
|
||||
<font key="font" metaFont="system"/>
|
||||
<date key="date" timeIntervalSinceReferenceDate="734115148.45631897">
|
||||
<!--2024-04-06 16:52:28 +0000-->
|
||||
</date>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
</datePickerCell>
|
||||
</datePicker>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="c10-ub-U31">
|
||||
<rect key="frame" x="-2" y="51" width="132" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Note for recipient" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="oLA-Nu-fzO">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="noteForRecipientCheckboxAction:" target="EXb-m8-yzj" id="j7a-Tc-uiu"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="io6-Kg-fLl">
|
||||
<rect key="frame" x="0.0" y="25" width="460" height="22"/>
|
||||
<textFieldCell key="cell" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="Note for the recipient" bezelStyle="round" id="z5W-BH-NnM">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<stackView distribution="fillEqually" orientation="horizontal" alignment="bottom" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="mWb-eX-nfh">
|
||||
<rect key="frame" x="0.0" y="0.0" width="460" height="20"/>
|
||||
<subviews>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Bmf-yY-Y7V">
|
||||
<rect key="frame" x="-7" y="-7" width="240" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Delete" bezelStyle="rounded" image="trash.fill" catalog="system" imagePosition="leading" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Kb4-Qg-9Ag">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<color key="bezelColor" name="systemRedColor" catalog="System" colorSpace="catalog"/>
|
||||
<connections>
|
||||
<action selector="delete:" target="EXb-m8-yzj" id="PZq-SH-QVa"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button horizontalHuggingPriority="249" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="PaV-TL-cbq">
|
||||
<rect key="frame" x="227" y="-7" width="240" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Save" bezelStyle="rounded" image="arrow.up.square.fill" catalog="system" imagePosition="trailing" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="O1I-T0-iRC">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<color key="bezelColor" name="AccentColor"/>
|
||||
<connections>
|
||||
<action selector="save:" target="EXb-m8-yzj" id="O4N-dj-SlN"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<visibilityPriorities>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
</visibilityPriorities>
|
||||
<customSpacing>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="46m-5F-ME5" firstAttribute="leading" secondItem="T4D-9c-PyA" secondAttribute="leading" id="5LB-gI-S8e"/>
|
||||
<constraint firstItem="io6-Kg-fLl" firstAttribute="leading" secondItem="T4D-9c-PyA" secondAttribute="leading" id="94h-0I-pw6"/>
|
||||
<constraint firstAttribute="trailing" secondItem="pkv-9L-nhv" secondAttribute="trailing" id="IFt-uJ-etl"/>
|
||||
<constraint firstAttribute="trailing" secondItem="CXW-ZO-B2f" secondAttribute="trailing" id="Uvk-cg-D23"/>
|
||||
<constraint firstAttribute="trailing" secondItem="aRB-vw-b4r" secondAttribute="trailing" id="Zp9-vZ-oxU"/>
|
||||
<constraint firstItem="pkv-9L-nhv" firstAttribute="leading" secondItem="T4D-9c-PyA" secondAttribute="leading" id="bFe-9X-gFR"/>
|
||||
<constraint firstItem="AWy-Qo-wHH" firstAttribute="leading" secondItem="T4D-9c-PyA" secondAttribute="leading" id="dyr-SA-Kmy"/>
|
||||
<constraint firstAttribute="trailing" secondItem="46m-5F-ME5" secondAttribute="trailing" id="gV6-Jx-VH4"/>
|
||||
<constraint firstAttribute="bottom" secondItem="mWb-eX-nfh" secondAttribute="bottom" id="iLe-Zw-oO2"/>
|
||||
<constraint firstAttribute="trailing" secondItem="io6-Kg-fLl" secondAttribute="trailing" id="mkY-UE-SAy"/>
|
||||
<constraint firstItem="AWy-Qo-wHH" firstAttribute="top" secondItem="T4D-9c-PyA" secondAttribute="top" id="ndl-52-MnV"/>
|
||||
<constraint firstItem="CXW-ZO-B2f" firstAttribute="leading" secondItem="T4D-9c-PyA" secondAttribute="leading" id="oWL-JR-HTk"/>
|
||||
<constraint firstItem="mWb-eX-nfh" firstAttribute="leading" secondItem="T4D-9c-PyA" secondAttribute="leading" id="wiX-kw-Ohy"/>
|
||||
<constraint firstItem="aRB-vw-b4r" firstAttribute="leading" secondItem="T4D-9c-PyA" secondAttribute="leading" id="xPY-Ll-QCz"/>
|
||||
<constraint firstAttribute="trailing" secondItem="mWb-eX-nfh" secondAttribute="trailing" id="yUj-je-uXE"/>
|
||||
<constraint firstAttribute="trailing" secondItem="AWy-Qo-wHH" secondAttribute="trailing" id="zxC-eX-HrE"/>
|
||||
</constraints>
|
||||
<visibilityPriorities>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
</visibilityPriorities>
|
||||
<customSpacing>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="T4D-9c-PyA" firstAttribute="leading" secondItem="EXb-m8-yzj" secondAttribute="leading" constant="10" id="Dhe-wL-ygv"/>
|
||||
<constraint firstItem="T4D-9c-PyA" firstAttribute="top" secondItem="EXb-m8-yzj" secondAttribute="top" constant="10" id="JrC-o6-So3"/>
|
||||
<constraint firstAttribute="trailing" secondItem="T4D-9c-PyA" secondAttribute="trailing" constant="10" id="gs6-zf-D6h"/>
|
||||
<constraint firstAttribute="bottom" secondItem="T4D-9c-PyA" secondAttribute="bottom" constant="10" id="r5C-pm-lv4"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="circleShare" destination="yDE-lS-rJZ" id="yHV-25-RJd"/>
|
||||
<outlet property="deleteButton" destination="Bmf-yY-Y7V" id="Wd0-LR-DV5"/>
|
||||
<outlet property="emailShareMenuItem" destination="5DB-JD-Ij0" id="LD5-P8-V2l"/>
|
||||
<outlet property="expirationDateCheckbox" destination="HaL-4Z-csA" id="vIf-I8-e3i"/>
|
||||
<outlet property="expirationDatePicker" destination="46m-5F-ME5" id="eGk-fe-IIf"/>
|
||||
<outlet property="federatedCloudShareMenuItem" destination="RZP-ME-baz" id="quL-N2-y1z"/>
|
||||
<outlet property="groupShareMenuItem" destination="bnp-aV-ZvE" id="8iU-IP-YXK"/>
|
||||
<outlet property="hideDownloadCheckbox" destination="2TN-96-apv" id="OPr-4x-aiK"/>
|
||||
<outlet property="labelTextField" destination="CXW-ZO-B2f" id="otQ-jh-Psr"/>
|
||||
<outlet property="noteForRecipientCheckbox" destination="c10-ub-U31" id="aG6-4P-cBv"/>
|
||||
<outlet property="noteTextField" destination="io6-Kg-fLl" id="JKm-A1-SqR"/>
|
||||
<outlet property="optionsTitleTextField" destination="AWy-Qo-wHH" id="BjX-oW-0Lp"/>
|
||||
<outlet property="passwordProtectCheckbox" destination="ylD-hi-7Oq" id="qdw-aF-uh2"/>
|
||||
<outlet property="passwordSecureField" destination="pkv-9L-nhv" id="992-i5-CPF"/>
|
||||
<outlet property="publicLinkShareMenuItem" destination="JhA-rv-1xy" id="usv-L6-M7k"/>
|
||||
<outlet property="saveButton" destination="PaV-TL-cbq" id="OvF-Le-oQj"/>
|
||||
<outlet property="shareRecipientTextField" destination="f8k-Ae-oQc" id="bfc-Vn-Zu9"/>
|
||||
<outlet property="shareTypePicker" destination="aRB-vw-b4r" id="Dfx-Dw-zEM"/>
|
||||
<outlet property="talkConversationShare" destination="aHo-Mr-vTn" id="hxF-qw-VQO"/>
|
||||
<outlet property="uploadEditPermissionCheckbox" destination="luZ-Vf-V24" id="ojW-WP-98U"/>
|
||||
<outlet property="userShareMenuItem" destination="CpL-qc-lAA" id="R2q-dc-og5"/>
|
||||
</connections>
|
||||
</view>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="EXb-m8-yzj" secondAttribute="trailing" id="9yJ-jE-Iin"/>
|
||||
<constraint firstAttribute="bottom" secondItem="EXb-m8-yzj" secondAttribute="bottom" id="IDB-la-fxl"/>
|
||||
<constraint firstItem="EXb-m8-yzj" firstAttribute="leading" secondItem="91w-SP-6sl" secondAttribute="leading" id="imf-Yj-oUg"/>
|
||||
</constraints>
|
||||
<holdingPriorities>
|
||||
<real value="250"/>
|
||||
<real value="250"/>
|
||||
</holdingPriorities>
|
||||
</splitView>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="K3D-6U-Cbr">
|
||||
<rect key="frame" x="8" y="474" width="484" height="31"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="No shares" id="byC-34-Vtu">
|
||||
<font key="font" textStyle="largeTitle" name=".SFNS-Regular"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<visualEffectView blendingMode="withinWindow" material="HUDWindow" state="followsWindowActiveState" translatesAutoresizingMaskIntoConstraints="NO" id="1ud-mC-gQV">
|
||||
<rect key="frame" x="10" y="339" width="480" height="300"/>
|
||||
<subviews>
|
||||
<progressIndicator wantsLayer="YES" maxValue="100" indeterminate="YES" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="acb-Yu-Zpm">
|
||||
<rect key="frame" x="224" y="134" width="32" height="32"/>
|
||||
</progressIndicator>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="acb-Yu-Zpm" firstAttribute="centerY" secondItem="1ud-mC-gQV" secondAttribute="centerY" id="Dhf-yv-2Wr"/>
|
||||
<constraint firstItem="acb-Yu-Zpm" firstAttribute="centerX" secondItem="1ud-mC-gQV" secondAttribute="centerX" id="Kzb-iw-xCx"/>
|
||||
</constraints>
|
||||
</visualEffectView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="1ud-mC-gQV" firstAttribute="bottom" secondItem="ZzY-1Z-3xa" secondAttribute="bottom" id="HTR-Xt-ACu"/>
|
||||
<constraint firstItem="1ud-mC-gQV" firstAttribute="top" secondItem="ZzY-1Z-3xa" secondAttribute="top" id="Hjv-Wk-Rk1"/>
|
||||
<constraint firstItem="91w-SP-6sl" firstAttribute="top" secondItem="Jw6-da-U8j" secondAttribute="top" constant="10" id="JFe-jp-e9y"/>
|
||||
<constraint firstAttribute="width" constant="500" id="KBX-aG-ZDU"/>
|
||||
<constraint firstItem="1ud-mC-gQV" firstAttribute="trailing" secondItem="ZzY-1Z-3xa" secondAttribute="trailing" id="Kpk-6o-ao7"/>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="380" id="LND-hR-7Co"/>
|
||||
<constraint firstAttribute="trailing" secondItem="91w-SP-6sl" secondAttribute="trailing" constant="10" id="XrV-hl-kxC"/>
|
||||
<constraint firstAttribute="bottom" secondItem="91w-SP-6sl" secondAttribute="bottom" constant="10" id="Z4B-85-NR5"/>
|
||||
<constraint firstItem="1ud-mC-gQV" firstAttribute="height" secondItem="ZzY-1Z-3xa" secondAttribute="height" id="dfF-9z-iG9"/>
|
||||
<constraint firstItem="91w-SP-6sl" firstAttribute="leading" secondItem="Jw6-da-U8j" secondAttribute="leading" constant="10" id="elm-Nc-tc0"/>
|
||||
<constraint firstItem="1ud-mC-gQV" firstAttribute="leading" secondItem="ZzY-1Z-3xa" secondAttribute="leading" id="kSz-tV-EFM"/>
|
||||
<constraint firstItem="K3D-6U-Cbr" firstAttribute="leading" secondItem="ZzY-1Z-3xa" secondAttribute="leading" id="lut-va-B3T"/>
|
||||
<constraint firstItem="K3D-6U-Cbr" firstAttribute="trailing" secondItem="ZzY-1Z-3xa" secondAttribute="trailing" id="vRC-IM-ovt"/>
|
||||
<constraint firstItem="K3D-6U-Cbr" firstAttribute="centerY" secondItem="ZzY-1Z-3xa" secondAttribute="centerY" id="wFD-ph-Bsp"/>
|
||||
</constraints>
|
||||
<point key="canvasLocation" x="-270" y="79"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="arrow.up.square.fill" catalog="system" width="15" height="14"/>
|
||||
<image name="doc" catalog="system" width="14" height="16"/>
|
||||
<image name="plus" catalog="system" width="18" height="17"/>
|
||||
<image name="trash.fill" catalog="system" width="15" height="17"/>
|
||||
<image name="xmark.circle.fill" catalog="system" width="20" height="20"/>
|
||||
<namedColor name="AccentColor">
|
||||
<color red="0.0" green="0.46000000000000002" blue="0.89000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</namedColor>
|
||||
</resources>
|
||||
</document>
|
|
@ -0,0 +1,17 @@
|
|||
//
|
||||
// ShareViewDataSourceUIDelegate.swift
|
||||
// FileProviderUIExt
|
||||
//
|
||||
// Created by Claudio Cambra on 28/2/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import NextcloudKit
|
||||
|
||||
protocol ShareViewDataSourceUIDelegate {
|
||||
func fetchStarted()
|
||||
func fetchFinished()
|
||||
func hideOptions(_ sender: Any)
|
||||
func showOptions(share: NKShare)
|
||||
func showError(_ errorString: String)
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
//
|
||||
// ShareeSuggestionsDataSource.swift
|
||||
// FileProviderUIExt
|
||||
//
|
||||
// Created by Claudio Cambra on 2/4/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import NextcloudKit
|
||||
import OSLog
|
||||
import SuggestionsTextFieldKit
|
||||
|
||||
class ShareeSuggestionsDataSource: SuggestionsDataSource {
|
||||
let kit: NextcloudKit
|
||||
var suggestions: [Suggestion] = []
|
||||
var inputString: String = "" {
|
||||
didSet { Task { await updateSuggestions() } }
|
||||
}
|
||||
|
||||
init(kit: NextcloudKit) {
|
||||
self.kit = kit
|
||||
}
|
||||
|
||||
private func updateSuggestions() async {
|
||||
let sharees = await fetchSharees(search: inputString)
|
||||
Logger.shareeDataSource.info("Fetched \(sharees.count, privacy: .public) sharees.")
|
||||
suggestions = suggestionsFromSharees(sharees)
|
||||
NotificationCenter.default.post(name: SuggestionsChangedNotificationName, object: self)
|
||||
}
|
||||
|
||||
private func fetchSharees(search: String) async -> [NKSharee] {
|
||||
Logger.shareeDataSource.debug("Searching sharees with: \(search, privacy: .public)")
|
||||
return await withCheckedContinuation { continuation in
|
||||
kit.searchSharees(
|
||||
search: inputString,
|
||||
page: 1,
|
||||
perPage: 20,
|
||||
completion: { account, sharees, data, error in
|
||||
defer { continuation.resume(returning: sharees ?? []) }
|
||||
guard error == .success else {
|
||||
Logger.shareeDataSource.error(
|
||||
"Error fetching sharees: \(error.description, privacy: .public)"
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private func suggestionsFromSharees(_ sharees: [NKSharee]) -> [Suggestion] {
|
||||
return sharees.map {
|
||||
Suggestion(
|
||||
imageName: "person.fill",
|
||||
displayText: $0.label.isEmpty ? $0.name : $0.label,
|
||||
data: $0
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@
|
|||
5307A6E82965DAD8001E0C6A /* NextcloudKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5307A6E72965DAD8001E0C6A /* NextcloudKit */; };
|
||||
5307A6EB2965DB8D001E0C6A /* RealmSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 5307A6EA2965DB8D001E0C6A /* RealmSwift */; };
|
||||
5307A6F229675346001E0C6A /* NextcloudFilesDatabaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5307A6F129675346001E0C6A /* NextcloudFilesDatabaseManager.swift */; };
|
||||
531522822B8E01C6002E31BE /* ShareTableItemView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 531522812B8E01C6002E31BE /* ShareTableItemView.xib */; };
|
||||
5318AD9129BF42FB00CBB71C /* NextcloudItemMetadataTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5318AD9029BF42FB00CBB71C /* NextcloudItemMetadataTable.swift */; };
|
||||
5318AD9529BF438F00CBB71C /* NextcloudLocalFileMetadataTable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5318AD9429BF438F00CBB71C /* NextcloudLocalFileMetadataTable.swift */; };
|
||||
5318AD9729BF493600CBB71C /* FileProviderMaterialisedEnumerationObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5318AD9629BF493600CBB71C /* FileProviderMaterialisedEnumerationObserver.swift */; };
|
||||
|
@ -20,9 +21,19 @@
|
|||
5352B36829DC17D60011CE03 /* NextcloudFilesDatabaseManager+LocalFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5352B36729DC17D60011CE03 /* NextcloudFilesDatabaseManager+LocalFiles.swift */; };
|
||||
5352B36C29DC44B50011CE03 /* FileProviderExtension+Thumbnailing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5352B36B29DC44B50011CE03 /* FileProviderExtension+Thumbnailing.swift */; };
|
||||
5352E85B29B7BFE6002CE85C /* Progress+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5352E85A29B7BFE6002CE85C /* Progress+Extensions.swift */; };
|
||||
5358F2B92BAA0F5300E3C729 /* NextcloudCapabilitiesKit in Frameworks */ = {isa = PBXBuildFile; productRef = 5358F2B82BAA0F5300E3C729 /* NextcloudCapabilitiesKit */; };
|
||||
535AE30E29C0A2CC0042A9BA /* Logger+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 535AE30D29C0A2CC0042A9BA /* Logger+Extensions.swift */; };
|
||||
53651E442BBC0CA300ECAC29 /* SuggestionsTextFieldKit in Frameworks */ = {isa = PBXBuildFile; productRef = 53651E432BBC0CA300ECAC29 /* SuggestionsTextFieldKit */; };
|
||||
53651E462BBC0D9500ECAC29 /* ShareeSuggestionsDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53651E452BBC0D9500ECAC29 /* ShareeSuggestionsDataSource.swift */; };
|
||||
536EFBF7295CF58100F4CB13 /* FileProviderSocketLineProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536EFBF6295CF58100F4CB13 /* FileProviderSocketLineProcessor.swift */; };
|
||||
536EFC36295E3C1100F4CB13 /* NextcloudAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536EFC35295E3C1100F4CB13 /* NextcloudAccount.swift */; };
|
||||
5374FD442B95EE1400C78D54 /* ShareController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5374FD432B95EE1400C78D54 /* ShareController.swift */; };
|
||||
5376307D2B85E2ED0026BFAB /* Logger+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5376307C2B85E2ED0026BFAB /* Logger+Extensions.swift */; };
|
||||
537630912B85F4980026BFAB /* ShareViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 537630902B85F4980026BFAB /* ShareViewController.xib */; };
|
||||
537630932B85F4B00026BFAB /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537630922B85F4B00026BFAB /* ShareViewController.swift */; };
|
||||
537630952B860D560026BFAB /* FPUIExtensionServiceSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537630942B860D560026BFAB /* FPUIExtensionServiceSource.swift */; };
|
||||
537630972B860D920026BFAB /* FPUIExtensionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537630962B860D920026BFAB /* FPUIExtensionService.swift */; };
|
||||
537630982B8612F00026BFAB /* FPUIExtensionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 537630962B860D920026BFAB /* FPUIExtensionService.swift */; };
|
||||
538E396A27F4765000FA63D5 /* UniformTypeIdentifiers.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 538E396927F4765000FA63D5 /* UniformTypeIdentifiers.framework */; };
|
||||
538E396D27F4765000FA63D5 /* FileProviderExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538E396C27F4765000FA63D5 /* FileProviderExtension.swift */; };
|
||||
538E396F27F4765000FA63D5 /* FileProviderItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 538E396E27F4765000FA63D5 /* FileProviderItem.swift */; };
|
||||
|
@ -39,11 +50,19 @@
|
|||
53903D352956184400D0B308 /* LocalSocketClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 539158B127BE891500816F56 /* LocalSocketClient.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
53903D37295618A400D0B308 /* LineProcessor.h in Headers */ = {isa = PBXBuildFile; fileRef = 53903D36295618A400D0B308 /* LineProcessor.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
539158AC27BE71A900816F56 /* FinderSyncSocketLineProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = 539158AB27BE71A900816F56 /* FinderSyncSocketLineProcessor.m */; };
|
||||
53B979812B84C81F002DA742 /* DocumentActionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53B979802B84C81F002DA742 /* DocumentActionViewController.swift */; };
|
||||
53D056312970594F00988392 /* LocalFilesUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D056302970594F00988392 /* LocalFilesUtils.swift */; };
|
||||
53D666612B70C9A70042C03D /* FileProviderConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D666602B70C9A70042C03D /* FileProviderConfig.swift */; };
|
||||
53ED472029C5E64200795DB1 /* FileProviderEnumerator+SyncEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53ED471F29C5E64200795DB1 /* FileProviderEnumerator+SyncEngine.swift */; };
|
||||
53ED472829C88E7000795DB1 /* NextcloudItemMetadataTable+NKFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53ED472729C88E7000795DB1 /* NextcloudItemMetadataTable+NKFile.swift */; };
|
||||
53ED473029C9CE0B00795DB1 /* FileProviderExtension+ClientInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53ED472F29C9CE0B00795DB1 /* FileProviderExtension+ClientInterface.swift */; };
|
||||
53FE14502B8E0658006C4193 /* ShareTableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53FE144F2B8E0658006C4193 /* ShareTableViewDataSource.swift */; };
|
||||
53FE14542B8E1219006C4193 /* NextcloudKit in Frameworks */ = {isa = PBXBuildFile; productRef = 53FE14532B8E1219006C4193 /* NextcloudKit */; };
|
||||
53FE14552B8E28E9006C4193 /* NextcloudAccount.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536EFC35295E3C1100F4CB13 /* NextcloudAccount.swift */; };
|
||||
53FE14592B8E3F6C006C4193 /* ShareTableItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53FE14582B8E3F6C006C4193 /* ShareTableItemView.swift */; };
|
||||
53FE145B2B8F1305006C4193 /* NKShare+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53FE145A2B8F1305006C4193 /* NKShare+Extensions.swift */; };
|
||||
53FE14652B8F6700006C4193 /* ShareViewDataSourceUIDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53FE14642B8F6700006C4193 /* ShareViewDataSourceUIDelegate.swift */; };
|
||||
53FE14672B8F78B6006C4193 /* ShareOptionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53FE14662B8F78B6006C4193 /* ShareOptionsView.swift */; };
|
||||
C2B573BA1B1CD91E00303B36 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C2B573B91B1CD91E00303B36 /* main.m */; };
|
||||
C2B573D21B1CD94B00303B36 /* main.m in Resources */ = {isa = PBXBuildFile; fileRef = C2B573B91B1CD91E00303B36 /* main.m */; };
|
||||
C2B573DE1B1CD9CE00303B36 /* FinderSync.m in Sources */ = {isa = PBXBuildFile; fileRef = C2B573DD1B1CD9CE00303B36 /* FinderSync.m */; };
|
||||
|
@ -143,6 +162,7 @@
|
|||
|
||||
/* Begin PBXFileReference section */
|
||||
5307A6F129675346001E0C6A /* NextcloudFilesDatabaseManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudFilesDatabaseManager.swift; sourceTree = "<group>"; };
|
||||
531522812B8E01C6002E31BE /* ShareTableItemView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ShareTableItemView.xib; sourceTree = "<group>"; };
|
||||
5318AD9029BF42FB00CBB71C /* NextcloudItemMetadataTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudItemMetadataTable.swift; sourceTree = "<group>"; };
|
||||
5318AD9429BF438F00CBB71C /* NextcloudLocalFileMetadataTable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudLocalFileMetadataTable.swift; sourceTree = "<group>"; };
|
||||
5318AD9629BF493600CBB71C /* FileProviderMaterialisedEnumerationObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileProviderMaterialisedEnumerationObserver.swift; sourceTree = "<group>"; };
|
||||
|
@ -155,8 +175,16 @@
|
|||
5352B36B29DC44B50011CE03 /* FileProviderExtension+Thumbnailing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileProviderExtension+Thumbnailing.swift"; sourceTree = "<group>"; };
|
||||
5352E85A29B7BFE6002CE85C /* Progress+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Progress+Extensions.swift"; sourceTree = "<group>"; };
|
||||
535AE30D29C0A2CC0042A9BA /* Logger+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Logger+Extensions.swift"; sourceTree = "<group>"; };
|
||||
53651E452BBC0D9500ECAC29 /* ShareeSuggestionsDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareeSuggestionsDataSource.swift; sourceTree = "<group>"; };
|
||||
536EFBF6295CF58100F4CB13 /* FileProviderSocketLineProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileProviderSocketLineProcessor.swift; sourceTree = "<group>"; };
|
||||
536EFC35295E3C1100F4CB13 /* NextcloudAccount.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextcloudAccount.swift; sourceTree = "<group>"; };
|
||||
5374FD432B95EE1400C78D54 /* ShareController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareController.swift; sourceTree = "<group>"; };
|
||||
5376307C2B85E2ED0026BFAB /* Logger+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Logger+Extensions.swift"; sourceTree = "<group>"; };
|
||||
5376307E2B85E5650026BFAB /* FileProviderUIExt.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = FileProviderUIExt.entitlements; sourceTree = "<group>"; };
|
||||
537630902B85F4980026BFAB /* ShareViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ShareViewController.xib; sourceTree = "<group>"; };
|
||||
537630922B85F4B00026BFAB /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
|
||||
537630942B860D560026BFAB /* FPUIExtensionServiceSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FPUIExtensionServiceSource.swift; sourceTree = "<group>"; };
|
||||
537630962B860D920026BFAB /* FPUIExtensionService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FPUIExtensionService.swift; sourceTree = "<group>"; };
|
||||
538E396727F4765000FA63D5 /* FileProviderExt.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = FileProviderExt.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
538E396927F4765000FA63D5 /* UniformTypeIdentifiers.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UniformTypeIdentifiers.framework; path = System/Library/Frameworks/UniformTypeIdentifiers.framework; sourceTree = SDKROOT; };
|
||||
538E396C27F4765000FA63D5 /* FileProviderExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileProviderExtension.swift; sourceTree = "<group>"; };
|
||||
|
@ -172,11 +200,20 @@
|
|||
539158AB27BE71A900816F56 /* FinderSyncSocketLineProcessor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FinderSyncSocketLineProcessor.m; sourceTree = "<group>"; };
|
||||
539158B127BE891500816F56 /* LocalSocketClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LocalSocketClient.h; sourceTree = "<group>"; };
|
||||
539158B227BEC98A00816F56 /* LocalSocketClient.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LocalSocketClient.m; sourceTree = "<group>"; };
|
||||
53B9797E2B84C81F002DA742 /* FileProviderUIExt.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = FileProviderUIExt.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
53B979802B84C81F002DA742 /* DocumentActionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DocumentActionViewController.swift; sourceTree = "<group>"; };
|
||||
53B979852B84C81F002DA742 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
53D056302970594F00988392 /* LocalFilesUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalFilesUtils.swift; sourceTree = "<group>"; };
|
||||
53D666602B70C9A70042C03D /* FileProviderConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileProviderConfig.swift; sourceTree = "<group>"; };
|
||||
53ED471F29C5E64200795DB1 /* FileProviderEnumerator+SyncEngine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileProviderEnumerator+SyncEngine.swift"; sourceTree = "<group>"; };
|
||||
53ED472729C88E7000795DB1 /* NextcloudItemMetadataTable+NKFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NextcloudItemMetadataTable+NKFile.swift"; sourceTree = "<group>"; };
|
||||
53ED472F29C9CE0B00795DB1 /* FileProviderExtension+ClientInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileProviderExtension+ClientInterface.swift"; sourceTree = "<group>"; };
|
||||
53FE144F2B8E0658006C4193 /* ShareTableViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareTableViewDataSource.swift; sourceTree = "<group>"; };
|
||||
53FE14572B8E3A7C006C4193 /* FileProviderUIExtRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = FileProviderUIExtRelease.entitlements; sourceTree = "<group>"; };
|
||||
53FE14582B8E3F6C006C4193 /* ShareTableItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareTableItemView.swift; sourceTree = "<group>"; };
|
||||
53FE145A2B8F1305006C4193 /* NKShare+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NKShare+Extensions.swift"; sourceTree = "<group>"; };
|
||||
53FE14642B8F6700006C4193 /* ShareViewDataSourceUIDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewDataSourceUIDelegate.swift; sourceTree = "<group>"; };
|
||||
53FE14662B8F78B6006C4193 /* ShareOptionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareOptionsView.swift; sourceTree = "<group>"; };
|
||||
C2B573B11B1CD91E00303B36 /* desktopclient.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = desktopclient.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
C2B573B51B1CD91E00303B36 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
C2B573B91B1CD91E00303B36 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
|
@ -211,6 +248,16 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
53B9797B2B84C81F002DA742 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5358F2B92BAA0F5300E3C729 /* NextcloudCapabilitiesKit in Frameworks */,
|
||||
53651E442BBC0CA300ECAC29 /* SuggestionsTextFieldKit in Frameworks */,
|
||||
53FE14542B8E1219006C4193 /* NextcloudKit in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
C2B573AE1B1CD91E00303B36 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -249,6 +296,8 @@
|
|||
children = (
|
||||
5350E4E72B0C514400F276CB /* ClientCommunicationProtocol.h */,
|
||||
5350E4E82B0C534A00F276CB /* ClientCommunicationService.swift */,
|
||||
537630962B860D920026BFAB /* FPUIExtensionService.swift */,
|
||||
537630942B860D560026BFAB /* FPUIExtensionServiceSource.swift */,
|
||||
);
|
||||
path = Services;
|
||||
sourceTree = "<group>";
|
||||
|
@ -263,6 +312,15 @@
|
|||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5376307B2B85E2E00026BFAB /* Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5376307C2B85E2ED0026BFAB /* Logger+Extensions.swift */,
|
||||
53FE145A2B8F1305006C4193 /* NKShare+Extensions.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
538E396827F4765000FA63D5 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -306,12 +364,34 @@
|
|||
path = NCDesktopClientSocketKit;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
53B9797F2B84C81F002DA742 /* FileProviderUIExt */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5376307B2B85E2E00026BFAB /* Extensions */,
|
||||
53B979802B84C81F002DA742 /* DocumentActionViewController.swift */,
|
||||
5374FD432B95EE1400C78D54 /* ShareController.swift */,
|
||||
53651E452BBC0D9500ECAC29 /* ShareeSuggestionsDataSource.swift */,
|
||||
53FE14662B8F78B6006C4193 /* ShareOptionsView.swift */,
|
||||
53FE14582B8E3F6C006C4193 /* ShareTableItemView.swift */,
|
||||
531522812B8E01C6002E31BE /* ShareTableItemView.xib */,
|
||||
53FE144F2B8E0658006C4193 /* ShareTableViewDataSource.swift */,
|
||||
537630922B85F4B00026BFAB /* ShareViewController.swift */,
|
||||
537630902B85F4980026BFAB /* ShareViewController.xib */,
|
||||
53FE14642B8F6700006C4193 /* ShareViewDataSourceUIDelegate.swift */,
|
||||
5376307E2B85E5650026BFAB /* FileProviderUIExt.entitlements */,
|
||||
53FE14572B8E3A7C006C4193 /* FileProviderUIExtRelease.entitlements */,
|
||||
53B979852B84C81F002DA742 /* Info.plist */,
|
||||
);
|
||||
path = FileProviderUIExt;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C2B573941B1CD88000303B36 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C2B573B31B1CD91E00303B36 /* desktopclient */,
|
||||
C2B573D81B1CD9CE00303B36 /* FinderSyncExt */,
|
||||
538E396B27F4765000FA63D5 /* FileProviderExt */,
|
||||
53B9797F2B84C81F002DA742 /* FileProviderUIExt */,
|
||||
53903D0D2956164F00D0B308 /* NCDesktopClientSocketKit */,
|
||||
538E396827F4765000FA63D5 /* Frameworks */,
|
||||
C2B573B21B1CD91E00303B36 /* Products */,
|
||||
|
@ -325,6 +405,7 @@
|
|||
C2B573D71B1CD9CE00303B36 /* FinderSyncExt.appex */,
|
||||
538E396727F4765000FA63D5 /* FileProviderExt.appex */,
|
||||
53903D0C2956164F00D0B308 /* NCDesktopClientSocketKit.framework */,
|
||||
53B9797E2B84C81F002DA742 /* FileProviderUIExt.appex */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
|
@ -430,6 +511,29 @@
|
|||
productReference = 53903D0C2956164F00D0B308 /* NCDesktopClientSocketKit.framework */;
|
||||
productType = "com.apple.product-type.framework";
|
||||
};
|
||||
53B9797D2B84C81F002DA742 /* FileProviderUIExt */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 53B979882B84C820002DA742 /* Build configuration list for PBXNativeTarget "FileProviderUIExt" */;
|
||||
buildPhases = (
|
||||
53B9797A2B84C81F002DA742 /* Sources */,
|
||||
53B9797B2B84C81F002DA742 /* Frameworks */,
|
||||
53B9797C2B84C81F002DA742 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
53FE14522B8E1213006C4193 /* PBXTargetDependency */,
|
||||
);
|
||||
name = FileProviderUIExt;
|
||||
packageProductDependencies = (
|
||||
53FE14532B8E1219006C4193 /* NextcloudKit */,
|
||||
5358F2B82BAA0F5300E3C729 /* NextcloudCapabilitiesKit */,
|
||||
53651E432BBC0CA300ECAC29 /* SuggestionsTextFieldKit */,
|
||||
);
|
||||
productName = FileProviderUIExt;
|
||||
productReference = 53B9797E2B84C81F002DA742 /* FileProviderUIExt.appex */;
|
||||
productType = "com.apple.product-type.app-extension";
|
||||
};
|
||||
C2B573B01B1CD91E00303B36 /* desktopclient */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = C2B573CC1B1CD91E00303B36 /* Build configuration list for PBXNativeTarget "desktopclient" */;
|
||||
|
@ -482,7 +586,7 @@
|
|||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = YES;
|
||||
LastSwiftUpdateCheck = 1420;
|
||||
LastSwiftUpdateCheck = 1520;
|
||||
LastUpgradeCheck = 1240;
|
||||
TargetAttributes = {
|
||||
538E396627F4765000FA63D5 = {
|
||||
|
@ -492,6 +596,9 @@
|
|||
CreatedOnToolsVersion = 14.2;
|
||||
ProvisioningStyle = Manual;
|
||||
};
|
||||
53B9797D2B84C81F002DA742 = {
|
||||
CreatedOnToolsVersion = 15.2;
|
||||
};
|
||||
C2B573B01B1CD91E00303B36 = {
|
||||
CreatedOnToolsVersion = 6.3.1;
|
||||
DevelopmentTeam = 9B5WD74GWJ;
|
||||
|
@ -522,6 +629,8 @@
|
|||
packageReferences = (
|
||||
5307A6E42965C6FA001E0C6A /* XCRemoteSwiftPackageReference "NextcloudKit" */,
|
||||
5307A6E92965DB57001E0C6A /* XCRemoteSwiftPackageReference "realm-swift" */,
|
||||
5358F2B72BAA045E00E3C729 /* XCRemoteSwiftPackageReference "NextcloudCapabilitiesKit" */,
|
||||
53651E422BBC0C7F00ECAC29 /* XCRemoteSwiftPackageReference "SuggestionsTextFieldKit" */,
|
||||
);
|
||||
productRefGroup = C2B573B21B1CD91E00303B36 /* Products */;
|
||||
projectDirPath = "";
|
||||
|
@ -530,6 +639,7 @@
|
|||
C2B573B01B1CD91E00303B36 /* desktopclient */,
|
||||
C2B573D61B1CD9CE00303B36 /* FinderSyncExt */,
|
||||
538E396627F4765000FA63D5 /* FileProviderExt */,
|
||||
53B9797D2B84C81F002DA742 /* FileProviderUIExt */,
|
||||
53903D0B2956164F00D0B308 /* NCDesktopClientSocketKit */,
|
||||
);
|
||||
};
|
||||
|
@ -550,6 +660,15 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
53B9797C2B84C81F002DA742 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
531522822B8E01C6002E31BE /* ShareTableItemView.xib in Resources */,
|
||||
537630912B85F4980026BFAB /* ShareViewController.xib in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
C2B573AF1B1CD91E00303B36 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -603,9 +722,11 @@
|
|||
53ED472029C5E64200795DB1 /* FileProviderEnumerator+SyncEngine.swift in Sources */,
|
||||
5318AD9929BF58D000CBB71C /* NKError+Extensions.swift in Sources */,
|
||||
53ED472829C88E7000795DB1 /* NextcloudItemMetadataTable+NKFile.swift in Sources */,
|
||||
537630972B860D920026BFAB /* FPUIExtensionService.swift in Sources */,
|
||||
5318AD9529BF438F00CBB71C /* NextcloudLocalFileMetadataTable.swift in Sources */,
|
||||
535AE30E29C0A2CC0042A9BA /* Logger+Extensions.swift in Sources */,
|
||||
5307A6F229675346001E0C6A /* NextcloudFilesDatabaseManager.swift in Sources */,
|
||||
537630952B860D560026BFAB /* FPUIExtensionServiceSource.swift in Sources */,
|
||||
53D056312970594F00988392 /* LocalFilesUtils.swift in Sources */,
|
||||
538E396F27F4765000FA63D5 /* FileProviderItem.swift in Sources */,
|
||||
5352B36829DC17D60011CE03 /* NextcloudFilesDatabaseManager+LocalFiles.swift in Sources */,
|
||||
|
@ -626,6 +747,25 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
53B9797A2B84C81F002DA742 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
537630932B85F4B00026BFAB /* ShareViewController.swift in Sources */,
|
||||
53FE14672B8F78B6006C4193 /* ShareOptionsView.swift in Sources */,
|
||||
53651E462BBC0D9500ECAC29 /* ShareeSuggestionsDataSource.swift in Sources */,
|
||||
53FE14652B8F6700006C4193 /* ShareViewDataSourceUIDelegate.swift in Sources */,
|
||||
53B979812B84C81F002DA742 /* DocumentActionViewController.swift in Sources */,
|
||||
5374FD442B95EE1400C78D54 /* ShareController.swift in Sources */,
|
||||
53FE145B2B8F1305006C4193 /* NKShare+Extensions.swift in Sources */,
|
||||
53FE14592B8E3F6C006C4193 /* ShareTableItemView.swift in Sources */,
|
||||
53FE14552B8E28E9006C4193 /* NextcloudAccount.swift in Sources */,
|
||||
5376307D2B85E2ED0026BFAB /* Logger+Extensions.swift in Sources */,
|
||||
53FE14502B8E0658006C4193 /* ShareTableViewDataSource.swift in Sources */,
|
||||
537630982B8612F00026BFAB /* FPUIExtensionService.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
C2B573AD1B1CD91E00303B36 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
@ -666,6 +806,10 @@
|
|||
target = 53903D0B2956164F00D0B308 /* NCDesktopClientSocketKit */;
|
||||
targetProxy = 53903D322956173F00D0B308 /* PBXContainerItemProxy */;
|
||||
};
|
||||
53FE14522B8E1213006C4193 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
productRef = 53FE14512B8E1213006C4193 /* NextcloudKit */;
|
||||
};
|
||||
C2B573E01B1CD9CE00303B36 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = C2B573D61B1CD9CE00303B36 /* FinderSyncExt */;
|
||||
|
@ -910,6 +1054,130 @@
|
|||
};
|
||||
name = Release;
|
||||
};
|
||||
53B979862B84C81F002DA742 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = FileProviderUIExt/FileProviderUIExt.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = FileProviderUIExt/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = FileProviderUIExt;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
INFOPLIST_OUTPUT_FORMAT = "same-as-input";
|
||||
INFOPLIST_PREPROCESS = NO;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.2;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(OC_APPLICATION_REV_DOMAIN).$(PRODUCT_NAME)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTED_PLATFORMS = macosx;
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = macOS;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
53B979872B84C81F002DA742 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = FileProviderUIExt/FileProviderUIExtRelease.entitlements;
|
||||
CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO;
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = FileProviderUIExt/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = FileProviderUIExt;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
INFOPLIST_OUTPUT_FORMAT = "same-as-input";
|
||||
INFOPLIST_PREPROCESS = NO;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 17.2;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
MARKETING_VERSION = 1.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(OC_APPLICATION_REV_DOMAIN).$(PRODUCT_NAME)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
SKIP_INSTALL = YES;
|
||||
SUPPORTED_PLATFORMS = macosx;
|
||||
SUPPORTS_MACCATALYST = NO;
|
||||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = macOS;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
C2B573991B1CD88000303B36 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
|
@ -1216,6 +1484,15 @@
|
|||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
53B979882B84C820002DA742 /* Build configuration list for PBXNativeTarget "FileProviderUIExt" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
53B979862B84C81F002DA742 /* Debug */,
|
||||
53B979872B84C81F002DA742 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
C2B573981B1CD88000303B36 /* Build configuration list for PBXProject "NextcloudIntegration" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
|
@ -1250,8 +1527,8 @@
|
|||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/nextcloud/NextcloudKit.git";
|
||||
requirement = {
|
||||
branch = develop;
|
||||
kind = branch;
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 2.5.9;
|
||||
};
|
||||
};
|
||||
5307A6E92965DB57001E0C6A /* XCRemoteSwiftPackageReference "realm-swift" */ = {
|
||||
|
@ -1262,6 +1539,22 @@
|
|||
version = 10.33.0;
|
||||
};
|
||||
};
|
||||
5358F2B72BAA045E00E3C729 /* XCRemoteSwiftPackageReference "NextcloudCapabilitiesKit" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/claucambra/NextcloudCapabilitiesKit.git";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 2.0.0;
|
||||
};
|
||||
};
|
||||
53651E422BBC0C7F00ECAC29 /* XCRemoteSwiftPackageReference "SuggestionsTextFieldKit" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/claucambra/SuggestionsTextFieldKit.git";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 1.0.0;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
|
@ -1280,6 +1573,26 @@
|
|||
package = 5307A6E92965DB57001E0C6A /* XCRemoteSwiftPackageReference "realm-swift" */;
|
||||
productName = RealmSwift;
|
||||
};
|
||||
5358F2B82BAA0F5300E3C729 /* NextcloudCapabilitiesKit */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 5358F2B72BAA045E00E3C729 /* XCRemoteSwiftPackageReference "NextcloudCapabilitiesKit" */;
|
||||
productName = NextcloudCapabilitiesKit;
|
||||
};
|
||||
53651E432BBC0CA300ECAC29 /* SuggestionsTextFieldKit */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 53651E422BBC0C7F00ECAC29 /* XCRemoteSwiftPackageReference "SuggestionsTextFieldKit" */;
|
||||
productName = SuggestionsTextFieldKit;
|
||||
};
|
||||
53FE14512B8E1213006C4193 /* NextcloudKit */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 5307A6E42965C6FA001E0C6A /* XCRemoteSwiftPackageReference "NextcloudKit" */;
|
||||
productName = NextcloudKit;
|
||||
};
|
||||
53FE14532B8E1219006C4193 /* NextcloudKit */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 5307A6E42965C6FA001E0C6A /* XCRemoteSwiftPackageReference "NextcloudKit" */;
|
||||
productName = NextcloudKit;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
rootObject = C2B573951B1CD88000303B36 /* Project object */;
|
||||
|
|
Loading…
Reference in a new issue