From de11a2e3bce4d909e774ceb6c54e8e46c52bbf04 Mon Sep 17 00:00:00 2001 From: Dhanesh Katre Date: Wed, 17 Sep 2025 13:52:10 +0530 Subject: [PATCH 1/4] Fixed: 1. Singular & Plural in items (Objekte) for Albums Grid View 2. Prepopulating album's rename popup with OG name 3. Increased the size of items in Photos Grid --- .../Details/AlbumDetailsViewModel.swift | 1 + .../Presentation/Details/PhotosGridView.swift | 29 ++++++++++++------ .../Presentation/List/AlbumsGridView.swift | 6 +++- .../de.lproj/Localizable.strings | Bin 190770 -> 190898 bytes .../en.lproj/Localizable.strings | 1 + 5 files changed, 27 insertions(+), 10 deletions(-) diff --git a/iOSClient/Albums/Presentation/Details/AlbumDetailsViewModel.swift b/iOSClient/Albums/Presentation/Details/AlbumDetailsViewModel.swift index 2f1c7508ef..44033644e1 100644 --- a/iOSClient/Albums/Presentation/Details/AlbumDetailsViewModel.swift +++ b/iOSClient/Albums/Presentation/Details/AlbumDetailsViewModel.swift @@ -37,6 +37,7 @@ class AlbumDetailsViewModel: ObservableObject { self.account = account self.album = album self.screenTitle = album.name + self.newAlbumName = album.name registerPublishers() loadAlbumPhotos() } diff --git a/iOSClient/Albums/Presentation/Details/PhotosGridView.swift b/iOSClient/Albums/Presentation/Details/PhotosGridView.swift index 9515bc45ba..231d5c0744 100644 --- a/iOSClient/Albums/Presentation/Details/PhotosGridView.swift +++ b/iOSClient/Albums/Presentation/Details/PhotosGridView.swift @@ -10,18 +10,29 @@ import SwiftUI struct PhotosGridView: View { + @Environment(\.horizontalSizeClass) var horizontalSizeClass + let photos: [AlbumPhoto : tableMetadata?] let onAddPhotosIntent: () -> Void - private let columns = [ - GridItem( - .adaptive( - minimum: 100, - maximum: 300 - ), - spacing: 1 - ) - ] + private var columns: [GridItem] { + let min: CGFloat + let max: CGFloat + + if horizontalSizeClass == .compact { + // iPhone + min = 120 + max = 160 + } else { + // iPad + min = 200 + max = 300 + } + + return [ + GridItem(.adaptive(minimum: min, maximum: max), spacing: 1) + ] + } var body: some View { diff --git a/iOSClient/Albums/Presentation/List/AlbumsGridView.swift b/iOSClient/Albums/Presentation/List/AlbumsGridView.swift index d09b6c61ef..c201b83b09 100644 --- a/iOSClient/Albums/Presentation/List/AlbumsGridView.swift +++ b/iOSClient/Albums/Presentation/List/AlbumsGridView.swift @@ -64,7 +64,11 @@ struct AlbumsGridView: View { private func makeSubtitle(for album: Album) -> String? { guard let count = album.itemCount else { return nil } - var parts: [String] = ["\(count) \(NSLocalizedString("_albums_list_entities_", comment: ""))"] + let quantifyingString = (count == 1) + ? NSLocalizedString("_albums_list_entity_", comment: "") + : NSLocalizedString("_albums_list_entities_", comment: "") + + var parts: [String] = ["\(count) \(quantifyingString)"] if count > 0, let end = album.endDate { let formatter = DateFormatter() diff --git a/iOSClient/Supporting Files/de.lproj/Localizable.strings b/iOSClient/Supporting Files/de.lproj/Localizable.strings index e0b4b5f89b48f457d12ec8db42f15ef12cc21dc1..c84f05a90085a1981680fec29bbaf29792bb4a78 100644 GIT binary patch delta 50 zcmV-20L}lh(hIWF3xI?Hv;r*Hm*DyV1ea=q0Wy=Y4H64a0Ac`Y0A&Dc0Cbm;&;lj5 IB-jED5&Xsx%K!iX delta 19 bcmdmVnS0YE?uHh|ElfH$wy(Lt#3l#;TPO%K diff --git a/iOSClient/Supporting Files/en.lproj/Localizable.strings b/iOSClient/Supporting Files/en.lproj/Localizable.strings index c53226c598..ff7eafd500 100644 --- a/iOSClient/Supporting Files/en.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/en.lproj/Localizable.strings @@ -1355,6 +1355,7 @@ "_albums_list_empty_subheading_" = "You can organize all your photos in as many albums as you like. You haven't created an album yet."; "_albums_list_empty_new_album_btn_" = "Create album"; "_albums_list_own_albums_heading_" = "My albums"; +"_albums_list_entity_" = "Item"; "_albums_list_entities_" = "Items"; "_albums_list_new_album_popup_title_" = "Create new Album"; "_albums_list_new_album_popup_desc_" = "Please enter an album name between 3 and 30 characters."; From 203165e342a2d0ef9f2cc0ba86e751bbd6ece87e Mon Sep 17 00:00:00 2001 From: Dhanesh Katre Date: Thu, 18 Sep 2025 13:11:59 +0530 Subject: [PATCH 2/4] Changes: 1. Minor code refactorings 2. Photo selection count in selection sheet now works from NCMedia and updates in real time --- .../Details/AlbumDetailsScreen.swift | 8 ++---- .../Presentation/List/AlbumsListScreen.swift | 8 ++---- .../List/AlbumsListViewModel.swift | 3 --- .../NCMediaViewRepresentable.swift | 10 +++---- .../PhotoSelection/PhotoSelectionSheet.swift | 25 ++++++++++++------ iOSClient/Media/NCMedia.swift | 7 ++++- .../de.lproj/Localizable.strings | Bin 190898 -> 191200 bytes .../en.lproj/Localizable.strings | 2 ++ 8 files changed, 33 insertions(+), 30 deletions(-) diff --git a/iOSClient/Albums/Presentation/Details/AlbumDetailsScreen.swift b/iOSClient/Albums/Presentation/Details/AlbumDetailsScreen.swift index 86c1e6d747..6850d0baa9 100644 --- a/iOSClient/Albums/Presentation/Details/AlbumDetailsScreen.swift +++ b/iOSClient/Albums/Presentation/Details/AlbumDetailsScreen.swift @@ -46,12 +46,8 @@ struct AlbumDetailsScreen: View { albumName: $viewModel.newAlbumName, error: viewModel.newAlbumNameError, isForRenamingAlbum: true, - onCreate: { - viewModel.onRenameAlbumPopupConfirm() - }, - onCancel: { - viewModel.onRenameAlbumPopupCancel() - } + onCreate: viewModel.onRenameAlbumPopupConfirm, + onCancel: viewModel.onRenameAlbumPopupCancel ) .alert( NSLocalizedString("_albums_delete_album_popup_title_", comment: ""), diff --git a/iOSClient/Albums/Presentation/List/AlbumsListScreen.swift b/iOSClient/Albums/Presentation/List/AlbumsListScreen.swift index e416b0a668..d51e9984bc 100644 --- a/iOSClient/Albums/Presentation/List/AlbumsListScreen.swift +++ b/iOSClient/Albums/Presentation/List/AlbumsListScreen.swift @@ -51,12 +51,8 @@ struct AlbumsListScreen: View { isPresented: $viewModel.isNewAlbumCreationPopupVisible, albumName: $viewModel.newAlbumName, error: viewModel.newAlbumNameError, - onCreate: { - viewModel.onNewAlbumPopupCreate() - }, - onCancel: { - viewModel.onNewAlbumPopupCancel() - } + onCreate: viewModel.onNewAlbumPopupCreate, + onCancel: viewModel.onNewAlbumPopupCancel ) } diff --git a/iOSClient/Albums/Presentation/List/AlbumsListViewModel.swift b/iOSClient/Albums/Presentation/List/AlbumsListViewModel.swift index 3237d0d5c9..79a6190c8e 100644 --- a/iOSClient/Albums/Presentation/List/AlbumsListViewModel.swift +++ b/iOSClient/Albums/Presentation/List/AlbumsListViewModel.swift @@ -18,9 +18,6 @@ class AlbumsListViewModel: ObservableObject { @Published private(set) var isLoading: Bool = false @Published private(set) var errorMessage: String? = nil - private var thumbnailsTask: Task? - @Published private(set) var albumThumbnails: [String: UIImage] = [:] - @Published var isLoadingPopupVisible: Bool = false @Published var isNewAlbumCreationPopupVisible: Bool = false diff --git a/iOSClient/Albums/Presentation/PhotoSelection/NCMediaViewRepresentable.swift b/iOSClient/Albums/Presentation/PhotoSelection/NCMediaViewRepresentable.swift index 95841f71d9..931dfbdaa8 100644 --- a/iOSClient/Albums/Presentation/PhotoSelection/NCMediaViewRepresentable.swift +++ b/iOSClient/Albums/Presentation/PhotoSelection/NCMediaViewRepresentable.swift @@ -13,11 +13,14 @@ struct NCMediaViewRepresentable: UIViewControllerRepresentable { @Binding var ncMedia: NCMedia? + let itemSelectionCountCallback: (Int) -> Void + func makeUIViewController(context: Context) -> UIViewController { let sb = UIStoryboard(name: "NCMedia", bundle: nil) let media = sb.instantiateInitialViewController() as! NCMedia media.isInGeneralPhotosSelectionContext = true + media.generalPhotosSelectionCountCallback = itemSelectionCountCallback DispatchQueue.main.async { self.ncMedia = media @@ -26,12 +29,7 @@ struct NCMediaViewRepresentable: UIViewControllerRepresentable { let nav = UINavigationController(rootViewController: media) nav.navigationBar.isHidden = true - let tab = UITabBarController() - tab.setViewControllers([nav], animated: false) - tab.tabBar.isHidden = true - tab.additionalSafeAreaInsets.bottom = 0 - - return tab + return nav } func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} diff --git a/iOSClient/Albums/Presentation/PhotoSelection/PhotoSelectionSheet.swift b/iOSClient/Albums/Presentation/PhotoSelection/PhotoSelectionSheet.swift index 00f6257cbb..58ab13c735 100644 --- a/iOSClient/Albums/Presentation/PhotoSelection/PhotoSelectionSheet.swift +++ b/iOSClient/Albums/Presentation/PhotoSelection/PhotoSelectionSheet.swift @@ -12,15 +12,19 @@ struct PhotoSelectionSheet: View { let onPhotosSelected: ([String]) -> Void - @State private var selectedPhotosCount: Int = 0 // TODO: Figure out how to get this count from NCMedia + @State private var selectedPhotosCount: Int = 0 @State private var mediaVC: NCMedia? var body: some View { NavigationView { VStack { - NCMediaViewRepresentable(ncMedia: $mediaVC) - .frame(maxHeight: .infinity) + NCMediaViewRepresentable( + ncMedia: $mediaVC, + itemSelectionCountCallback: { count in + selectedPhotosCount = count + } + ).frame(maxHeight: .infinity) } .navigationTitle(NSLocalizedString("_albums_photo_selection_sheet_title_", comment: "")) .navigationBarTitleDisplayMode(.inline) @@ -38,11 +42,16 @@ struct PhotoSelectionSheet: View { .foregroundColor(Color(NCBrandColor.shared.customer)) } - // ToolbarItemGroup(placement: .bottomBar) { - // Text("\(selectedPhotosCount) items selected") - // .font(.subheadline) - // .foregroundColor(.secondary) - // } + ToolbarItemGroup(placement: .bottomBar) { + + let quantifyingString = (selectedPhotosCount == 1) + ? NSLocalizedString("_albums_photo_selection_sheet_item_selected_", comment: "") + : NSLocalizedString("_albums_photo_selection_sheet_items_selected_", comment: "") + + Text("\(selectedPhotosCount) \(quantifyingString)") + .font(.subheadline) + .foregroundColor(.secondary) + } } } } diff --git a/iOSClient/Media/NCMedia.swift b/iOSClient/Media/NCMedia.swift index bd43239ef0..238dec42ef 100644 --- a/iOSClient/Media/NCMedia.swift +++ b/iOSClient/Media/NCMedia.swift @@ -54,7 +54,11 @@ class NCMedia: UIViewController, NCEmptyDataSetDelegate { let refreshControl = UIRefreshControl() var isTop: Bool = true var isEditMode = false - var fileSelect: [String] = [] + var fileSelect: [String] = [] { + didSet { + generalPhotosSelectionCountCallback?(fileSelect.count) + } + } var filesExists: ThreadSafeArray = ThreadSafeArray() var ocIdDoNotExists: ThreadSafeArray = ThreadSafeArray() var searchMediaInProgress: Bool = false @@ -94,6 +98,7 @@ class NCMedia: UIViewController, NCEmptyDataSetDelegate { var hiddenCellMetadats: ThreadSafeArray = ThreadSafeArray() var isInGeneralPhotosSelectionContext: Bool = false + var generalPhotosSelectionCountCallback: ((Int) -> Void)? var session: NCSession.Session { NCSession.shared.getSession(controller: tabBarController) diff --git a/iOSClient/Supporting Files/de.lproj/Localizable.strings b/iOSClient/Supporting Files/de.lproj/Localizable.strings index c84f05a90085a1981680fec29bbaf29792bb4a78..1234f0ba56a5585700c24eb94c326898abd23076 100644 GIT binary patch delta 100 zcmdmVnft+2?uHh|Ele^Gr^`HGQew|!C}But$er9Mt~~vM0wWu@KSL5j7Emagp=9!Z xAMxo)_nBNcp{f{)f$9_|f7rq{{n~vdf$7_hF^PZ_C{1s8%*3)?;UUu`4glBcBU%6e delta 19 bcmaEGm3z}=?uHh|Ele^Gw|hNen#KVDUD*hX diff --git a/iOSClient/Supporting Files/en.lproj/Localizable.strings b/iOSClient/Supporting Files/en.lproj/Localizable.strings index ff7eafd500..df6b6da4eb 100644 --- a/iOSClient/Supporting Files/en.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/en.lproj/Localizable.strings @@ -1370,6 +1370,8 @@ "_albums_photo_selection_sheet_title_" = "Select items"; "_albums_photo_selection_sheet_back_btn_" = "Back"; "_albums_photo_selection_sheet_done_btn_" = "Done"; +"_albums_photo_selection_sheet_item_selected_" = "item selected"; +"_albums_photo_selection_sheet_items_selected_" = "items selected"; "_albums_photos_loading_msg_" = "Loading photos..."; "_albums_photos_error_msg_" = "Unable to load photos. Please try again later!"; "_albums_photos_empty_heading_" = "All that's\nmissing are\nyour photos"; From 36f8779237655ddf1a50071d572c48c6c8401fb8 Mon Sep 17 00:00:00 2001 From: Dhanesh Katre Date: Thu, 18 Sep 2025 15:46:36 +0530 Subject: [PATCH 3/4] Attempt to fix NMC-4645 --- iOSClient/Albums/API/Albums+WebDAV.swift | 2 +- .../Presentation/Details/AlbumDetailsViewModel.swift | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/iOSClient/Albums/API/Albums+WebDAV.swift b/iOSClient/Albums/API/Albums+WebDAV.swift index 1a8b9048a5..38504352c3 100644 --- a/iOSClient/Albums/API/Albums+WebDAV.swift +++ b/iOSClient/Albums/API/Albums+WebDAV.swift @@ -393,7 +393,7 @@ public extension NextcloudKit { fileName: String, options: NKRequestOptions = NKRequestOptions(), taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in }, - completion: @escaping (Result) -> Void + completion: @escaping (Result) -> Void ) { let session = NCSession.shared.getSession(account: account) diff --git a/iOSClient/Albums/Presentation/Details/AlbumDetailsViewModel.swift b/iOSClient/Albums/Presentation/Details/AlbumDetailsViewModel.swift index 44033644e1..aac10211fc 100644 --- a/iOSClient/Albums/Presentation/Details/AlbumDetailsViewModel.swift +++ b/iOSClient/Albums/Presentation/Details/AlbumDetailsViewModel.swift @@ -225,7 +225,12 @@ class AlbumDetailsViewModel: ObservableObject { AlbumsManager.shared.syncAlbums() case .failure(let error): - NCContentPresenter().showError(error: NKError(error: error)) + if error.errorCode == 409 { + // Item already present in album error handling + NCContentPresenter().showInfo(title: "Some items are already present in this album") + } else { + NCContentPresenter().showError(error: NKError(error: error)) + } } } } From 332c5199fcdccc5a46fb2f89d721367c45ef9d05 Mon Sep 17 00:00:00 2001 From: Dhanesh Katre Date: Thu, 18 Sep 2025 20:04:39 +0530 Subject: [PATCH 4/4] Fixed small bug, album's name not appearing in rename album popup in some cases --- .../Albums/Presentation/Details/AlbumDetailsViewModel.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iOSClient/Albums/Presentation/Details/AlbumDetailsViewModel.swift b/iOSClient/Albums/Presentation/Details/AlbumDetailsViewModel.swift index aac10211fc..a1643d260d 100644 --- a/iOSClient/Albums/Presentation/Details/AlbumDetailsViewModel.swift +++ b/iOSClient/Albums/Presentation/Details/AlbumDetailsViewModel.swift @@ -91,7 +91,7 @@ class AlbumDetailsViewModel: ObservableObject { } func onRenameAlbumPopupCancel() { - newAlbumName = "" + newAlbumName = self.album.name isRenameAlbumPopupVisible = false } @@ -186,7 +186,7 @@ class AlbumDetailsViewModel: ObservableObject { self?.album = newAlbum self?.loadAlbumPhotos { self?.screenTitle = self?.album.name ?? "" - self?.newAlbumName = "" + self?.newAlbumName = self?.album.name ?? "" } } }