Skip to content

Commit 1b6ea3e

Browse files
committed
settings, fixes new app setup flow, thumbs, refinements
1 parent b7756aa commit 1b6ea3e

File tree

5 files changed

+171
-86
lines changed

5 files changed

+171
-86
lines changed

Django Files/Views/ContentView.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public struct AuthViewContainer: View {
1919

2020
@State var selectedServer: DjangoFilesSession
2121

22+
var customURL: String? = nil
2223

2324
var needsRefresh: Binding<Bool>
2425

@@ -28,7 +29,7 @@ public struct AuthViewContainer: View {
2829
if selectedServer.url != "" {
2930
AuthView(
3031
authController: authController,
31-
httpsUrl: selectedServer.url,
32+
httpsUrl: customURL ?? selectedServer.url,
3233
doReset: authController.url?.absoluteString ?? ""
3334
!= selectedServer.url || !selectedServer.auth,
3435
session: selectedServer

Django Files/Views/FileContextMenu.swift

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,21 @@ struct FileContextMenuButtons: View {
2525
} label: {
2626
Label("Open Preview", systemImage: "arrow.up.forward.app")
2727
}
28-
}
2928

30-
Button {
31-
onCopyShareLink()
32-
notifyClipboard()
33-
} label: {
34-
Label("Copy Share Link", systemImage: "link")
35-
}
3629

37-
Button {
38-
onCopyRawLink()
39-
notifyClipboard()
40-
} label: {
41-
Label("Copy Raw Link", systemImage: "link.circle")
30+
Button {
31+
onCopyShareLink()
32+
notifyClipboard()
33+
} label: {
34+
Label("Copy Share Link", systemImage: "link")
35+
}
36+
37+
Button {
38+
onCopyRawLink()
39+
notifyClipboard()
40+
} label: {
41+
Label("Copy Raw Link", systemImage: "link.circle")
42+
}
4243
}
4344

4445
Button {
@@ -69,18 +70,14 @@ struct FileContextMenuButtons: View {
6970
} label: {
7071
Label("Set Password", systemImage: "key")
7172
}
72-
73+
74+
// Divider()
7375
// Button {
7476
// addToAlbum()
7577
// } label: {
7678
// Label("Add To Album", systemImage: "rectangle.stack.badge.plus")
7779
// }
78-
//
79-
// Button {
80-
// manageAlbums()
81-
// } label: {
82-
// Label("Manage Albums", systemImage: "person.2.crop.square.stack")
83-
// }
80+
//
8481
Divider()
8582

8683
Button {
@@ -96,13 +93,37 @@ struct FileContextMenuButtons: View {
9693
}
9794
}
9895
}
99-
100-
func notifyClipboard() {
101-
// Generate haptic feedback
102-
let generator = UINotificationFeedbackGenerator()
103-
generator.notificationOccurred(.success)
104-
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
105-
ToastManager.shared.showToast(message: "Copied to clipboard")
96+
}
97+
98+
99+
struct FileShareMenu: View {
100+
var onCopyShareLink: () -> Void = {}
101+
var onCopyRawLink: () -> Void = {}
102+
var openRawBrowser: () -> Void = {}
103+
104+
var body: some View {
105+
Group {
106+
Button {
107+
onCopyShareLink()
108+
notifyClipboard()
109+
} label: {
110+
Label("Copy Share Link", systemImage: "link")
111+
}
112+
Button {
113+
onCopyRawLink()
114+
notifyClipboard()
115+
} label: {
116+
Label("Copy Raw Link", systemImage: "link.circle")
117+
}
106118
}
107119
}
108120
}
121+
122+
func notifyClipboard() {
123+
// Generate haptic feedback
124+
let generator = UINotificationFeedbackGenerator()
125+
generator.notificationOccurred(.success)
126+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
127+
ToastManager.shared.showToast(message: "Copied to clipboard")
128+
}
129+
}

Django Files/Views/FileList.swift

Lines changed: 96 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,9 @@ struct FileListView: View {
7979
List {
8080
ForEach(files, id: \.id) { file in
8181
NavigationLink(value: file) {
82-
FileRowView(file: file, isPrivate: file.private, hasPassword: (file.password != ""), hasExpiration: (file.expr != ""))
82+
FileRowView(file: file, isPrivate: file.private, hasPassword: (file.password != ""), hasExpiration: (file.expr != ""), serverURL: URL(string: server.wrappedValue!.url)!)
8383
.contextMenu {
84-
createFileMenuButtons(for: file, isPreviewing: false, isPrivate: file.private, expirationText: $expirationText, passwordText: $passwordText, fileNameText: $fileNameText)
84+
fileContextMenu(for: file, isPreviewing: false, isPrivate: file.private, expirationText: $expirationText, passwordText: $passwordText, fileNameText: $fileNameText)
8585
}
8686
}
8787
.id(file.id)
@@ -118,7 +118,14 @@ struct FileListView: View {
118118
.toolbar {
119119
ToolbarItem(placement: .navigationBarTrailing) {
120120
Menu {
121-
createFileMenuButtons(for: file, isPreviewing: true, isPrivate: file.private, expirationText: $expirationText, passwordText: $passwordText, fileNameText: $fileNameText)
121+
fileShareMenu(for: file)
122+
} label: {
123+
Image(systemName: "square.and.arrow.up")
124+
}
125+
}
126+
ToolbarItem(placement: .navigationBarTrailing) {
127+
Menu {
128+
fileContextMenu(for: file, isPreviewing: true, isPrivate: file.private, expirationText: $expirationText, passwordText: $passwordText, fileNameText: $fileNameText)
122129
} label: {
123130
Image(systemName: "ellipsis.circle")
124131
}
@@ -255,8 +262,20 @@ struct FileListView: View {
255262
}
256263
}
257264

258-
// Helper function to create consistent FileContextMenuButtons
259-
private func createFileMenuButtons(for file: DFFile, isPreviewing: Bool, isPrivate: Bool, expirationText: Binding<String>, passwordText: Binding<String>, fileNameText: Binding<String>) -> FileContextMenuButtons {
265+
private func fileShareMenu(for file: DFFile) -> FileShareMenu {
266+
FileShareMenu(
267+
onCopyShareLink: {
268+
UIPasteboard.general.string = file.url
269+
},
270+
onCopyRawLink: {
271+
UIPasteboard.general.string = file.raw
272+
},
273+
)
274+
}
275+
276+
277+
278+
private func fileContextMenu(for file: DFFile, isPreviewing: Bool, isPrivate: Bool, expirationText: Binding<String>, passwordText: Binding<String>, fileNameText: Binding<String>) -> FileContextMenuButtons {
260279
var isPrivate: Bool = isPrivate
261280
return FileContextMenuButtons(
262281
isPreviewing: isPreviewing,
@@ -506,52 +525,89 @@ struct FileRowView: View {
506525
@State var isPrivate: Bool
507526
@State var hasPassword: Bool
508527
@State var hasExpiration: Bool
528+
let serverURL: URL
509529

510530
private func getIcon() -> String {
511-
switch file.mime {
512-
case "image/jpeg":
531+
if file.mime.hasPrefix("image/") {
513532
return "photo.artframe"
514-
default:
533+
} else if file.mime.hasPrefix("video/") {
534+
return "video.fill"
535+
} else {
515536
return "doc.fill"
516537
}
517538
}
518539

540+
private var thumbnailURL: URL {
541+
var components = URLComponents(url: serverURL.appendingPathComponent("/raw/\(file.name)"), resolvingAgainstBaseURL: true)
542+
components?.queryItems = [URLQueryItem(name: "thumb", value: "true")]
543+
return components?.url ?? serverURL
544+
}
545+
546+
init(file: DFFile, isPrivate: Bool, hasPassword: Bool, hasExpiration: Bool, serverURL: URL) {
547+
self.file = file
548+
self.isPrivate = isPrivate
549+
self.hasPassword = hasPassword
550+
self.hasExpiration = hasExpiration
551+
self.serverURL = serverURL
552+
}
553+
519554
var body: some View {
520-
VStack(alignment: .leading, spacing: 2) {
521-
Text(file.name)
522-
.font(.headline)
523-
.lineLimit(1)
524-
.foregroundColor(.blue)
555+
HStack {
556+
if file.mime.hasPrefix("image/") {
557+
AsyncImage(url: thumbnailURL) { image in
558+
image
559+
.resizable()
560+
.scaledToFill()
561+
} placeholder: {
562+
ProgressView()
563+
}
564+
.frame(width: 64, height: 64)
565+
.clipped()
566+
} else {
567+
Label("", systemImage: getIcon())
568+
.font(.system(size: 32))
569+
.frame(width: 64, height: 64)
570+
.foregroundColor(.white)
571+
572+
}
525573

526-
HStack(spacing: 5) {
527-
Label(file.mime, systemImage: getIcon())
528-
.font(.caption)
529-
.labelStyle(CustomLabel(spacing: 3))
530-
531-
Label(file.userUsername!, systemImage: "person")
532-
.font(.caption)
533-
.labelStyle(CustomLabel(spacing: 3))
534-
535-
Label("", systemImage: "lock")
536-
.font(.caption)
537-
.labelStyle(CustomLabel(spacing: 3))
538-
.opacity(isPrivate ? 1 : 0)
539-
540-
Label("", systemImage: "key")
541-
.font(.caption)
542-
.labelStyle(CustomLabel(spacing: 3))
543-
.opacity(hasPassword ? 1 : 0)
574+
VStack(alignment: .leading, spacing: 2) {
544575

545-
Label("", systemImage: "calendar.badge.exclamationmark")
546-
.font(.caption)
547-
.labelStyle(CustomLabel(spacing: 3))
548-
.opacity(hasExpiration ? 1 : 0)
576+
Text(file.name)
577+
.font(.headline)
578+
.lineLimit(1)
579+
.foregroundColor(.blue)
549580

550-
551-
Text(file.formattedDate())
552-
.font(.caption)
553-
.foregroundColor(.secondary)
554-
.frame(maxWidth: .infinity, alignment: .trailing)
581+
HStack(spacing: 5) {
582+
Label(file.mime, systemImage: getIcon())
583+
.font(.caption)
584+
.labelStyle(CustomLabel(spacing: 3))
585+
586+
Label(file.userUsername!, systemImage: "person")
587+
.font(.caption)
588+
.labelStyle(CustomLabel(spacing: 3))
589+
590+
Label("", systemImage: "lock")
591+
.font(.caption)
592+
.labelStyle(CustomLabel(spacing: 3))
593+
.opacity(isPrivate ? 1 : 0)
594+
595+
Label("", systemImage: "key")
596+
.font(.caption)
597+
.labelStyle(CustomLabel(spacing: 3))
598+
.opacity(hasPassword ? 1 : 0)
599+
600+
Label("", systemImage: "calendar.badge.exclamationmark")
601+
.font(.caption)
602+
.labelStyle(CustomLabel(spacing: 3))
603+
.opacity(hasExpiration ? 1 : 0)
604+
605+
606+
Text(file.formattedDate())
607+
.font(.caption)
608+
.foregroundColor(.secondary)
609+
.frame(maxWidth: .infinity, alignment: .trailing)
610+
}
555611
}
556612
}
557613
}

Django Files/Views/ShortList.swift

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ struct ShortListView: View {
1919
private let shortsPerPage = 50
2020

2121
var body: some View {
22-
NavigationView {
22+
NavigationStack {
2323
List {
2424
ForEach(shorts) { short in
2525
ShortRow(short: short)
@@ -53,18 +53,10 @@ struct ShortListView: View {
5353
.navigationTitle("Short URLs")
5454
.toolbar {
5555
ToolbarItem(placement: .navigationBarTrailing) {
56-
Menu {
57-
Button {
58-
// Create album action
59-
} label: {
60-
Label("Create Short", systemImage: "plus")
61-
}
62-
63-
Button("Refresh") {
64-
loadInitialShorts()
65-
}
56+
Button {
57+
// Create album action
6658
} label: {
67-
Image(systemName: "ellipsis.circle")
59+
Label("Create Short", systemImage: "plus")
6860
}
6961
}
7062
}

Django Files/Views/TabView.swift

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,31 @@ struct TabViewWindow: View {
3434
AlbumListView(server: $sessionManager.selectedSession)
3535
}
3636

37-
3837
Tab("Shorts", systemImage: "link") {
3938
ShortListView(server: $sessionManager.selectedSession)
4039
}
41-
42-
Tab("Web", systemImage: "globe"){
40+
Tab("Server List", systemImage: "server.rack") {
41+
ServerSelector(selectedSession: $sessionManager.selectedSession)
42+
}
43+
Tab("User Settings", systemImage: "person") {
44+
if let selectedSession = sessionManager.selectedSession {
45+
AuthViewContainer(
46+
selectedServer: selectedSession,
47+
customURL: selectedSession.url + "/settings/user/",
48+
needsRefresh: $needsRefresh
49+
)
50+
}
51+
}
52+
Tab("Server Settings", systemImage: "person.2.badge.gearshape") {
53+
if let selectedSession = sessionManager.selectedSession {
54+
AuthViewContainer(
55+
selectedServer: selectedSession,
56+
customURL: selectedSession.url + "/settings/site/",
57+
needsRefresh: $needsRefresh
58+
)
59+
}
60+
}
61+
Tab("Mobile Web (Legacy)", systemImage: "globe"){
4362
if let selectedSession = sessionManager.selectedSession {
4463
AuthViewContainer(
4564
selectedServer: selectedSession,
@@ -50,10 +69,6 @@ struct TabViewWindow: View {
5069
Text("Please select a server")
5170
}
5271
}
53-
54-
Tab("Server List", systemImage: "server.rack") {
55-
ServerSelector(selectedSession: $sessionManager.selectedSession)
56-
}
5772
}
5873
.onAppear {
5974
sessionManager.loadLastSelectedSession(from: sessions)

0 commit comments

Comments
 (0)