Skip to content

Commit 94e7543

Browse files
authored
Server select in share sheet (#33)
* Server select in share sheet * handle file upload on share sheet * encryption plist entry
1 parent 6ec077c commit 94e7543

File tree

3 files changed

+84
-31
lines changed

3 files changed

+84
-31
lines changed

Django Files/Info.plist

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
33
<plist version="1.0">
44
<dict>
5+
<key>ITSAppUsesNonExemptEncryption</key>
6+
<false/>
57
<key>CFBundleURLTypes</key>
68
<array>
79
<dict>

UploadAndCopy/Base.lproj/MainInterface.storyboard

+25-14
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<dependencies>
55
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23721"/>
66
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
7+
<capability name="UIMenu" message="Requires Xcode 11 or later." minToolsVersion="11.0" requiredIntegratedClassName="UICommandDiff"/>
78
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
89
</dependencies>
910
<scenes>
@@ -16,14 +17,14 @@
1617
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
1718
<subviews>
1819
<view contentMode="bottom" id="mZN-4f-xCI">
19-
<rect key="frame" x="509" y="673" width="358" height="339"/>
20+
<rect key="frame" x="509" y="653" width="358" height="359"/>
2021
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
2122
<subviews>
22-
<visualEffectView opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="GO1-Km-czD">
23-
<rect key="frame" x="0.0" y="0.0" width="358" height="339"/>
23+
<visualEffectView opaque="NO" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="GO1-Km-czD">
24+
<rect key="frame" x="0.0" y="0.0" width="358" height="359"/>
2425
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
2526
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="jkr-JH-ISC">
26-
<rect key="frame" x="0.0" y="0.0" width="358" height="339"/>
27+
<rect key="frame" x="0.0" y="0.0" width="358" height="359"/>
2728
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
2829
</view>
2930
<blurEffect style="regular"/>
@@ -41,24 +42,31 @@
4142
<rect key="frame" x="24" y="37" width="311" height="210"/>
4243
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
4344
</imageView>
45+
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" showsMenuAsPrimaryAction="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="lIw-lC-Qye" userLabel="Servers">
46+
<rect key="frame" x="8" y="258" width="342" height="30"/>
47+
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
48+
<menu key="menu" id="EuD-Tg-ocz">
49+
<children>
50+
<command title="Item 1" id="Aum-d2-ejp"/>
51+
<command title="Item 2" id="16T-AE-w7d"/>
52+
</children>
53+
</menu>
54+
<state key="normal" title="Button"/>
55+
<buttonConfiguration key="configuration" style="tinted" title="Button">
56+
<color key="baseBackgroundColor" systemColor="systemGrayColor"/>
57+
</buttonConfiguration>
58+
</button>
4459
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" showsMenuAsPrimaryAction="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="qbS-Ny-dwp" userLabel="Share">
45-
<rect key="frame" x="205" y="281" width="130" height="50"/>
60+
<rect key="frame" x="205" y="297" width="145" height="50"/>
4661
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
4762
<state key="normal" title="Button"/>
4863
<buttonConfiguration key="configuration" style="filled" title="Share"/>
4964
<connections>
5065
<action selector="onShare:" destination="j1y-V4-xli" eventType="primaryActionTriggered" id="ve5-Ud-qIO"/>
5166
</connections>
5267
</button>
53-
<label opaque="NO" userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="UXe-96-hUT" userLabel="ServerName">
54-
<rect key="frame" x="24" y="255" width="311" height="21"/>
55-
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
56-
<fontDescription key="fontDescription" type="system" pointSize="17"/>
57-
<nil key="textColor"/>
58-
<nil key="highlightedColor"/>
59-
</label>
6068
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="hrx-Ux-tAw">
61-
<rect key="frame" x="24" y="281" width="130" height="50"/>
69+
<rect key="frame" x="8" y="297" width="146" height="50"/>
6270
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
6371
<buttonConfiguration key="configuration" style="gray" title="Cancel">
6472
<color key="baseForegroundColor" systemColor="lightTextColor"/>
@@ -107,9 +115,9 @@
107115
<connections>
108116
<outlet property="CancelButton" destination="hrx-Ux-tAw" id="1w3-yX-eza"/>
109117
<outlet property="ImageView" destination="T3b-pJ-MQ3" id="uTe-ZR-hRh"/>
110-
<outlet property="ServerLabel" destination="UXe-96-hUT" id="ITX-y0-3rY"/>
111118
<outlet property="ShareButton" destination="qbS-Ny-dwp" id="pgM-iP-QEf"/>
112119
<outlet property="activityIndicator" destination="R4J-pt-1BH" id="aTU-cF-bWo"/>
120+
<outlet property="availableServers" destination="lIw-lC-Qye" id="MNJ-N7-UbZ"/>
113121
<outlet property="progressBar" destination="UxW-kr-0i4" id="klH-Vg-pEi"/>
114122
<outlet property="shareLabel" destination="h6M-o6-yR9" id="IY1-ab-l1d"/>
115123
<outlet property="shortText" destination="etx-MM-w5d" id="HZN-xL-9My"/>
@@ -126,5 +134,8 @@
126134
<systemColor name="lightTextColor">
127135
<color white="1" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
128136
</systemColor>
137+
<systemColor name="systemGrayColor">
138+
<color red="0.5568627451" green="0.5568627451" blue="0.57647058819999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
139+
</systemColor>
129140
</resources>
130141
</document>

UploadAndCopy/ShareViewController.swift

+57-17
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class ShareViewController: UIViewController, UITextFieldDelegate, URLSessionTask
2828
@IBOutlet weak var cancelButton: UIButton!
2929
@IBOutlet weak var progressBar: UIProgressView!
3030
@IBOutlet weak var imageView: UIImageView!
31-
@IBOutlet weak var serverLabel: UILabel!
31+
@IBOutlet weak var availableServers: UIButton!
3232
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
3333
@IBOutlet weak var shortTextLabel: UILabel!
3434
@IBOutlet weak var shortText: UITextField!
@@ -53,28 +53,16 @@ class ShareViewController: UIViewController, UITextFieldDelegate, URLSessionTask
5353
self.showMessageAndDismiss(message: "Nothing to share.")
5454
return
5555
}
56-
guard let server = getDefaultServer() else {
57-
self.showMessageAndDismiss(message: "No servers configured.")
58-
return
59-
}
60-
session = server
6156

62-
if URL(string: server.url) == nil{
63-
self.showMessageAndDismiss(message: "Server '\(server.url)' is invalid.")
64-
return
65-
}
66-
if URL(string: server.url)?.scheme == nil{
67-
self.showMessageAndDismiss(message: "Server '\(server.url)' scheme (http or https) is invalid.")
68-
return
69-
}
57+
getAvailableServers()
7058

7159
self.progressBar.isHidden = true
7260
var loaded: Bool = false
7361
for extensionItem in extensionItems {
7462
for ele in extensionItem.attachments! {
7563
let itemProvider = ele
7664

77-
if itemProvider.hasItemConformingToTypeIdentifier("public.image"){
65+
if itemProvider.hasItemConformingToTypeIdentifier("public.image") {
7866
itemProvider.loadItem(forTypeIdentifier: "public.image", options: nil, completionHandler: { (item, error) in
7967
DispatchQueue.main.async {
8068
self.shortText.isHidden = true
@@ -107,6 +95,19 @@ class ShareViewController: UIViewController, UITextFieldDelegate, URLSessionTask
10795
loaded = true
10896
break
10997
}
98+
else if itemProvider.hasItemConformingToTypeIdentifier("public.file-url") {
99+
itemProvider.loadItem(forTypeIdentifier: "public.file-url", options: nil, completionHandler: { (item, error) in
100+
DispatchQueue.main.async {
101+
self.shortText.isHidden = true
102+
self.shareLabel.text = "Upload File"
103+
self.shareURL = item as? URL
104+
self.activityIndicator.stopAnimating()
105+
self.shareButton.isEnabled = true
106+
}
107+
})
108+
loaded = true
109+
break
110+
}
110111
else if itemProvider.hasItemConformingToTypeIdentifier("public.url"){
111112
itemProvider.loadItem(forTypeIdentifier: "public.url", options: nil, completionHandler: { (item, error) in
112113
DispatchQueue.main.async {
@@ -143,7 +144,46 @@ class ShareViewController: UIViewController, UITextFieldDelegate, URLSessionTask
143144
}
144145
}
145146

146-
serverLabel.text = session.url
147+
}
148+
149+
func getAvailableServers() {
150+
let context = sharedModelContainer.mainContext
151+
152+
do {
153+
let descriptor = FetchDescriptor<DjangoFilesSession>(
154+
predicate: #Predicate { $0.auth == true },
155+
sortBy: [SortDescriptor(\.url)]
156+
)
157+
let sessions = try context.fetch(descriptor)
158+
159+
guard !sessions.isEmpty else {
160+
availableServers.setTitle("No servers available", for: .normal)
161+
return
162+
}
163+
164+
// Pick the default session, or fallback to the first one
165+
let defaultSession = sessions.first(where: { $0.defaultSession }) ?? sessions.first!
166+
167+
// Build menu actions
168+
let actions = sessions.map { session in
169+
UIAction(title: session.url) { _ in
170+
self.session = session
171+
self.availableServers.setTitle(session.url, for: .normal)
172+
}
173+
}
174+
175+
let menu = UIMenu(title: "Select a Server", options: .displayInline, children: actions)
176+
availableServers.menu = menu
177+
availableServers.showsMenuAsPrimaryAction = true
178+
179+
// Set the default selected title and session
180+
availableServers.setTitle(defaultSession.url, for: .normal)
181+
session = defaultSession
182+
183+
} catch {
184+
self.showMessageAndDismiss(message: error.localizedDescription)
185+
availableServers.setTitle("Error loading servers", for: .normal)
186+
}
147187
}
148188

149189
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
@@ -165,7 +205,7 @@ class ShareViewController: UIViewController, UITextFieldDelegate, URLSessionTask
165205
}
166206
}
167207
catch {
168-
print(error)
208+
self.showMessageAndDismiss(message: error.localizedDescription)
169209
}
170210
return selectedServer
171211
}

0 commit comments

Comments
 (0)