@@ -55,8 +55,11 @@ class FileMenuViewModel: ObservableObject {
5555 @Published var pendingLeaveShareAction : ( ( ) -> Void ) ?
5656 @Published var isExecutingAction : Bool = false
5757
58+ @Published var dynamicMenuItems : [ MenuItem ] = [ ]
59+ @Published var dynamicHeight : CGFloat = 0
60+
5861 // MARK: - Input Properties
59- let fileViewModel : FileModel
62+ var fileViewModel : FileModel
6063 let menuItems : [ MenuItem ]
6164 let selectedItemCount : Int ?
6265 let selectedFiles : [ FileModel ] ?
@@ -68,7 +71,6 @@ class FileMenuViewModel: ObservableObject {
6871 let cachedFormattedDate : String
6972 let preCalculatedHeight : CGFloat
7073 let archiveName : String ?
71- let accessRoleName : String ?
7274
7375 // MARK: - Private Properties
7476 private var dragStartTime : Date = Date ( )
@@ -80,7 +82,11 @@ class FileMenuViewModel: ObservableObject {
8082
8183 // MARK: - Dependencies
8284 typealias DownloadHandler = ( FileModel , @escaping ( URL ? , Error ? ) -> Void ) -> Void
85+ typealias MenuItemsGenerator = ( FileModel ) -> [ MenuItem ]
86+ typealias FileModelUpdateHandler = ( FileModel ) -> Void
8387 private var downloadHandler : DownloadHandler ?
88+ private var menuItemsGenerator : MenuItemsGenerator ?
89+ private var fileModelUpdateHandler : FileModelUpdateHandler ?
8490 var viewControllerProvider : ( ( ) -> UIViewController ? ) ?
8591 private var capturedPresentingViewController : UIViewController ?
8692
@@ -119,13 +125,15 @@ class FileMenuViewModel: ObservableObject {
119125
120126 if showArchiveInfo, let sharedByArchive = fileViewModel. sharedByArchive {
121127 self . archiveName = sharedByArchive. name
122- self . accessRoleName = fileViewModel. accessRole. title. uppercased ( )
123128 } else {
124129 self . archiveName = nil
125- self . accessRoleName = nil
126130 }
127131
128132 self . preCalculatedHeight = Self . calculateSheetHeight ( for: menuItems, showArchiveInfo: showArchiveInfo && fileViewModel. sharedByArchive != nil )
133+
134+ // Initialize dynamic properties
135+ self . dynamicMenuItems = menuItems
136+ self . dynamicHeight = self . preCalculatedHeight
129137 }
130138
131139 deinit {
@@ -207,15 +215,15 @@ class FileMenuViewModel: ObservableObject {
207215 }
208216
209217 var backgroundOpacity : Double {
210- isAnimating ? max ( 0.0 , 0.3 * ( 1.0 - Double( dragOffset / preCalculatedHeight ) ) ) : 0.0
218+ isAnimating ? max ( 0.0 , 0.3 * ( 1.0 - Double( dragOffset / dynamicHeight ) ) ) : 0.0
211219 }
212220
213221 var regularMenuItems : [ MenuItem ] {
214- return menuItems . filter { $0. type != . delete && $0. type != . unshare }
222+ return dynamicMenuItems . filter { $0. type != . delete && $0. type != . unshare }
215223 }
216224
217225 var destructiveMenuItem : MenuItem ? {
218- return menuItems . first { $0. type == . delete || $0. type == . unshare }
226+ return dynamicMenuItems . first { $0. type == . delete || $0. type == . unshare }
219227 }
220228
221229 var displayTitle : String {
@@ -226,6 +234,103 @@ class FileMenuViewModel: ObservableObject {
226234 }
227235 }
228236
237+ var accessRoleName : String ? {
238+ guard showArchiveInfo, fileViewModel. sharedByArchive != nil else {
239+ return nil
240+ }
241+ return fileViewModel. accessRole. title. uppercased ( )
242+ }
243+
244+ // MARK: - Access Role Update
245+ func fetchUpdatedAccessRole( ) {
246+ guard showArchiveInfo, fileViewModel. sharedByArchive != nil else {
247+ return
248+ }
249+
250+ let itemInfo = ( folderLinkId: fileViewModel. folderLinkId, parentFolderLinkId: fileViewModel. parentFolderLinkId)
251+
252+ let endpoint : FilesEndpoint
253+ if fileViewModel. type. isFolder {
254+ endpoint = FilesEndpoint . getFolder ( itemInfo: itemInfo)
255+ } else {
256+ endpoint = FilesEndpoint . getRecord ( itemInfo: itemInfo)
257+ }
258+
259+ let apiOperation = APIOperation ( endpoint)
260+
261+ apiOperation. execute ( in: APIRequestDispatcher ( ) ) { [ weak self] result in
262+ guard let self = self else { return }
263+
264+ DispatchQueue . main. async {
265+ switch result {
266+ case . json( let response, _) :
267+ if self . fileViewModel. type. isFolder {
268+ guard let model: APIResults < FolderVO > = JSONHelper . decoding (
269+ from: response,
270+ with: APIResults< FolderVO> . decoder
271+ ) ,
272+ model. isSuccessful,
273+ let folderData = model. results. first? . data? . first? . folderVO,
274+ let accessRoleString = folderData. accessRole else {
275+ return
276+ }
277+
278+ let newAccessRole = AccessRole . roleForValue ( accessRoleString)
279+ if self . fileViewModel. accessRole != newAccessRole {
280+ self . fileViewModel. accessRole = newAccessRole
281+ self . updatePermissions ( forAccessRole: accessRoleString)
282+ self . regenerateMenuItemsAndAnimateHeight ( )
283+ }
284+ } else {
285+ guard let model: APIResults < RecordVO > = JSONHelper . decoding (
286+ from: response,
287+ with: APIResults< RecordVO> . decoder
288+ ) ,
289+ model. isSuccessful,
290+ let recordData = model. results. first? . data? . first? . recordVO,
291+ let accessRoleString = recordData. accessRole else {
292+ return
293+ }
294+
295+ let newAccessRole = AccessRole . roleForValue ( accessRoleString)
296+ if self . fileViewModel. accessRole != newAccessRole {
297+ self . fileViewModel. accessRole = newAccessRole
298+ self . updatePermissions ( forAccessRole: accessRoleString)
299+ self . regenerateMenuItemsAndAnimateHeight ( )
300+ }
301+ }
302+
303+ case . error:
304+ break
305+
306+ default :
307+ break
308+ }
309+ }
310+ }
311+ }
312+
313+ private func updatePermissions( forAccessRole accessRoleString: String ) {
314+ let newPermissions = ArchiveVOData . permissions ( forAccessRole: accessRoleString)
315+ self . fileViewModel. permissions = newPermissions
316+ }
317+
318+ private func regenerateMenuItemsAndAnimateHeight( ) {
319+ fileModelUpdateHandler ? ( fileViewModel)
320+
321+ guard let generator = menuItemsGenerator else {
322+ return
323+ }
324+
325+ let newMenuItems = generator ( fileViewModel)
326+ let newHeight = Self . calculateSheetHeight ( for: newMenuItems, showArchiveInfo: showArchiveInfo && fileViewModel. sharedByArchive != nil )
327+
328+ withAnimation ( . easeInOut( duration: 0.3 ) ) {
329+ self . dynamicMenuItems = newMenuItems
330+ self . dynamicHeight = newHeight
331+ }
332+ }
333+
229334 // MARK: - Animation Control
230335 func startPresentationAnimation( ) {
231336 withAnimation ( . easeOut( duration: 0.3 ) ) {
@@ -296,10 +401,18 @@ class FileMenuViewModel: ObservableObject {
296401 downloadHandler = handler
297402 }
298403
404+ func setMenuItemsGenerator( _ generator: @escaping MenuItemsGenerator ) {
405+ menuItemsGenerator = generator
406+ }
407+
408+ func setFileModelUpdateHandler( _ handler: @escaping FileModelUpdateHandler ) {
409+ fileModelUpdateHandler = handler
410+ }
411+
299412 func dismissWithAnimation( ) {
300413 withAnimation ( . easeIn( duration: 0.25 ) ) {
301414 isAnimating = false
302- dragOffset = preCalculatedHeight
415+ dragOffset = dynamicHeight
303416 }
304417
305418 DispatchQueue . main. asyncAfter ( deadline: . now( ) + 0.25 ) {
@@ -326,7 +439,7 @@ class FileMenuViewModel: ObservableObject {
326439
327440 func handleDragEnded( _ value: DragGesture . Value ) {
328441 isDragging = false
329- let threshold = preCalculatedHeight * 0.3
442+ let threshold = dynamicHeight * 0.3
330443
331444 if value. translation. height > threshold || value. predictedEndTranslation. height > threshold {
332445 dismissWithAnimation ( )
0 commit comments