@@ -9,25 +9,6 @@ import SwiftUI
9
9
import SwiftData
10
10
import Foundation
11
11
12
-
13
- struct FileListNavStack : View {
14
- let server : Binding < DjangoFilesSession ? >
15
-
16
- @State private var navigationPath = NavigationPath ( )
17
-
18
- var body : some View {
19
- if ( server. wrappedValue != nil ) {
20
- ZStack {
21
- NavigationStack ( path: $navigationPath) {
22
- FileListView ( server: server, albumID: nil )
23
- }
24
- }
25
- } else {
26
- Label ( " No server selected. " , systemImage: " exclamationmark.triangle " )
27
- }
28
- }
29
- }
30
-
31
12
struct CustomLabel : LabelStyle {
32
13
var spacing : Double = 0.0
33
14
@@ -141,6 +122,7 @@ struct FileRowView: View {
141
122
struct FileListView : View {
142
123
let server : Binding < DjangoFilesSession ? >
143
124
let albumID : Int ?
125
+ let navigationPath : Binding < NavigationPath >
144
126
145
127
@State private var files : [ DFFile ] = [ ]
146
128
@State private var currentPage = 1
@@ -151,7 +133,6 @@ struct FileListView: View {
151
133
152
134
@State private var previewFile : Bool = true
153
135
@State private var selectedFile : DFFile ? = nil
154
- @State private var navigationPath = NavigationPath ( )
155
136
156
137
@State private var showingDeleteConfirmation = false
157
138
@State private var fileIDsToDelete : [ Int ] = [ ]
@@ -172,197 +153,184 @@ struct FileListView: View {
172
153
@State private var redirectURLs : [ String : String ] = [ : ]
173
154
174
155
var body : some View {
175
- if server. wrappedValue != nil {
176
- List {
177
- ForEach ( files, id: \. id) { file in
178
- NavigationLink ( value: file) {
179
- FileRowView ( file: file, isPrivate: file. private, hasPassword: ( file. password != " " ) , hasExpiration: ( file. expr != " " ) , serverURL: URL ( string: server. wrappedValue!. url) !)
180
- . contextMenu {
181
- fileContextMenu ( for: file, isPreviewing: false , isPrivate: file. private, expirationText: $expirationText, passwordText: $passwordText, fileNameText: $fileNameText)
182
- }
183
- }
184
- . id ( file. id)
185
-
186
- // If this is the last item and we have more pages, load more when it appears
187
- if hasNextPage && file. id == files. last? . id {
188
- Color . clear
189
- . frame ( height: 20 )
190
- . onAppear {
191
- loadNextPage ( )
192
- }
193
- }
156
+ List {
157
+ ForEach ( files, id: \. id) { file in
158
+ NavigationLink ( value: file) {
159
+ FileRowView ( file: file, isPrivate: file. private, hasPassword: ( file. password != " " ) , hasExpiration: ( file. expr != " " ) , serverURL: URL ( string: server. wrappedValue!. url) !)
160
+ . contextMenu {
161
+ fileContextMenu ( for: file, isPreviewing: false , isPrivate: file. private, expirationText: $expirationText, passwordText: $passwordText, fileNameText: $fileNameText)
162
+ }
194
163
}
164
+ . id ( file. id)
195
165
196
- if isLoading && hasNextPage {
197
- HStack {
198
- ProgressView ( )
199
- }
166
+ if hasNextPage && file. id == files. last? . id {
167
+ Color . clear
168
+ . frame ( height: 20 )
169
+ . onAppear {
170
+ loadNextPage ( )
171
+ }
200
172
}
201
173
}
202
- . navigationDestination ( for: DFFile . self) { file in
203
- ZStack {
204
- if redirectURLs [ file. raw] == nil {
205
- ProgressView ( )
206
- . onAppear {
207
- Task {
208
- await loadRedirectURL ( for: file)
209
- }
174
+
175
+ if isLoading && hasNextPage {
176
+ HStack {
177
+ ProgressView ( )
178
+ }
179
+ }
180
+ }
181
+ . navigationDestination ( for: DFFile . self) { file in
182
+ ZStack {
183
+ if redirectURLs [ file. raw] == nil {
184
+ ProgressView ( )
185
+ . onAppear {
186
+ Task {
187
+ await loadRedirectURL ( for: file)
210
188
}
211
- } else {
212
- ContentPreview ( mimeType : file . mime , fileURL : URL ( string : redirectURLs [ file . raw ] ! ) )
213
- . navigationTitle ( file. name )
214
- . navigationBarTitleDisplayMode ( . inline )
215
- . toolbar {
216
- ToolbarItem ( placement : . navigationBarTrailing ) {
217
- Menu {
218
- fileShareMenu ( for : file )
219
- } label : {
220
- Image ( systemName : " square.and.arrow.up " )
221
- }
189
+ }
190
+ } else {
191
+ ContentPreview ( mimeType : file . mime , fileURL : URL ( string : redirectURLs [ file. raw ] ! ) )
192
+ . navigationTitle ( file . name )
193
+ . navigationBarTitleDisplayMode ( . inline )
194
+ . toolbar {
195
+ ToolbarItem ( placement : . navigationBarTrailing ) {
196
+ Menu {
197
+ fileShareMenu ( for : file )
198
+ } label : {
199
+ Image ( systemName : " square.and.arrow.up " )
222
200
}
223
- ToolbarItem ( placement : . navigationBarTrailing ) {
224
- Menu {
225
- fileContextMenu ( for : file , isPreviewing : true , isPrivate : file . private , expirationText : $expirationText , passwordText : $passwordText , fileNameText : $fileNameText )
226
- } label : {
227
- Image ( systemName : " ellipsis.circle " )
228
- }
201
+ }
202
+ ToolbarItem ( placement : . navigationBarTrailing ) {
203
+ Menu {
204
+ fileContextMenu ( for : file , isPreviewing : true , isPrivate : file . private , expirationText : $expirationText , passwordText : $passwordText , fileNameText : $fileNameText )
205
+ } label : {
206
+ Image ( systemName : " ellipsis.circle " )
229
207
}
230
208
}
231
- }
209
+ }
232
210
}
233
211
}
234
- . onChange ( of : selectedFile ) { oldValue , newValue in
235
- if let file = newValue {
236
- navigationPath . append ( file )
237
- selectedFile = nil // Reset after navigation
238
- }
212
+ }
213
+ . listStyle ( . plain )
214
+ . refreshable {
215
+ Task {
216
+ await refreshFiles ( )
239
217
}
240
- . alert ( " Delete File " , isPresented: $showingDeleteConfirmation) {
241
- Button ( " Cancel " , role: . cancel) { }
242
- Button ( " Delete " , role: . destructive) {
243
- Task {
244
- await deleteFiles ( fileIDs: fileIDsToDelete)
245
-
246
- // Return to the file list if we're in a detail view
247
- if navigationPath. count > 0 {
248
- navigationPath. removeLast ( )
249
- }
218
+ }
219
+ . navigationTitle ( server. wrappedValue != nil ? " Files ( \( URL ( string: server. wrappedValue!. url) ? . host ?? " unknown " ) ) " : " Files " )
220
+ . toolbar {
221
+ ToolbarItem ( placement: . navigationBarTrailing) {
222
+ Menu {
223
+ Button ( action: {
224
+ showingUploadSheet = true
225
+ } ) {
226
+ Label ( " Upload File " , systemImage: " arrow.up.doc " )
250
227
}
251
- }
252
- } message: {
253
- Text ( " Are you sure you want to delete \" \( fileNameToDelete) \" ? " )
254
- }
255
- . alert ( " Set File Expiration " , isPresented: $showingExpirationDialog) {
256
- TextField ( " Enter expiration " , text: $expirationText)
257
- Button ( " Cancel " , role: . cancel) {
258
- fileToExpire = nil
259
- }
260
- Button ( " Set " ) {
261
- if let file = fileToExpire {
262
- let expirationValue = expirationText
228
+ Button ( action: {
263
229
Task {
264
- await setFileExpr ( file: file, expr: expirationValue)
265
- await MainActor . run {
266
- expirationText = " "
267
- fileToExpire = nil
268
- }
230
+ await uploadClipboard ( )
269
231
}
232
+ } ) {
233
+ Label ( " Upload Clipboard " , systemImage: " clipboard " )
234
+ }
235
+ Button ( action: {
236
+ // Create a short
237
+ } ) {
238
+ Label ( " Create Short " , systemImage: " link.badge.plus " )
270
239
}
240
+ Button ( action: {
241
+ // Create an Album
242
+ } ) {
243
+ Label ( " Create Album " , systemImage: " photo.badge.plus " )
244
+ }
245
+ } label: {
246
+ Image ( systemName: " plus " )
271
247
}
272
- } message: {
273
- Text ( " Enter time until file expiration. Examples: 1h, 5days, 2y " )
274
248
}
275
- . alert ( " Set File Password " , isPresented: $showingPasswordDialog) {
276
- TextField ( " Enter password " , text: $passwordText)
277
- Button ( " Cancel " , role: . cancel) {
278
- fileToPassword = nil
279
- }
280
- Button ( " Set " ) {
281
- if let file = fileToPassword {
282
- let passwordValue = passwordText // Capture the value
283
- Task {
284
- await setFilePassword ( file: file, password: passwordValue)
285
- // Only clear after the API call completes
286
- await MainActor . run {
287
- passwordText = " "
288
- fileToPassword = nil
289
- }
290
- }
249
+ }
250
+ . sheet ( isPresented: $showingUploadSheet,
251
+ onDismiss: { Task { await refreshFiles ( ) } }
252
+ ) {
253
+ if let serverInstance = server. wrappedValue {
254
+ FileUploadView ( server: serverInstance)
255
+ }
256
+ }
257
+ . alert ( " Delete File " , isPresented: $showingDeleteConfirmation) {
258
+ Button ( " Cancel " , role: . cancel) { }
259
+ Button ( " Delete " , role: . destructive) {
260
+ Task {
261
+ await deleteFiles ( fileIDs: fileIDsToDelete)
262
+
263
+ // Return to the file list if we're in a detail view
264
+ if navigationPath. wrappedValue. count > 0 {
265
+ navigationPath. wrappedValue. removeLast ( )
291
266
}
292
267
}
293
- } message: {
294
- Text ( " Enter a password for the file. " )
295
268
}
296
- . alert ( " Rename File " , isPresented: $showingRenameDialog) {
297
- TextField ( " New File Name " , text: $fileNameText)
298
- Button ( " Cancel " , role: . cancel) {
299
- fileToRename = nil
300
- }
301
- Button ( " Set " ) {
302
- if let file = fileToRename {
303
- let fileNameValue = fileNameText // Capture the value
304
- Task {
305
- await renameFile ( file: file, name: fileNameValue)
306
- // Only clear after the API call completes
307
- await MainActor . run {
308
- fileNameText = " "
309
- fileToRename = nil
310
- }
269
+ } message: {
270
+ Text ( " Are you sure you want to delete \" \( fileNameToDelete) \" ? " )
271
+ }
272
+ . alert ( " Set File Expiration " , isPresented: $showingExpirationDialog) {
273
+ TextField ( " Enter expiration " , text: $expirationText)
274
+ Button ( " Cancel " , role: . cancel) {
275
+ fileToExpire = nil
276
+ }
277
+ Button ( " Set " ) {
278
+ if let file = fileToExpire {
279
+ let expirationValue = expirationText
280
+ Task {
281
+ await setFileExpr ( file: file, expr: expirationValue)
282
+ await MainActor . run {
283
+ expirationText = " "
284
+ fileToExpire = nil
311
285
}
312
286
}
313
287
}
314
- } message: {
315
- Text ( " Enter a new name for this file. " )
316
288
}
317
- . sheet ( isPresented: $showingUploadSheet,
318
- onDismiss: { Task { await refreshFiles ( ) } }
319
- ) {
320
- if let serverInstance = server. wrappedValue {
321
- FileUploadView ( server: serverInstance)
322
- }
289
+ } message: {
290
+ Text ( " Enter time until file expiration. Examples: 1h, 5days, 2y " )
291
+ }
292
+ . alert ( " Set File Password " , isPresented: $showingPasswordDialog) {
293
+ TextField ( " Enter password " , text: $passwordText)
294
+ Button ( " Cancel " , role: . cancel) {
295
+ fileToPassword = nil
323
296
}
324
- . toolbar {
325
- ToolbarItem ( placement: . navigationBarTrailing) {
326
- Menu {
327
- Button ( action: {
328
- showingUploadSheet = true
329
- } ) {
330
- Label ( " Upload File " , systemImage: " arrow.up.doc " )
331
- }
332
- Button ( action: {
333
- Task {
334
- await uploadClipboard ( )
335
- }
336
- } ) {
337
- Label ( " Upload Clipboard " , systemImage: " clipboard " )
338
- }
339
- Button ( action: {
340
- // Create a short
341
- } ) {
342
- Label ( " Create Short " , systemImage: " link.badge.plus " )
343
- }
344
- Button ( action: {
345
- // Create an Album
346
- } ) {
347
- Label ( " Create Album " , systemImage: " photo.badge.plus " )
297
+ Button ( " Set " ) {
298
+ if let file = fileToPassword {
299
+ let passwordValue = passwordText
300
+ Task {
301
+ await setFilePassword ( file: file, password: passwordValue)
302
+ await MainActor . run {
303
+ passwordText = " "
304
+ fileToPassword = nil
348
305
}
349
- } label: {
350
- Image ( systemName: " plus " )
351
306
}
352
307
}
353
308
}
354
- . listStyle ( . plain)
355
- . refreshable {
356
- Task {
357
- await refreshFiles ( )
358
- }
309
+ } message: {
310
+ Text ( " Enter a password for the file. " )
311
+ }
312
+ . alert ( " Rename File " , isPresented: $showingRenameDialog) {
313
+ TextField ( " New File Name " , text: $fileNameText)
314
+ Button ( " Cancel " , role: . cancel) {
315
+ fileToRename = nil
359
316
}
360
- . navigationTitle ( server. wrappedValue != nil ? " Files ( \( URL ( string: server. wrappedValue!. url) ? . host ?? " unknown " ) ) " : " Files " )
361
- . onAppear {
362
- loadFiles ( )
317
+ Button ( " Set " ) {
318
+ if let file = fileToRename {
319
+ let fileNameValue = fileNameText
320
+ Task {
321
+ await renameFile ( file: file, name: fileNameValue)
322
+ await MainActor . run {
323
+ fileNameText = " "
324
+ fileToRename = nil
325
+ }
326
+ }
327
+ }
363
328
}
364
- } else {
365
- Label ( " No server selected. " , systemImage: " exclamationmark.triangle " )
329
+ } message: {
330
+ Text ( " Enter a new name for this file. " )
331
+ }
332
+ . onAppear {
333
+ loadFiles ( )
366
334
}
367
335
}
368
336
0 commit comments