From 8fc462c516549af2dc8da9cb238831a268c93cd8 Mon Sep 17 00:00:00 2001 From: Richard Harangozo Date: Fri, 7 Feb 2025 16:29:41 +0100 Subject: [PATCH 01/13] Remove UINavigationBar.Style.modalLight refs: MBL-18460 affects: Student, Teacher, Parent release note: none --- .../UIKit/UINavigationBarExtensions.swift | 22 +++++++++---------- .../Settings/View/DashboardSettingsView.swift | 2 +- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Core/Core/Common/Extensions/UIKit/UINavigationBarExtensions.swift b/Core/Core/Common/Extensions/UIKit/UINavigationBarExtensions.swift index 069700f41d..11ddae2614 100644 --- a/Core/Core/Common/Extensions/UIKit/UINavigationBarExtensions.swift +++ b/Core/Core/Common/Extensions/UIKit/UINavigationBarExtensions.swift @@ -19,14 +19,17 @@ import UIKit extension UINavigationBar { - public enum Style: Equatable { case modal, modalLight, global, color(UIColor?) } + + public enum Style: Equatable { + case modal + case global + case color(UIColor?) + } func useStyle(_ style: Style) { switch style { case .modal: useModalStyle() - case .modalLight: - useModalStyle(isLightFont: true) case .global: useGlobalNavStyle() case .color(let color): @@ -65,7 +68,6 @@ extension UINavigationBar { */ public func useModalStyle( brand: Brand = Brand.shared, - isLightFont: Bool = false, forcedTheme: UITraitCollection? = nil ) { var backgroundColor = UIColor.backgroundLightest @@ -83,14 +85,10 @@ extension UINavigationBar { barStyle = .default isTranslucent = false - applyAppearanceChanges( - backgroundColor: backgroundColor, - foregroundColor: foregroundColor, - isLightFont: isLightFont - ) + applyAppearanceChanges(backgroundColor: backgroundColor, foregroundColor: foregroundColor) } - private func applyAppearanceChanges(backgroundColor: UIColor?, foregroundColor: UIColor?, isLightFont: Bool = false) { + private func applyAppearanceChanges(backgroundColor: UIColor?, foregroundColor: UIColor?) { let appearance = UINavigationBarAppearance() if isTranslucent { @@ -107,8 +105,8 @@ extension UINavigationBar { appearance.titleTextAttributes = [.foregroundColor: foreGroundColor] } - appearance.titleTextAttributes[.font] = UIFont.scaledNamedFont(isLightFont ? .semibold16 : .bold17) - appearance.buttonAppearance.normal.titleTextAttributes[.font] = UIFont.scaledNamedFont(isLightFont ? .semibold16 : .regular17) + appearance.titleTextAttributes[.font] = UIFont.scaledNamedFont(.bold17) + appearance.buttonAppearance.normal.titleTextAttributes[.font] = UIFont.scaledNamedFont(.regular17) standardAppearance = appearance scrollEdgeAppearance = standardAppearance diff --git a/Core/Core/Features/Dashboard/Settings/View/DashboardSettingsView.swift b/Core/Core/Features/Dashboard/Settings/View/DashboardSettingsView.swift index 5d20a06e1e..68ca79b3da 100644 --- a/Core/Core/Features/Dashboard/Settings/View/DashboardSettingsView.swift +++ b/Core/Core/Features/Dashboard/Settings/View/DashboardSettingsView.swift @@ -77,7 +77,7 @@ public struct DashboardSettingsView: View { .padding(.vertical, verticalPadding) } .background(Color.backgroundLightest.ignoresSafeArea()) - .navigationBarStyle(.modalLight) + .navigationBarStyle(.modal) .navigationTitle(Text("Dashboard Settings", bundle: .core)) } From 87697b6e09589ae57c6cd006f32aa376d6fbaee3 Mon Sep 17 00:00:00 2001 From: Richard Harangozo Date: Fri, 7 Feb 2025 18:34:14 +0100 Subject: [PATCH 02/13] Unify navigation bar fonts --- .../UINavigationControllerTheme.swift | 7 +++++++ .../CommonUI/UIViews/TitleSubtitleView.swift | 2 ++ .../CommonUI/UIViews/TitleSubtitleView.xib | 18 +++++------------- .../UIKit/UINavigationBarExtensions.swift | 10 +++++----- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/UINavigationControllerTheme.swift b/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/UINavigationControllerTheme.swift index 30b0bf18b3..d8eaef9b26 100644 --- a/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/UINavigationControllerTheme.swift +++ b/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/UINavigationControllerTheme.swift @@ -87,6 +87,10 @@ struct NavBarBackButtonModifier: ViewModifier { } extension View { + /// Sets the UINavigationBar's background color, title color and font, button color and font. + /// Only affects Views inside a UINavigationController. + /// Title style does not affect custom titleViews, like `TitleSubtitleView`. + /// Button style does not affect custom buttons, like `InstUI.NavigationBarButton`. public func navigationBarStyle(_ style: UINavigationBar.Style) -> some View { modifier(NavigationBarStyleModifier(style: style)) } @@ -95,6 +99,9 @@ extension View { modifier(TitleSubtitleModifier(title: title, subtitle: subtitle)) } + /// Sets the UINavigationBar's background color, button color to match the `Brand.shared` colors, + /// sets the button font and sets the brand logo as the titleView. + /// Only affects Views inside a UINavigationController. public func navigationBarGlobal() -> some View { modifier(GlobalNavigationBarModifier()) } diff --git a/Core/Core/Common/CommonUI/UIViews/TitleSubtitleView.swift b/Core/Core/Common/CommonUI/UIViews/TitleSubtitleView.swift index c92b701f55..4cf17a888c 100644 --- a/Core/Core/Common/CommonUI/UIViews/TitleSubtitleView.swift +++ b/Core/Core/Common/CommonUI/UIViews/TitleSubtitleView.swift @@ -48,6 +48,8 @@ public class TitleSubtitleView: UIView { let view = loadFromXib() view.titleLabel.text = "" view.subtitleLabel.text = "" + view.titleLabel.font = .scaledNamedFont(.semibold16) + view.subtitleLabel.font = .scaledNamedFont(.regular12) view.titleLabel.accessibilityElementsHidden = true view.subtitleLabel.accessibilityElementsHidden = true view.accessibilityTraits = [.header] diff --git a/Core/Core/Common/CommonUI/UIViews/TitleSubtitleView.xib b/Core/Core/Common/CommonUI/UIViews/TitleSubtitleView.xib index 3088822e39..855ec17857 100644 --- a/Core/Core/Common/CommonUI/UIViews/TitleSubtitleView.xib +++ b/Core/Core/Common/CommonUI/UIViews/TitleSubtitleView.xib @@ -1,9 +1,9 @@ - + - + @@ -13,11 +13,11 @@ - + - - - - - - - - diff --git a/Core/Core/Common/Extensions/UIKit/UINavigationBarExtensions.swift b/Core/Core/Common/Extensions/UIKit/UINavigationBarExtensions.swift index 11ddae2614..f712fb82b5 100644 --- a/Core/Core/Common/Extensions/UIKit/UINavigationBarExtensions.swift +++ b/Core/Core/Common/Extensions/UIKit/UINavigationBarExtensions.swift @@ -96,17 +96,17 @@ extension UINavigationBar { } else { appearance.configureWithDefaultBackground() - if let backgroundColor = backgroundColor { + if let backgroundColor { appearance.backgroundColor = backgroundColor } } - if let foreGroundColor = foregroundColor { - appearance.titleTextAttributes = [.foregroundColor: foreGroundColor] + if let foregroundColor { + appearance.titleTextAttributes = [.foregroundColor: foregroundColor] } - appearance.titleTextAttributes[.font] = UIFont.scaledNamedFont(.bold17) - appearance.buttonAppearance.normal.titleTextAttributes[.font] = UIFont.scaledNamedFont(.regular17) + appearance.titleTextAttributes[.font] = UIFont.scaledNamedFont(.semibold16) + appearance.buttonAppearance.normal.titleTextAttributes[.font] = UIFont.scaledNamedFont(.regular16) standardAppearance = appearance scrollEdgeAppearance = standardAppearance From 2ef15518096bf070b38f41acae6434bc3537f793 Mon Sep 17 00:00:00 2001 From: Richard Harangozo Date: Sat, 8 Feb 2025 00:01:17 +0100 Subject: [PATCH 03/13] Add InstUI.NavigationBarTitleView --- .../InstUI/Views/NavigationBarTitleView.swift | 54 +++++++++++++++++++ .../EnvironmentValues.swift | 3 ++ .../NavigationBarColorConfiguration.swift | 50 +++++++++++++++++ .../UINavigationControllerTheme.swift | 10 ++++ .../AssignmentListPreferencesScreen.swift | 17 ++---- .../View/AssignmentListScreen.swift | 7 ++- 6 files changed, 127 insertions(+), 14 deletions(-) create mode 100644 Core/Core/Common/CommonUI/InstUI/Views/NavigationBarTitleView.swift create mode 100644 Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/NavigationBarColorConfiguration.swift diff --git a/Core/Core/Common/CommonUI/InstUI/Views/NavigationBarTitleView.swift b/Core/Core/Common/CommonUI/InstUI/Views/NavigationBarTitleView.swift new file mode 100644 index 0000000000..29cd3422cc --- /dev/null +++ b/Core/Core/Common/CommonUI/InstUI/Views/NavigationBarTitleView.swift @@ -0,0 +1,54 @@ +// +// This file is part of Canvas. +// Copyright (C) 2025-present Instructure, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// + +import SwiftUI + +extension InstUI { + + public struct NavigationBarTitleView: View { + @Environment(\.dynamicTypeSize) private var dynamicTypeSize + @Environment(\.navBarColors) private var navBarColors + + private let title: String + private let subtitle: String? + + public init( + title: String, + subtitle: String? = nil + ) { + self.title = title + self.subtitle = subtitle + } + + public var body: some View { + VStack(spacing: 1) { + Text(title) + .font(.semibold16) + .foregroundColor(navBarColors.title) + + if let subtitle, subtitle.isNotEmpty { + Text(subtitle) + .font(.regular12) + .foregroundColor(navBarColors.subtitle) + } + } + .accessibilityElement(children: .combine) + .accessibilityAddTraits(.isHeader) + } + } +} diff --git a/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/EnvironmentValues.swift b/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/EnvironmentValues.swift index e59b52ef0f..1840a5eecc 100644 --- a/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/EnvironmentValues.swift +++ b/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/EnvironmentValues.swift @@ -60,4 +60,7 @@ extension EnvironmentValues { get { self[HorizontalPadding.self] } set { self[HorizontalPadding.self] = newValue } } + + // Used for passing colors to NavigationBar components. + @Entry var navBarColors: NavigationBarColorConfiguration = .init(style: .modal) } diff --git a/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/NavigationBarColorConfiguration.swift b/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/NavigationBarColorConfiguration.swift new file mode 100644 index 0000000000..0086a6e7ab --- /dev/null +++ b/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/NavigationBarColorConfiguration.swift @@ -0,0 +1,50 @@ +// +// This file is part of Canvas. +// Copyright (C) 2025-present Instructure, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// + +import SwiftUI + +struct NavigationBarColorConfiguration { + var title: Color + var subtitle: Color + var tint: Color + + // private, because currently we don't want to allow custom configurations + private init(title: Color, subtitle: Color, tint: Color) { + self.title = title + self.subtitle = subtitle + self.tint = tint + } + + init(style: UINavigationBar.Style, brand: Brand = .shared) { + switch style { + case .modal: + self.init( + title: .textDarkest, + subtitle: .textDark, + tint: brand.linkColor.asColor + ) + case .global: + let color = brand.navTextColor.asColor + self.init(title: color, subtitle: color, tint: color) + case .color: + let color = Color.textLightest + self.init(title: color, subtitle: color, tint: color) + } + } +} + diff --git a/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/UINavigationControllerTheme.swift b/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/UINavigationControllerTheme.swift index d8eaef9b26..03473f7372 100644 --- a/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/UINavigationControllerTheme.swift +++ b/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/UINavigationControllerTheme.swift @@ -30,7 +30,9 @@ struct NavigationBarStyleModifier: ViewModifier { func body(content: Content) -> some View { (controller.value as? NavigationBarStyled)?.navigationBarStyle = style controller.value.navigationController?.navigationBar.useStyle(style) + return content.overlay(Color?.none) // needs something modified to actually run + .environment(\.navBarColors, .init(style: style)) } } @@ -99,6 +101,14 @@ extension View { modifier(TitleSubtitleModifier(title: title, subtitle: subtitle)) } + public func navigationBarTitleView(title: String, subtitle: String? = nil) -> some View { + toolbar { + ToolbarItem(placement: .principal) { + InstUI.NavigationBarTitleView(title: title, subtitle: subtitle) + } + } + } + /// Sets the UINavigationBar's background color, button color to match the `Brand.shared` colors, /// sets the button font and sets the brand logo as the titleView. /// Only affects Views inside a UINavigationController. diff --git a/Core/Core/Features/Assignments/AssignmentList/View/AssignmentListPreferencesScreen.swift b/Core/Core/Features/Assignments/AssignmentList/View/AssignmentListPreferencesScreen.swift index cb0a36b1ce..6b849e4f4d 100644 --- a/Core/Core/Features/Assignments/AssignmentList/View/AssignmentListPreferencesScreen.swift +++ b/Core/Core/Features/Assignments/AssignmentList/View/AssignmentListPreferencesScreen.swift @@ -45,8 +45,12 @@ public struct AssignmentListPreferencesScreen: View { } } .background(Color.backgroundLightest) - .navigationTitleStyled(navBarTitleView) + .navigationBarTitleView( + title: String(localized: "Assignment List Preferences", bundle: .core), + subtitle: viewModel.courseName + ) .navigationBarItems(leading: cancelButton, trailing: doneButton) + .navigationBarStyle(.modal) .onDisappear { viewModel.didDismiss() } @@ -66,17 +70,6 @@ public struct AssignmentListPreferencesScreen: View { .accessibilityIdentifier("AssignmentFilter.doneButton") } - private var navBarTitleView: some View { - VStack { - Text(String(localized: "Assignment List Preferences", bundle: .core)) - .foregroundStyle(Color.textDarkest) - .font(.semibold16) - Text(viewModel.courseName) - .foregroundStyle(Color.textDark) - .font(.regular12) - } - } - // MARK: - Sections @ViewBuilder diff --git a/Core/Core/Features/Assignments/AssignmentList/View/AssignmentListScreen.swift b/Core/Core/Features/Assignments/AssignmentList/View/AssignmentListScreen.swift index fc26a89e56..e08fa1de02 100644 --- a/Core/Core/Features/Assignments/AssignmentList/View/AssignmentListScreen.swift +++ b/Core/Core/Features/Assignments/AssignmentList/View/AssignmentListScreen.swift @@ -49,14 +49,17 @@ public struct AssignmentListScreen: View, ScreenViewTrackable { } } .background(Color.backgroundLightest.edgesIgnoringSafeArea(.all)) - .navigationBarStyle(.color(viewModel.courseColor)) - .navigationTitle(String(localized: "Assignments", bundle: .core), subtitle: viewModel.courseName) + .navigationBarTitleView( + title: String(localized: "Assignments", bundle: .core), + subtitle: viewModel.courseName + ) .navigationBarGenericBackButton() .navBarItems( trailing: .filterIcon(isBackgroundContextColor: true, isSolid: viewModel.isFilterIconSolid) { viewModel.navigateToPreferences(viewController: controller) } ) + .navigationBarStyle(.color(viewModel.courseColor)) .onAppear(perform: viewModel.viewDidAppear) .onReceive(viewModel.$defaultDetailViewRoute, perform: setupDefaultSplitDetailView) } From c810e0fffbde97f436aaf3d9df9e9a173670b2a9 Mon Sep 17 00:00:00 2001 From: Richard Harangozo Date: Mon, 10 Feb 2025 00:03:56 +0100 Subject: [PATCH 04/13] Remove unused Inbox AttachmentPicker --- .../AttachmentPickerAssembly.swift | 26 -- .../AttachmentPickerInteractor.swift | 40 --- .../AttachmentPickerInteractorLive.swift | 193 ---------- .../AttachmentPickerInteractorPreview.swift | 69 ---- .../View/AttachmentPickerView.swift | 329 ------------------ .../ViewModel/AttachmentPickerViewModel.swift | 237 ------------- Core/Core/Resources/Localizable.xcstrings | 11 + .../AttachmentPickerInteractorLiveTests.swift | 154 -------- .../AttachmentPickerViewModelTests.swift | 115 ------ 9 files changed, 11 insertions(+), 1163 deletions(-) delete mode 100644 Core/Core/Features/Inbox/AttachmentPicker/Model/AttachmentPickerInteractor/AttachmentPickerInteractor.swift delete mode 100644 Core/Core/Features/Inbox/AttachmentPicker/Model/AttachmentPickerInteractor/AttachmentPickerInteractorLive.swift delete mode 100644 Core/Core/Features/Inbox/AttachmentPicker/Model/AttachmentPickerInteractor/AttachmentPickerInteractorPreview.swift delete mode 100644 Core/Core/Features/Inbox/AttachmentPicker/View/AttachmentPickerView.swift delete mode 100644 Core/Core/Features/Inbox/AttachmentPicker/ViewModel/AttachmentPickerViewModel.swift delete mode 100644 Core/CoreTests/Features/Inbox/AttachmentPicker/Model/AttachmentPickerInteractorLiveTests.swift delete mode 100644 Core/CoreTests/Features/Inbox/AttachmentPicker/ViewModel/AttachmentPickerViewModelTests.swift diff --git a/Core/Core/Features/Inbox/AttachmentPicker/AttachmentPickerAssembly.swift b/Core/Core/Features/Inbox/AttachmentPicker/AttachmentPickerAssembly.swift index a7b5dc92bf..1a44a2dbf4 100644 --- a/Core/Core/Features/Inbox/AttachmentPicker/AttachmentPickerAssembly.swift +++ b/Core/Core/Features/Inbox/AttachmentPicker/AttachmentPickerAssembly.swift @@ -20,25 +20,6 @@ import Foundation import Combine public enum AttachmentPickerAssembly { - public static func makeAttachmentPickerViewController( - subTitle: String? = nil, - env: AppEnvironment = .shared, - batchId: String, - uploadManager: UploadManager, - alreadyUploadedFiles: CurrentValueSubject<[File], Never> - ) -> UIViewController { - let interactor = AttachmentPickerInteractorLive( - batchId: batchId, - uploadFolderPath: "conversation attachments", - restrictForFolderPath: true, - uploadManager: uploadManager, - alreadyUploadedFiles: alreadyUploadedFiles - ) - let viewModel = AttachmentPickerViewModel(subTitle: subTitle, router: env.router, interactor: interactor) - let view = AttachmentPickerView(model: viewModel) - return CoreHostingController(view) - } - public static func makeAudioPickerViewcontroller( router: Router, onSelect: @escaping (URL) -> Void = { _ in } @@ -60,13 +41,6 @@ public enum AttachmentPickerAssembly { #if DEBUG - public static func makePreview(env: AppEnvironment) - -> AttachmentPickerView { - let interactor = AttachmentPickerInteractorPreview() - let viewModel = AttachmentPickerViewModel(router: env.router, interactor: interactor) - return AttachmentPickerView(model: viewModel) - } - public static func makeAudioPickerPreview( env: AppEnvironment ) -> AudioPickerView { diff --git a/Core/Core/Features/Inbox/AttachmentPicker/Model/AttachmentPickerInteractor/AttachmentPickerInteractor.swift b/Core/Core/Features/Inbox/AttachmentPicker/Model/AttachmentPickerInteractor/AttachmentPickerInteractor.swift deleted file mode 100644 index 2a9f06270e..0000000000 --- a/Core/Core/Features/Inbox/AttachmentPicker/Model/AttachmentPickerInteractor/AttachmentPickerInteractor.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// This file is part of Canvas. -// Copyright (C) 2024-present Instructure, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// - -import Foundation -import Combine - -protocol AttachmentPickerInteractor { - var files: PassthroughSubject<[File], Error> { get } - var alreadyUploadedFiles: CurrentValueSubject<[File], Never> { get } - var isCancelConfirmationNeeded: Bool { get } - - func uploadFiles() - - func addFile(url: URL) - - func addFile(file: File) - - func retry() - - func cancel() - - func removeFile(file: File) - - func deleteFile(file: File) -> AnyPublisher -} diff --git a/Core/Core/Features/Inbox/AttachmentPicker/Model/AttachmentPickerInteractor/AttachmentPickerInteractorLive.swift b/Core/Core/Features/Inbox/AttachmentPicker/Model/AttachmentPickerInteractor/AttachmentPickerInteractorLive.swift deleted file mode 100644 index f4cf1bd38d..0000000000 --- a/Core/Core/Features/Inbox/AttachmentPicker/Model/AttachmentPickerInteractor/AttachmentPickerInteractorLive.swift +++ /dev/null @@ -1,193 +0,0 @@ -// -// This file is part of Canvas. -// Copyright (C) 2024-present Instructure, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// - -import Foundation -import Combine - -class AttachmentPickerInteractorLive: AttachmentPickerInteractor { - public let files = PassthroughSubject<[File], Error>() - public let alreadyUploadedFiles: CurrentValueSubject<[File], Never> - public var isCancelConfirmationNeeded: Bool { - return !fileStore.all.isAllUploaded || alreadyUploadedFiles.value.contains(where: { $0.isUploading }) - } - - private let env: AppEnvironment - private var uploadManager: UploadManager - private let batchId: String - private let uploadFolderPath: String? - private let restrictForFolderPath: Bool - - private var uploadFolder: Folder? - private var uploadFolderPathStore: Store? - private var subscriptions = Set() - private let publisherProvider: URLSessionDataTaskPublisherProvider - - private lazy var fileStore = uploadManager.subscribe(batchID: batchId) { [weak self] in - self?.update() - } - - init( - env: AppEnvironment = .shared, - batchId: String, - uploadFolderPath: String? = nil, - restrictForFolderPath: Bool = false, - uploadManager: UploadManager, - alreadyUploadedFiles: CurrentValueSubject<[File], Never>, - publisherProvider: URLSessionDataTaskPublisherProvider = URLSessionDataTaskPublisherProviderLive() - ) { - self.env = env - self.uploadManager = uploadManager - self.batchId = batchId - self.uploadFolderPath = uploadFolderPath - self.restrictForFolderPath = restrictForFolderPath - self.alreadyUploadedFiles = alreadyUploadedFiles - self.publisherProvider = publisherProvider - - getFolderByPath() - fileStore.refresh() - } - - private func update() { - files.send(fileStore.all) - } - - func uploadFiles() { - fileStore.all.forEach { file in - if !file.isUploaded { - uploadManager.upload(file: file, to: .myFiles, folderPath: uploadFolderPath) - } - } - } - - func addFile(url: URL) { - do { - try uploadManager.add(url: url, batchID: batchId) - fileStore.refresh() - uploadFiles() - } catch { - files.send(completion: .failure(NSError.instructureError("Failed to add file"))) - } - } - - func addFile(file: File) { - file.taskID = "localProcess" - var newValues = alreadyUploadedFiles.value - newValues.append(file) - alreadyUploadedFiles.send(newValues) - if restrictForFolderPath && file.folderID != uploadFolder?.id { - duplicateFileToUploadFolder(file: file) - } else { - file.taskID = nil - } - } - - func retry() { - uploadFiles() - } - - func cancel() { - fileStore.all.forEach {file in - if !file.isUploaded { - removeFile(file: file) - uploadManager.cancel(file: file) - } - } - let filteredFiles: [File] = alreadyUploadedFiles.value.filter { !$0.isUploading } - alreadyUploadedFiles.send(filteredFiles) - } - - func removeFile(file: File) { - if alreadyUploadedFiles.value.contains(file) { - let newValues = alreadyUploadedFiles.value.filter { $0 != file } - alreadyUploadedFiles.send(newValues) - } else { - uploadManager.viewContext.delete(file) - fileStore.refresh() - } - } - - func deleteFile(file: File) -> AnyPublisher { - uploadManager.viewContext.delete(file) - fileStore.refresh() - - guard let fileId = file.id else { - return Just(()).eraseToAnyPublisher() - } - - return ReactiveStore(useCase: DeleteFile(fileID: fileId)) - .getEntities() - .mapToVoid() - .replaceError(with: ()) - .eraseToAnyPublisher() - } - - private func duplicateFileToUploadFolder(file: File) { - guard let fileId = file.id else { return } - - getOnlineFileURL(fileId: fileId) - .map { [weak self] url in - guard let self, let url else { return } - _ = try? self.uploadManager.add(url: url, batchID: self.batchId) - let newValues = alreadyUploadedFiles.value.filter { $0 != file } - alreadyUploadedFiles.send(newValues) - uploadFiles() - } - .sink() - .store(in: &subscriptions) - } - - private func getOnlineFileURL(fileId: String) -> AnyPublisher { - ReactiveStore(useCase: GetFile(context: .currentUser, fileID: fileId)) - .getEntities() - .map { files in - return files.first?.url - } - .flatMap { [weak self] url in - if let url, - let self, - let session = self.env.currentSession, - let request = try? url.urlRequest(relativeTo: session.baseURL, accessToken: session.accessToken, actAsUserID: session.actAsUserID) { - return self.publisherProvider.getPublisher(for: request) - } else { - return Fail(error: NSError.instructureError("Failed to duplicate file")).eraseToAnyPublisher() - } - } - .map { (localURL: URL, fileName: String) in - var modifiedURL = localURL - modifiedURL.deleteLastPathComponent() - modifiedURL = modifiedURL.appendingPathComponent(fileName) - try? FileManager.default.moveItem(atPath: localURL.path, toPath: modifiedURL.path) - return modifiedURL - } - .eraseToAnyPublisher() - } - - private func getFolderByPath() { - uploadFolderPathStore = env.subscribe(GetFolderByPath(context: .currentUser, path: uploadFolderPath ?? "")) - - uploadFolderPathStore? - .allObjects - .map { [weak self] folders in - self?.uploadFolder = folders.first - } - .sink() - .store(in: &subscriptions) - - uploadFolderPathStore?.exhaust() - } -} diff --git a/Core/Core/Features/Inbox/AttachmentPicker/Model/AttachmentPickerInteractor/AttachmentPickerInteractorPreview.swift b/Core/Core/Features/Inbox/AttachmentPicker/Model/AttachmentPickerInteractor/AttachmentPickerInteractorPreview.swift deleted file mode 100644 index 4198b0e4b0..0000000000 --- a/Core/Core/Features/Inbox/AttachmentPicker/Model/AttachmentPickerInteractor/AttachmentPickerInteractorPreview.swift +++ /dev/null @@ -1,69 +0,0 @@ -// -// This file is part of Canvas. -// Copyright (C) 2024-present Instructure, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// - -import Foundation -import Combine - -#if DEBUG -class AttachmentPickerInteractorPreview: AttachmentPickerInteractor { - var alreadyUploadedFiles = CurrentValueSubject<[File], Never>([]) - var files: PassthroughSubject<[File], Error> = PassthroughSubject<[File], Error>() - var isCancelConfirmationNeeded = false - - public private(set) var uploadFilesCalled: Bool = false - public private(set) var addFileCalled: Bool = false - public private(set) var retryCalled: Bool = false - public private(set) var cancelCalled: Bool = false - public private(set) var removeFileCalled: Bool = false - public private(set) var deleteFileCalled: Bool = false - - func uploadFiles() { - uploadFilesCalled = true - } - - func addFile(url: URL) { - addFileCalled = true - } - - func addFile(file: File) { - addFileCalled = true - } - - func retry() { - retryCalled = true - } - - func cancel() { - cancelCalled = true - } - - func removeFile(file: File) { - removeFileCalled = true - } - - func deleteFile(file: File) -> AnyPublisher { - deleteFileCalled = true - return Just(()).eraseToAnyPublisher() - } - - func throwError() { - files.send(completion: .failure(NSError.instructureError("Failed to add file"))) - } -} - -#endif diff --git a/Core/Core/Features/Inbox/AttachmentPicker/View/AttachmentPickerView.swift b/Core/Core/Features/Inbox/AttachmentPicker/View/AttachmentPickerView.swift deleted file mode 100644 index ee8fcf4651..0000000000 --- a/Core/Core/Features/Inbox/AttachmentPicker/View/AttachmentPickerView.swift +++ /dev/null @@ -1,329 +0,0 @@ -// -// This file is part of Canvas. -// Copyright (C) 2024-present Instructure, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// - -import SwiftUI -import PhotosUI - -public struct AttachmentPickerView: View { - @ObservedObject private var viewModel: AttachmentPickerViewModel - @Environment(\.viewController) private var controller - @ScaledMetric private var uiScale: CGFloat = 1 - - init(model: AttachmentPickerViewModel) { - self.viewModel = model - } - - public var body: some View { - VStack(alignment: .center, spacing: 0) { - headerView - if viewModel.fileList.isEmpty && viewModel.alreadyUploadedFileList.isEmpty { - emptyView - } else { - contentView - } - } - .navigationTitleStyled( - VStack(spacing: 0) { - Text(viewModel.title).font(.headline) - if let subtitle = viewModel.subTitle, subtitle.isNotEmpty { - Text(subtitle).font(.subheadline) - } - } - ) - .navigationBarItems(leading: cancelButton, trailing: actionButton) - .fileImporter( - isPresented: $viewModel.isFilePickerVisible, - allowedContentTypes: [.item], - allowsMultipleSelection: false - ) { result in - switch result { - case .success(let urls): - urls.forEach { url in - if url.startAccessingSecurityScopedResource() { - viewModel.didSelectFile(url: url) - } - } - case .failure: - viewModel.showDialog(title: viewModel.fileErrorTitle, message: viewModel.fileErrorMessage) - } - } - .sheet(isPresented: $viewModel.isImagePickerVisible, content: { - ImagePickerViewController(sourceType: .photoLibrary, imageHandler: viewModel.didSelectFile) - }) - .sheet(isPresented: $viewModel.isTakePhotoVisible, content: { - ImagePickerViewController(sourceType: .camera, imageHandler: viewModel.didSelectFile) - .interactiveDismissDisabled() - }) - .sheet(isPresented: $viewModel.isAudioRecordVisible, content: { - AttachmentPickerAssembly.makeAudioPickerViewcontroller(router: viewModel.router, onSelect: viewModel.didSelectFile) - .interactiveDismissDisabled() - }) - .confirmationAlert( - isPresented: $viewModel.isShowingCancelDialog, - presenting: viewModel.confirmAlert - ) - } - - private var contentView: some View { - List { - ForEach(viewModel.fileList, id: \.self) { file in - rowView(for: file) - .listRowSpacing(0) - .removeListRowSeparatorLeadingInset() - } - - ForEach(viewModel.alreadyUploadedFileList, id: \.self) { file in - rowView(for: file, shouldDeleteOnRemove: false) - .listRowSpacing(0) - .removeListRowSeparatorLeadingInset() - } - } - .listStyle(.plain) - .accessibilityElement(children: .contain) - } - - @ViewBuilder - private func rowView(for file: File, shouldDeleteOnRemove: Bool = true) -> some View { - let fileSizeWithUnit = ByteCountFormatter.string(fromByteCount: Int64(file.size), countStyle: .file) - Button { - viewModel.fileSelected.accept((controller, file)) - } label: { - VStack(spacing: 0) { - HStack(spacing: 0) { - VStack(alignment: .leading, spacing: 4) { - Text(file.displayName ?? file.localFileURL?.lastPathComponent ?? "").font(.bold16).foregroundStyle(Color.textDarkest) - Text(fileSizeWithUnit).font(.regular14).foregroundStyle(Color.textDark) - if (file.uploadError != nil) { - HStack(spacing: 0) { - Image.warningLine - .resizable() - .frame( - width: 15 * uiScale.iconScale, - height: 15 * uiScale.iconScale - ) - Text(file.uploadError!).multilineTextAlignment(.leading) - } - .font(.regular14).foregroundStyle(Color.textDanger) - } - } - - Spacer() - if (file.isUploading) { - ProgressView() - } else { - Button { - viewModel.deleteFileButtonDidTap.accept(file) - } label: { - Image.xLine - .resizable() - .frame( - width: 25 * uiScale.iconScale, - height: 25 * uiScale.iconScale - ) - } - } - } - } - } - .foregroundStyle(Color.textDarkest) - .accessibilityElement(children: .ignore) - .accessibilityLabel(Text(verbatim: "\(file.displayName ?? file.localFileURL?.lastPathComponent ?? "") (\(fileSizeWithUnit)")) - .swipeActions(edge: .trailing, allowsFullSwipe: false) { - Button { - if shouldDeleteOnRemove { - viewModel.deleteFileButtonDidTap.accept(file) - } else { - viewModel.removeButtonDidTap.accept(file) - } - } label: { - Image.trashLine - .resizable() - .frame( - width: 25 * uiScale.iconScale, - height: 25 * uiScale.iconScale - ) - } - } - } - - private var headerView: some View { - VStack(spacing: 0) { - if viewModel.fileList.containsUploading { - progressHeader - } else if viewModel.fileList.containsError { - errorHeader - } - selectionHeader - } - .font(.regular16) - .foregroundStyle(Color.textDarkest) - } - - private var selectionHeader: some View { - VStack(spacing: 0) { - HStack(spacing: 0) { - Text("\(viewModel.fileList.count + viewModel.alreadyUploadedFileList.count) Items", bundle: .core) - Spacer() - Button { - viewModel.addAttachmentButtonDidTap.accept(controller) - } label: { - Image.addLine - .resizable() - .frame( - width: 25 * uiScale.iconScale, - height: 25 * uiScale.iconScale - ) - } - .accessibilityLabel(Text("Add new attachment", bundle: .core)) - } - .padding(.vertical, 12) - .padding(.horizontal, 12) - separator - } - } - - private var progressHeader: some View { - VStack { - VStack { - Text( - "Uploading \(viewModel.fileList.formattedBytesSent) of \(viewModel.fileList.formattedTotalSize)", - bundle: .core - ) - ProgressView( - value: Float(viewModel.fileList.totalBytesSent), - total: Float(max(viewModel.fileList.totalSize, viewModel.fileList.totalBytesSent)) - ) - } - .padding(12) - separator - } - } - - private var errorHeader: some View { - VStack { - VStack { - Text("Upload Failed", bundle: .core).font(.headline) - Text("One or more files failed to upload. Check your internet connection and retry to submit.", bundle: .core) - .multilineTextAlignment(.center) - } - .padding(12) - - separator - } - } - - private var emptyView: some View { - VStack(spacing: 0) { - Spacer() - - Image.paperclipLine - .resizable() - .frame(width: 100, height: 100) - .foregroundStyle(Color.textDarkest) - .accessibilityHidden(true) - - Text("No attachments", bundle: .core) - .font(.bold20) - .foregroundStyle(Color.textDarkest) - .padding(.bottom, 6) - .accessibilityHidden(true) - - Text("Add an attachment by tapping the plus at top right.", bundle: .core) - .multilineTextAlignment(.center) - .font(.regular14) - .foregroundStyle(Color.textDarkest) - .accessibilityLabel(Text("No attachments, add an attachment by tapping the plus at top right.", bundle: .core)) - - Spacer() - } - .padding(.horizontal, 12) - } - - private var separator: some View { - Color.borderMedium - .frame(height: 0.5) - } - - private var actionButton: some View { - VStack { - if viewModel.fileList.containsError { - retryButton - } else if viewModel.fileList.containsUploading { - uploadButton.disabled(true) - } else if viewModel.alreadyUploadedFileList.contains(where: { $0.isUploading }) { - doneButton.disabled(true) - } else if viewModel.fileList.isAllUploaded { - doneButton - } else { - uploadButton - } - } - } - - private var uploadButton: some View { - Button { - viewModel.uploadButtonDidTap.accept(controller) - } label: { - Text("Upload", bundle: .core) - .font(.regular16) - .foregroundColor(.accentColor) - } - } - - private var retryButton: some View { - Button { - viewModel.retryButtonDidTap.accept(controller) - } label: { - Text("Retry", bundle: .core) - .font(.regular16) - .foregroundColor(.accentColor) - } - } - - private var cancelButton: some View { - Button { - viewModel.cancelButtonDidTap.accept(controller) - } label: { - Text("Cancel", bundle: .core) - .font(.regular16) - .foregroundColor(.accentColor) - } - } - - private var doneButton: some View { - Button { - viewModel.doneButtonDidTap.accept(controller) - } label: { - Text("Done", bundle: .core) - .font(.regular16) - .foregroundColor(.accentColor) - } - } -} - -#if DEBUG - -struct AttachmentView_Previews: PreviewProvider { - static let env = PreviewEnvironment() - - static var previews: some View { - AttachmentPickerAssembly.makePreview(env: env) - } -} - -#endif diff --git a/Core/Core/Features/Inbox/AttachmentPicker/ViewModel/AttachmentPickerViewModel.swift b/Core/Core/Features/Inbox/AttachmentPicker/ViewModel/AttachmentPickerViewModel.swift deleted file mode 100644 index 0615b6213a..0000000000 --- a/Core/Core/Features/Inbox/AttachmentPicker/ViewModel/AttachmentPickerViewModel.swift +++ /dev/null @@ -1,237 +0,0 @@ -// -// This file is part of Canvas. -// Copyright (C) 2024-present Instructure, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// - -import Foundation -import Combine -import CombineExt - -class AttachmentPickerViewModel: ObservableObject { - - // MARK: Inputs / Outputs - - @Published public var isAttachmentSelectorVisible: Bool = false - @Published public var isFilePickerVisible: Bool = false - @Published public var isImagePickerVisible: Bool = false - @Published public var isTakePhotoVisible: Bool = false - @Published public var isAudioRecordVisible: Bool = false - @Published public var isFileSelectVisible: Bool = false - @Published public private(set) var fileList: [File] = [] - @Published public private(set) var alreadyUploadedFileList: [File] = [] - @Published public var isFileErrorOccured: Bool = false - public let title = String(localized: "Attachments", bundle: .core) - public let subTitle: String? - public let fileErrorTitle = String(localized: "Error", bundle: .core) - public let fileErrorMessage = String(localized: "Failed to add attachment. Please try again!", bundle: .core) - @Published public var isShowingCancelDialog = false - public let confirmAlert = ConfirmationAlertViewModel( - title: String(localized: "Cancel Uploading", bundle: .core), - message: String(localized: "Are sure you want to leave your process? Your changes will not be saved.", bundle: .core), - cancelButtonTitle: String(localized: "Cancel", bundle: .core), - confirmButtonTitle: String(localized: "Discard", bundle: .core), - isDestructive: true - ) - - public let cancelButtonDidTap = PassthroughRelay() - public let doneButtonDidTap = PassthroughRelay() - public let uploadButtonDidTap = PassthroughRelay() - public let retryButtonDidTap = PassthroughRelay() - public let addAttachmentButtonDidTap = PassthroughRelay() - public let removeButtonDidTap = PassthroughRelay() - public let deleteFileButtonDidTap = PassthroughRelay() - public var fileSelected = PassthroughRelay<(WeakViewController, File)>() - public let router: Router - - // MARK: Private - - private let interactor: AttachmentPickerInteractor - private var subscriptions = Set() - private var linkedFiles: [File] = [] - - public init (subTitle: String? = nil, router: Router, interactor: AttachmentPickerInteractor) { - self.subTitle = subTitle - self.router = router - self.interactor = interactor - - setupInputBindings(router: router) - setupOutputBindings() - } - - private func showDialog(viewController: WeakViewController) { - let sheet = BottomSheetPickerViewController.create() - - sheet.addAction( - image: .documentLine, - title: String(localized: "Upload file", bundle: .core), - accessibilityIdentifier: nil - ) { [weak self] in - self?.isFilePickerVisible = true - } - sheet.addAction( - image: .imageLine, - title: String(localized: "Upload photo", bundle: .core), - accessibilityIdentifier: nil - ) { [weak self] in - self?.isImagePickerVisible = true - } - sheet.addAction( - image: .cameraLine, - title: String(localized: "Take photo", bundle: .core), - accessibilityIdentifier: nil - ) { [weak self] in - self?.isTakePhotoVisible = true - } - sheet.addAction( - image: .audioLine, - title: String(localized: "Record audio", bundle: .core), - accessibilityIdentifier: nil - ) { [weak self] in - self?.isAudioRecordVisible = true - } - sheet.addAction( - image: .folderLine, - title: String(localized: "Attach from Canvas files", bundle: .core), - accessibilityIdentifier: nil - ) { [weak self] in - guard let self, let top = AppEnvironment.shared.window?.rootViewController?.topMostViewController() else { return } - - let viewController = AttachmentPickerAssembly.makeFilePickerViewController(env: .shared, onSelect: self.didSelectFile) - self.router.show(viewController, from: top, options: .modal(isDismissable: true, embedInNav: true)) - - } - router.show(sheet, from: viewController, options: .modal()) - } - - func showDialog(title: String?, message: String?) { - let actionTitle = String(localized: "OK", bundle: .core) - let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) - let action = UIAlertAction(title: actionTitle, style: .default) { [weak self] _ in - self?.isImagePickerVisible = false - self?.isTakePhotoVisible = false - self?.isFilePickerVisible = false - self?.isAudioRecordVisible = false - } - alert.addAction(action) - - if let top = AppEnvironment.shared.window?.rootViewController?.topMostViewController() { - router.show(alert, from: top, options: .modal()) - } - } - - func didSelectFile(url: URL) { - isImagePickerVisible = false - isTakePhotoVisible = false - isFilePickerVisible = false - isAudioRecordVisible = false - - interactor.addFile(url: url) - } - - func didSelectFile(file: File) { - interactor.addFile(file: file) - } - - private func setupOutputBindings() { - interactor.files.sink(receiveCompletion: { [weak self] result in - switch result { - case.failure: - self?.showDialog(title: self?.fileErrorTitle, message: self?.fileErrorMessage) - case .finished: break - } - }, receiveValue: { [weak self] files in - self?.fileList = files - }) - .store(in: &subscriptions) - - interactor.alreadyUploadedFiles.sink { [weak self] files in - self?.alreadyUploadedFileList = files - } - .store(in: &subscriptions) - } - - private func setupInputBindings(router: Router) { - cancelButtonDidTap - .handleEvents(receiveOutput: { [weak self] _ in - if self?.interactor.isCancelConfirmationNeeded == true { - self?.isShowingCancelDialog = true - } - }) - .flatMap { [weak self, confirmAlert] value in - if self?.interactor.isCancelConfirmationNeeded == true { - return confirmAlert.userConfirmation().map { value }.eraseToAnyPublisher() - } - return Just(value).eraseToAnyPublisher() - } - .sink { [weak self, router] viewController in - self?.interactor.cancel() - router.dismiss(viewController) - } - .store(in: &subscriptions) - - doneButtonDidTap - .sink { [router] viewController in - router.dismiss(viewController) - } - .store(in: &subscriptions) - - uploadButtonDidTap - .sink { [weak self] _ in - self?.interactor.uploadFiles() - } - .store(in: &subscriptions) - - retryButtonDidTap - .sink { [weak self] _ in - self?.interactor.retry() - } - .store(in: &subscriptions) - - addAttachmentButtonDidTap - .sink { [weak self] viewController in - self?.showDialog(viewController: viewController) - } - .store(in: &subscriptions) - - removeButtonDidTap - .sink { [weak self] file in - self?.interactor.removeFile(file: file) - } - .store(in: &subscriptions) - - deleteFileButtonDidTap - .flatMap { [weak self] file in - if let self { - return self.interactor.deleteFile(file: file) - } else { - return Just(()).eraseToAnyPublisher() - } - } - .sink() - .store(in: &subscriptions) - - fileSelected.sink { [weak self] (controller, file) in - if let url = file.url, let fileController = router.match(url.appendingQueryItems(.init(name: "canEdit", value: "false"))) { - router.show(fileController, from: controller, options: .modal(isDismissable: true, embedInNav: true, addDoneButton: true)) - } else { - let shouldUploadTitle = String(localized: "Upload Files", bundle: .core) - let shouldUploadMessage = String(localized: "You have to upload the attachments to get access to previews!", bundle: .core) - self?.showDialog(title: shouldUploadTitle, message: shouldUploadMessage) - } - } - .store(in: &subscriptions) - } -} diff --git a/Core/Core/Resources/Localizable.xcstrings b/Core/Core/Resources/Localizable.xcstrings index ffd9c1ff62..d74d37229c 100644 --- a/Core/Core/Resources/Localizable.xcstrings +++ b/Core/Core/Resources/Localizable.xcstrings @@ -14885,6 +14885,7 @@ } }, "%lld Items" : { + "extractionState" : "stale", "localizations" : { "ar" : { "stringUnit" : { @@ -24523,6 +24524,7 @@ } }, "Add an attachment by tapping the plus at top right." : { + "extractionState" : "stale", "localizations" : { "ar" : { "stringUnit" : { @@ -28876,6 +28878,7 @@ } }, "Add new attachment" : { + "extractionState" : "stale", "localizations" : { "ar" : { "stringUnit" : { @@ -43181,6 +43184,7 @@ } }, "Are sure you want to leave your process? Your changes will not be saved." : { + "extractionState" : "stale", "localizations" : { "ar" : { "stringUnit" : { @@ -64671,6 +64675,7 @@ } }, "Cancel Uploading" : { + "extractionState" : "stale", "localizations" : { "ar" : { "stringUnit" : { @@ -137465,6 +137470,7 @@ } }, "Failed to add attachment. Please try again!" : { + "extractionState" : "stale", "localizations" : { "ar" : { "stringUnit" : { @@ -217651,6 +217657,7 @@ } }, "No attachments" : { + "extractionState" : "stale", "localizations" : { "ar" : { "stringUnit" : { @@ -217907,6 +217914,7 @@ } }, "No attachments, add an attachment by tapping the plus at top right." : { + "extractionState" : "stale", "localizations" : { "ar" : { "stringUnit" : { @@ -360881,6 +360889,7 @@ } }, "Upload" : { + "extractionState" : "stale", "localizations" : { "ar" : { "stringUnit" : { @@ -362673,6 +362682,7 @@ } }, "Upload Files" : { + "extractionState" : "stale", "localizations" : { "ar" : { "stringUnit" : { @@ -384775,6 +384785,7 @@ } }, "You have to upload the attachments to get access to previews!" : { + "extractionState" : "stale", "localizations" : { "ar" : { "stringUnit" : { diff --git a/Core/CoreTests/Features/Inbox/AttachmentPicker/Model/AttachmentPickerInteractorLiveTests.swift b/Core/CoreTests/Features/Inbox/AttachmentPicker/Model/AttachmentPickerInteractorLiveTests.swift deleted file mode 100644 index 79f800f573..0000000000 --- a/Core/CoreTests/Features/Inbox/AttachmentPicker/Model/AttachmentPickerInteractorLiveTests.swift +++ /dev/null @@ -1,154 +0,0 @@ -// -// This file is part of Canvas. -// Copyright (C) 2024-present Instructure, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// - -import Foundation -import Combine -@testable import Core -import TestsFoundation -import CoreData -import XCTest - -class AttachmentPickerInteractorLiveTests: CoreTestCase { - var testee: AttachmentPickerInteractorLive! - - private let batchId: String = "testBatchId" - private let file1 = File.make(from: .make( - id: "p1", - display_name: "PDF File 1", - contentType: "application/pdf", - url: URL(string: "/files/d?download=1")!, - mime_class: "pdf" - )) - private let file2 = File.make(from: .make( - id: "p2", - display_name: "PDF File 2", - contentType: "application/pdf", - url: URL(string: "/files/d?download=2")!, - mime_class: "pdf" - )) - private let alreadyUploadedFiles = CurrentValueSubject<[File], Never>([]) - - override func setUp() { - super.setUp() - testee = AttachmentPickerInteractorLive(batchId: batchId, uploadManager: uploadManager, alreadyUploadedFiles: alreadyUploadedFiles) - } - - func testAddFile() { - testee.addFile(url: file1.url!) - XCTAssertTrue(uploadManager.addWasCalled) - } - - func testUpload() { - testee.addFile(url: file1.url!) - testee.uploadFiles() - XCTAssertTrue(file1.isUploaded) - - testee.addFile(url: file2.url!) - testee.uploadFiles() - XCTAssertTrue(file2.isUploaded) - } - - func testCancel() { - testee.addFile(url: file1.url!) - testee.uploadFiles() - XCTAssertTrue(file1.isUploaded) - - testee.addFile(url: file2.url!) - - testee.cancel() - } - - func testRetry() { - testee.addFile(url: file1.url!) - testee.uploadFiles() - XCTAssertTrue(file1.isUploaded) - - testee.addFile(url: file2.url!) - testee.retry() - XCTAssertTrue(file2.isUploaded) - } - - func testAddFileFromOnlineStore() { - let file = File.make() - var addesFiles: [File] = [] - let expectation = self.expectation(description: "addFile") - var subscriptions: [AnyCancellable] = [] - - testee.addFile(file: file) - alreadyUploadedFiles - .sink { files in - addesFiles = files - expectation.fulfill() - } - .store(in: &subscriptions) - wait(for: [expectation], timeout: 2) - - XCTAssertEqual(addesFiles, [file]) - } - - func testRemoveFile() { - let file = File.make() - var addesFiles: [File] = [] - let expectation = self.expectation(description: "addFile") - var subscriptions: [AnyCancellable] = [] - testee.addFile(file: file) - testee.addFile(url: file1.url!) - - testee.removeFile(file: file) - - alreadyUploadedFiles - .sink { files in - addesFiles = files - expectation.fulfill() - } - .store(in: &subscriptions) - wait(for: [expectation], timeout: 2) - - XCTAssertEqual(addesFiles, []) - } - - func testDeleteFile() { - let apiFile = APIFile.make() - let file = File.make(from: apiFile) - testee.addFile(file: file) - testee.addFile(url: file1.url!) - let deleteRequest = DeleteFileRequest(fileID: file.id!) - api.mock(deleteRequest, value: apiFile) - - XCTAssertFinish(testee.deleteFile(file: file)) - waitForState(.data) - } - - func testIsCancelDialogNeeded() { - XCTAssertFalse(testee.isCancelConfirmationNeeded) - let file = File.make() - testee.addFile(file: file) - file.taskID = "1" - XCTAssertTrue(testee.isCancelConfirmationNeeded) - testee.uploadFiles() - file.taskID = nil - XCTAssertFalse(testee.isCancelConfirmationNeeded) - } - - private func waitForState(_ state: StoreState) { - let stateUpdate = expectation(description: "Expected state reached") - stateUpdate.assertForOverFulfill = false - stateUpdate.fulfill() - wait(for: [stateUpdate], timeout: 1) - } -} diff --git a/Core/CoreTests/Features/Inbox/AttachmentPicker/ViewModel/AttachmentPickerViewModelTests.swift b/Core/CoreTests/Features/Inbox/AttachmentPicker/ViewModel/AttachmentPickerViewModelTests.swift deleted file mode 100644 index c73148690c..0000000000 --- a/Core/CoreTests/Features/Inbox/AttachmentPicker/ViewModel/AttachmentPickerViewModelTests.swift +++ /dev/null @@ -1,115 +0,0 @@ -// -// This file is part of Canvas. -// Copyright (C) 2024-present Instructure, Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// - -import Foundation -import Combine -@testable import Core -import CoreData -import XCTest - -class AttachmentPickerViewModelTests: CoreTestCase { - var testee: AttachmentPickerViewModel! - - private let interactor = AttachmentPickerInteractorPreview() - - override func setUp() { - super.setUp() - testee = AttachmentPickerViewModel(router: environment.router, interactor: interactor) - } - - func testCancelButton() { - let viewController = WeakViewController(UIViewController()) - - testee.didSelectFile(url: URL(string: "testDomain.com/testResourse1")!) - testee.cancelButtonDidTap.accept(viewController) - XCTAssertTrue(testee.fileList.isEmpty) - } - - func testUploadButton() { - let viewController = WeakViewController(UIViewController()) - - let testFile = File.make() - testee.didSelectFile(url: testFile.localFileURL ?? URL(string: "test")!) - XCTAssertTrue(interactor.addFileCalled) - - testee.uploadButtonDidTap.accept(viewController) - XCTAssertTrue(interactor.uploadFilesCalled) - } - - func testRetryButton() { - let viewController = WeakViewController(UIViewController()) - - let testFile = File.make() - testee.didSelectFile(url: testFile.localFileURL ?? URL(string: "test")!) - XCTAssertTrue(interactor.addFileCalled) - - testee.retryButtonDidTap.accept(viewController) - XCTAssertTrue(interactor.retryCalled) - } - - func testAddAttachmentDialog() { - let viewController = WeakViewController(UIViewController()) - - testee.addAttachmentButtonDidTap.accept(viewController) - wait(for: [router.showExpectation], timeout: 1) - let dialog = router.presented as? BottomSheetPickerViewController - XCTAssertNotNil(dialog) - } - - func testErrorHandling() { - interactor.throwError() - - wait(for: [router.showExpectation], timeout: 1) - let dialog = router.presented as? UIAlertController - XCTAssertNotNil(dialog) - XCTAssertEqual(dialog?.title, "Error") - XCTAssertEqual(dialog?.message, "Failed to add attachment. Please try again!") - XCTAssertEqual(dialog?.actions.count, 1) - } - - func testFileDelete() { - testee.deleteFileButtonDidTap.accept(File.make()) - - XCTAssertTrue(interactor.deleteFileCalled) - } - - func testFileRemove() { - testee.removeButtonDidTap.accept(File.make()) - - XCTAssertTrue(interactor.removeFileCalled) - } - - func testFileOpen() { - router.mock("https://canvas.instructure.com/files/1?canEdit=false", factory: { FileDetailsViewController() }) - let file = File.make(from: APIFile.make(url: URL(string: "https://canvas.instructure.com/files/1")!)) - testee.fileSelected.accept((WeakViewController(), file)) - - wait(for: [router.showExpectation], timeout: 1) - let viewController = router.presented as? FileDetailsViewController - XCTAssertNotNil(viewController) - } - - func testFileOpenError() { - let file = File.make(from: APIFile.make(url: URL(string: "https://canvas.instructure.com/files/2")!)) - testee.fileSelected.accept((WeakViewController(), file)) - - wait(for: [router.showExpectation], timeout: 1) - let viewController = router.presented as? UIAlertController - XCTAssertNotNil(viewController) - } -} From dfb62d1bed69dff67e0548455f6662db7490ba7d Mon Sep 17 00:00:00 2001 From: Richard Harangozo Date: Mon, 10 Feb 2025 00:11:50 +0100 Subject: [PATCH 05/13] Remove navigationTitleStyled, replace usages with navigationBarTitleView --- .../CommonUI/ViewModifiers/NavBarItems.swift | 10 ------- .../View/CourseSyncProgressView.swift | 16 +++------- .../View/CourseSyncSelectorView.swift | 16 +++------- .../View/CustomizeCourseView.swift | 16 +++------- .../Grades/View/GradeFilterView.swift | 17 ++++------- .../View/InboxCoursePickerView.swift | 9 +++--- .../View/ModulePublishProgressView.swift | 18 ++++-------- .../View/AssignmentPickerView.swift | 3 +- .../View/CoursePickerView.swift | 3 +- .../View/SubmitAssignmentExtensionView.swift | 29 ++++--------------- 10 files changed, 38 insertions(+), 99 deletions(-) diff --git a/Core/Core/Common/CommonUI/ViewModifiers/NavBarItems.swift b/Core/Core/Common/CommonUI/ViewModifiers/NavBarItems.swift index d42dac244c..2b075afe66 100644 --- a/Core/Core/Common/CommonUI/ViewModifiers/NavBarItems.swift +++ b/Core/Core/Common/CommonUI/ViewModifiers/NavBarItems.swift @@ -62,14 +62,4 @@ extension View { ToolbarItem(placement: .navigationBarTrailing, content: trailing) } } - - /** - The built-in `.navigationTitle(Text)` modifier ignores all font and color modifiers on the text - so we use this `toolbar` based solution to have the title styled. - */ - public func navigationTitleStyled(_ title: T) -> some View where T: View { - toolbar { - ToolbarItem(placement: .principal, content: { title }) - } - } } diff --git a/Core/Core/Features/CourseSync/CourseSyncProgress/View/CourseSyncProgressView.swift b/Core/Core/Features/CourseSync/CourseSyncProgress/View/CourseSyncProgressView.swift index 4bfffceca7..58c88d896f 100644 --- a/Core/Core/Features/CourseSync/CourseSyncProgress/View/CourseSyncProgressView.swift +++ b/Core/Core/Features/CourseSync/CourseSyncProgress/View/CourseSyncProgressView.swift @@ -27,7 +27,10 @@ struct CourseSyncProgressView: View { var body: some View { content - .navigationTitleStyled(navBarTitleView) + .navigationBarTitleView( + title: String(localized: "Offline Content", bundle: .core), + subtitle: String(localized: "All Courses", bundle: .core) + ) .navigationBarItems(leading: cancelButton, trailing: trailingBarItem) .navigationBarStyle(.modal) .confirmationAlert( @@ -91,17 +94,6 @@ struct CourseSyncProgressView: View { .padding(.vertical, 32) } - private var navBarTitleView: some View { - VStack(spacing: 1) { - Text("Offline Content", bundle: .core) - .font(.semibold16) - .foregroundColor(.textDarkest) - Text("All Courses", bundle: .core) - .font(.regular12) - .foregroundColor(.textDark) - } - } - private var cancelButton: some View { Button { viewModel.cancelButtonDidTap.accept() diff --git a/Core/Core/Features/CourseSync/CourseSyncSelector/View/CourseSyncSelectorView.swift b/Core/Core/Features/CourseSync/CourseSyncSelector/View/CourseSyncSelectorView.swift index bac4811af6..73a05b664f 100644 --- a/Core/Core/Features/CourseSync/CourseSyncSelector/View/CourseSyncSelectorView.swift +++ b/Core/Core/Features/CourseSync/CourseSyncSelector/View/CourseSyncSelectorView.swift @@ -36,7 +36,10 @@ struct CourseSyncSelectorView: View { var body: some View { content .background(Color.backgroundLightest) - .navigationTitleStyled(navBarTitleView) + .navigationBarTitleView( + title: String(localized: "Offline Content", bundle: .core), + subtitle: viewModel.navBarSubtitle + ) .navigationBarItems(leading: leftNavBarButton, trailing: cancelButton) .navigationBarStyle(.modal) } @@ -179,17 +182,6 @@ struct CourseSyncSelectorView: View { .padding(.vertical, 32) } - private var navBarTitleView: some View { - VStack(spacing: 1) { - Text("Offline Content", bundle: .core) - .font(.semibold16) - .foregroundColor(.textDarkest) - Text(viewModel.navBarSubtitle) - .font(.regular12) - .foregroundColor(.textDark) - } - } - private var cancelButton: some View { Button { viewModel.cancelButtonDidTap.accept(viewController) diff --git a/Core/Core/Features/Dashboard/CourseCardList/View/CustomizeCourseView.swift b/Core/Core/Features/Dashboard/CourseCardList/View/CustomizeCourseView.swift index 801d56f8c9..549e26e9ce 100644 --- a/Core/Core/Features/Dashboard/CourseCardList/View/CustomizeCourseView.swift +++ b/Core/Core/Features/Dashboard/CourseCardList/View/CustomizeCourseView.swift @@ -86,7 +86,10 @@ struct CustomizeCourseView: View { } InstUI.Divider() } - .navigationTitleStyled(navBarTitleView) + .navigationBarTitleView( + title: String(localized: "Customize Course", bundle: .core), + subtitle: viewModel.courseName + ) .navigationBarItems(leading: cancelNavBarButton, trailing: doneNavBarButton) .navigationBarStyle(.modal) .onReceive(viewModel.dismissView) { _ in @@ -101,17 +104,6 @@ struct CustomizeCourseView: View { } } } - private var navBarTitleView: some View { - VStack(spacing: 1) { - Text("Customize Course", bundle: .core) - .font(.semibold16) - .foregroundColor(.textDarkest) - Text(viewModel.courseName) - .font(.regular12) - .foregroundColor(.textDark) - } - } - private var cancelNavBarButton: some View { Button(action: cancel, label: { Text("Cancel", bundle: .core).fontWeight(.regular) diff --git a/Core/Core/Features/Grades/View/GradeFilterView.swift b/Core/Core/Features/Grades/View/GradeFilterView.swift index 9551c96efb..21a1d70ca1 100644 --- a/Core/Core/Features/Grades/View/GradeFilterView.swift +++ b/Core/Core/Features/Grades/View/GradeFilterView.swift @@ -38,19 +38,12 @@ public struct GradeFilterView: View { } sortBySection } - .navigationTitleStyled(navBarTitleView) + .navigationBarTitleView( + title: String(localized: "Grade List Preferences", bundle: .core), + subtitle: viewModel.courseName + ) .navigationBarItems(leading: cancelButton, trailing: sendButton) - } - } - - private var navBarTitleView: some View { - VStack { - Text(String(localized: "Grade List Preferences", bundle: .core)) - .foregroundStyle(Color.textDarkest) - .font(.semibold16) - Text(viewModel.courseName ?? "") - .foregroundStyle(Color.textDark) - .font(.regular12) + .navigationBarStyle(.modal) } } diff --git a/Core/Core/Features/Inbox/InboxCoursePicker/View/InboxCoursePickerView.swift b/Core/Core/Features/Inbox/InboxCoursePicker/View/InboxCoursePickerView.swift index 92d683cfee..c297544bad 100644 --- a/Core/Core/Features/Inbox/InboxCoursePicker/View/InboxCoursePickerView.swift +++ b/Core/Core/Features/Inbox/InboxCoursePicker/View/InboxCoursePickerView.swift @@ -27,12 +27,13 @@ public struct InboxCoursePickerView: View { public var body: some View { ScrollView { - let titleText = viewModel.groups.isEmpty - ? Text("Select a Course", bundle: .core) - : Text("Select a Course or a Group", bundle: .core) + let title = viewModel.groups.isEmpty + ? String(localized: "Select a Course", bundle: .core) + : String(localized: "Select a Course or a Group", bundle: .core) content - .navigationTitleStyled(titleText.font(.semibold17).foregroundColor(.textDarkest)) + .navigationBarTitleView(title: title) .navigationBarTitleDisplayMode(.inline) + .navigationBarStyle(.modal) } .refreshable { await viewModel.refresh() diff --git a/Core/Core/Features/Modules/ModuleList/View/ModulePublishProgressView.swift b/Core/Core/Features/Modules/ModuleList/View/ModulePublishProgressView.swift index b90f70b97a..f29b5d5fe8 100644 --- a/Core/Core/Features/Modules/ModuleList/View/ModulePublishProgressView.swift +++ b/Core/Core/Features/Modules/ModuleList/View/ModulePublishProgressView.swift @@ -29,7 +29,7 @@ struct ModulePublishProgressView: View { var body: some View { content - .navigationTitleStyled(titleText.navigationBarTitleStyle()) + .navigationBarTitleView(title: title) .navigationBarItems(leading: dismissButton, trailing: trailingBarButton) .navigationBarStyle(.modal) } @@ -50,16 +50,16 @@ struct ModulePublishProgressView: View { // MARK: - Navigation Bar - private var titleText: Text { + private var title: String { switch viewModel.title { case .allModulesAndItems: - Text("All Modules and Items", bundle: .core) + String(localized: "All Modules and Items", bundle: .core) case .allModules: - Text("All Modules") + String(localized: "All Modules") case .selectedModuleAndItems: - Text("Selected Module and Items", bundle: .core) + String(localized: "Selected Module and Items", bundle: .core) case .selectedModule: - Text("Selected Module", bundle: .core) + String(localized: "Selected Module", bundle: .core) } } @@ -171,12 +171,6 @@ struct ModulePublishProgressView: View { // MARK: - Helpers private extension View { - func navigationBarTitleStyle() -> some View { - self - .font(.semibold16) - .foregroundStyle(Color.textDarkest) - } - func navigationBarButtonStyle() -> some View { self .font(.semibold16) diff --git a/Core/Core/Features/SubmitAssignmentExtension/View/AssignmentPickerView.swift b/Core/Core/Features/SubmitAssignmentExtension/View/AssignmentPickerView.swift index 6f765bf768..7465c58e1c 100644 --- a/Core/Core/Features/SubmitAssignmentExtension/View/AssignmentPickerView.swift +++ b/Core/Core/Features/SubmitAssignmentExtension/View/AssignmentPickerView.swift @@ -28,8 +28,9 @@ public struct AssignmentPickerView: View { public var body: some View { content - .navigationTitleStyled(Text("Select Assignment", bundle: .core).font(.semibold17).foregroundColor(.textDarkest)) + .navigationBarTitleView(title: String(localized: "Select Assignment", bundle: .core)) .navigationBarTitleDisplayMode(.inline) + .navigationBarStyle(.modal) .onReceive(viewModel.dismissViewDidTrigger) { presentationMode.wrappedValue.dismiss() } diff --git a/Core/Core/Features/SubmitAssignmentExtension/View/CoursePickerView.swift b/Core/Core/Features/SubmitAssignmentExtension/View/CoursePickerView.swift index 4f51be2377..99da50517f 100644 --- a/Core/Core/Features/SubmitAssignmentExtension/View/CoursePickerView.swift +++ b/Core/Core/Features/SubmitAssignmentExtension/View/CoursePickerView.swift @@ -28,8 +28,9 @@ public struct CoursePickerView: View { public var body: some View { content - .navigationTitleStyled(Text("Select Course", bundle: .core).font(.semibold17).foregroundColor(.textDarkest)) + .navigationBarTitleView(title: String(localized: "Select Course", bundle: .core)) .navigationBarTitleDisplayMode(.inline) + .navigationBarStyle(.modal) } @ViewBuilder diff --git a/Core/Core/Features/SubmitAssignmentExtension/View/SubmitAssignmentExtensionView.swift b/Core/Core/Features/SubmitAssignmentExtension/View/SubmitAssignmentExtensionView.swift index 708c5e2a8e..cdcd38765b 100644 --- a/Core/Core/Features/SubmitAssignmentExtension/View/SubmitAssignmentExtensionView.swift +++ b/Core/Core/Features/SubmitAssignmentExtension/View/SubmitAssignmentExtensionView.swift @@ -30,6 +30,8 @@ public struct SubmitAssignmentExtensionView: View { @AccessibilityFocusState private var accessibilityFocus: AccessibilityFocusArea? + private let title: String = String(localized: "Canvas Student", bundle: .core) + public init(viewModel: SubmitAssignmentExtensionViewModel) { self.viewModel = viewModel } @@ -50,10 +52,10 @@ public struct SubmitAssignmentExtensionView: View { Text("Please log in via the application", bundle: .core) .foregroundColor(.textDarkest) .font(.regular16) - .navigationBarGlobal() - .navigationTitleStyled(title) + .navigationBarTitleView(title: title) .navigationBarTitleDisplayMode(.inline) .navBarItems(trailing: cancelButton) + .navigationBarStyle(.modal) } private var contentView: some View { @@ -69,34 +71,15 @@ public struct SubmitAssignmentExtensionView: View { } .padding(.horizontal, 20) } - .navigationBarGlobal() - .navigationTitleStyled(titleView) + .navigationBarTitleView(title: title, subtitle: env.currentSession?.userName) .navigationBarTitleDisplayMode(.inline) .navBarItems(leading: cancelButton, trailing: submitButton) + .navigationBarStyle(.modal) .onDisappear { accessibilityFocus = nil } } - private var titleView: some View { - VStack(spacing: 1) { - title - if let userName = env.currentSession?.userName { - Text(userName) - .font(.regular12) - .foregroundColor(.textDark) - } - } - .accessibilityElement(children: .combine) - .accessibilityAddTraits(.isHeader) - } - - private var title: Text { - Text("Canvas Student", bundle: .core) - .font(.semibold16) - .foregroundColor(.textDarkest) - } - @ViewBuilder private var commentBox: some View { TextEditor(text: $viewModel.comment) From 22879c5df384d60767c69706e227764bd72ca8d2 Mon Sep 17 00:00:00 2001 From: Richard Harangozo Date: Mon, 10 Feb 2025 09:35:19 +0100 Subject: [PATCH 06/13] Add title only modifier variant --- .../UIKitSwiftUIBridging/UINavigationControllerTheme.swift | 6 +++++- .../InboxCoursePicker/View/InboxCoursePickerView.swift | 2 +- .../Modules/ModuleList/View/ModulePublishProgressView.swift | 2 +- .../View/AssignmentPickerView.swift | 2 +- .../SubmitAssignmentExtension/View/CoursePickerView.swift | 2 +- .../View/SubmitAssignmentExtensionView.swift | 2 +- 6 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/UINavigationControllerTheme.swift b/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/UINavigationControllerTheme.swift index 03473f7372..5d6ef413d3 100644 --- a/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/UINavigationControllerTheme.swift +++ b/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/UINavigationControllerTheme.swift @@ -101,7 +101,7 @@ extension View { modifier(TitleSubtitleModifier(title: title, subtitle: subtitle)) } - public func navigationBarTitleView(title: String, subtitle: String? = nil) -> some View { + public func navigationBarTitleView(title: String, subtitle: String?) -> some View { toolbar { ToolbarItem(placement: .principal) { InstUI.NavigationBarTitleView(title: title, subtitle: subtitle) @@ -109,6 +109,10 @@ extension View { } } + public func navigationBarTitleView(_ title: String) -> some View { + navigationBarTitleView(title: title, subtitle: nil) + } + /// Sets the UINavigationBar's background color, button color to match the `Brand.shared` colors, /// sets the button font and sets the brand logo as the titleView. /// Only affects Views inside a UINavigationController. diff --git a/Core/Core/Features/Inbox/InboxCoursePicker/View/InboxCoursePickerView.swift b/Core/Core/Features/Inbox/InboxCoursePicker/View/InboxCoursePickerView.swift index c297544bad..6295cb14a4 100644 --- a/Core/Core/Features/Inbox/InboxCoursePicker/View/InboxCoursePickerView.swift +++ b/Core/Core/Features/Inbox/InboxCoursePicker/View/InboxCoursePickerView.swift @@ -31,7 +31,7 @@ public struct InboxCoursePickerView: View { ? String(localized: "Select a Course", bundle: .core) : String(localized: "Select a Course or a Group", bundle: .core) content - .navigationBarTitleView(title: title) + .navigationBarTitleView(title) .navigationBarTitleDisplayMode(.inline) .navigationBarStyle(.modal) } diff --git a/Core/Core/Features/Modules/ModuleList/View/ModulePublishProgressView.swift b/Core/Core/Features/Modules/ModuleList/View/ModulePublishProgressView.swift index f29b5d5fe8..8d2c2b59d7 100644 --- a/Core/Core/Features/Modules/ModuleList/View/ModulePublishProgressView.swift +++ b/Core/Core/Features/Modules/ModuleList/View/ModulePublishProgressView.swift @@ -29,7 +29,7 @@ struct ModulePublishProgressView: View { var body: some View { content - .navigationBarTitleView(title: title) + .navigationBarTitleView(title) .navigationBarItems(leading: dismissButton, trailing: trailingBarButton) .navigationBarStyle(.modal) } diff --git a/Core/Core/Features/SubmitAssignmentExtension/View/AssignmentPickerView.swift b/Core/Core/Features/SubmitAssignmentExtension/View/AssignmentPickerView.swift index 7465c58e1c..62586d4472 100644 --- a/Core/Core/Features/SubmitAssignmentExtension/View/AssignmentPickerView.swift +++ b/Core/Core/Features/SubmitAssignmentExtension/View/AssignmentPickerView.swift @@ -28,7 +28,7 @@ public struct AssignmentPickerView: View { public var body: some View { content - .navigationBarTitleView(title: String(localized: "Select Assignment", bundle: .core)) + .navigationBarTitleView(String(localized: "Select Assignment", bundle: .core)) .navigationBarTitleDisplayMode(.inline) .navigationBarStyle(.modal) .onReceive(viewModel.dismissViewDidTrigger) { diff --git a/Core/Core/Features/SubmitAssignmentExtension/View/CoursePickerView.swift b/Core/Core/Features/SubmitAssignmentExtension/View/CoursePickerView.swift index 99da50517f..aba636ed98 100644 --- a/Core/Core/Features/SubmitAssignmentExtension/View/CoursePickerView.swift +++ b/Core/Core/Features/SubmitAssignmentExtension/View/CoursePickerView.swift @@ -28,7 +28,7 @@ public struct CoursePickerView: View { public var body: some View { content - .navigationBarTitleView(title: String(localized: "Select Course", bundle: .core)) + .navigationBarTitleView(String(localized: "Select Course", bundle: .core)) .navigationBarTitleDisplayMode(.inline) .navigationBarStyle(.modal) } diff --git a/Core/Core/Features/SubmitAssignmentExtension/View/SubmitAssignmentExtensionView.swift b/Core/Core/Features/SubmitAssignmentExtension/View/SubmitAssignmentExtensionView.swift index cdcd38765b..5acfb781d2 100644 --- a/Core/Core/Features/SubmitAssignmentExtension/View/SubmitAssignmentExtensionView.swift +++ b/Core/Core/Features/SubmitAssignmentExtension/View/SubmitAssignmentExtensionView.swift @@ -52,7 +52,7 @@ public struct SubmitAssignmentExtensionView: View { Text("Please log in via the application", bundle: .core) .foregroundColor(.textDarkest) .font(.regular16) - .navigationBarTitleView(title: title) + .navigationBarTitleView(title) .navigationBarTitleDisplayMode(.inline) .navBarItems(trailing: cancelButton) .navigationBarStyle(.modal) From 4db5eab80546166fdd1ef5c540c4a6e897f7fdb7 Mon Sep 17 00:00:00 2001 From: Richard Harangozo Date: Mon, 10 Feb 2025 09:53:32 +0100 Subject: [PATCH 07/13] Replace native navigation title calls --- Core/Core/Features/About/View/AboutView.swift | 3 ++- .../AssignmentDueDates/View/AssignmentDueDatesView.swift | 3 ++- .../AssignmentEditor/AssignmentAssigneeList.swift | 3 ++- .../AssignmentEditor/AssignmentAssigneePicker.swift | 3 ++- .../AssignmentEditor/AssignmentEditorView.swift | 4 +++- .../CourseSyncSettings/View/CourseSyncSettingsView.swift | 3 ++- .../View/CourseSmartSearchFilterEditorView.swift | 3 ++- .../SmartSearch/View/CourseSmartSearchHelpView.swift | 3 ++- .../Dashboard/Settings/View/DashboardSettingsView.swift | 2 +- .../Features/DeveloperMenu/BaseScreenTesterScreen.swift | 3 ++- Core/Core/Features/DeveloperMenu/DeveloperMenuView.swift | 3 ++- .../DeveloperMenu/PushNotificationDebugView.swift | 3 ++- .../FileUploadProgress/View/FileProgressListView.swift | 3 ++- .../Features/Files/View/FileEditor/FileEditorView.swift | 9 ++++++++- .../Addressbook/View/AddressbookRecipientView.swift | 3 ++- .../Inbox/Addressbook/View/AddressbookRoleView.swift | 3 ++- .../Inbox/AttachmentPicker/View/FilePickerView.swift | 3 ++- .../Inbox/MessageDetails/View/MessageDetailsView.swift | 3 ++- .../Login/LoginUsePolicy/LoginUsePolicyView.swift | 4 ++-- .../ModuleList/View/ModuleFilePermissionEditorView.swift | 3 ++- Core/Core/Features/Pages/PageEditor/PageEditorView.swift | 7 ++++++- .../CalendarEvent/View/EditCalendarEventScreen.swift | 3 ++- .../CalendarEvent/View/EditCustomFrequencyScreen.swift | 3 ++- .../CalendarEvent/View/SelectEventFrequencyScreen.swift | 3 ++- .../CalendarFilter/View/CalendarFilterScreen.swift | 3 ++- .../CalendarSelector/View/SelectCalendarScreen.swift | 3 ++- .../CalendarToDo/View/CalendarToDoDetailsScreen.swift | 2 +- .../CalendarToDo/View/EditCalendarToDoScreen.swift | 3 ++- .../QuizDetails/Teacher/View/TeacherQuizEditorView.swift | 4 +++- .../Quizzes/QuizPreview/View/QuizPreviewView.swift | 3 ++- Core/Core/Features/Syllabus/SyllabusEditorView.swift | 4 +++- .../WebSitePreview/View/WebSitePreviewView.swift | 3 ++- .../ViewModel/WebSitePreviewViewModel.swift | 3 ++- .../View/AssignmentReminderDatePickerView.swift | 3 ++- 34 files changed, 79 insertions(+), 35 deletions(-) diff --git a/Core/Core/Features/About/View/AboutView.swift b/Core/Core/Features/About/View/AboutView.swift index e7b09554d6..401fe2fbb1 100644 --- a/Core/Core/Features/About/View/AboutView.swift +++ b/Core/Core/Features/About/View/AboutView.swift @@ -51,7 +51,8 @@ public struct AboutView: View { } .accessibilityIdentifier("AboutView") .background(Color.backgroundLightest) - .navigationTitle(viewModel.title) + .navigationBarTitleView(viewModel.title) + .navigationBarStyle(.modal) } } diff --git a/Core/Core/Features/Assignments/AssignmentDueDates/View/AssignmentDueDatesView.swift b/Core/Core/Features/Assignments/AssignmentDueDates/View/AssignmentDueDatesView.swift index abb4586695..461f09921e 100644 --- a/Core/Core/Features/Assignments/AssignmentDueDates/View/AssignmentDueDatesView.swift +++ b/Core/Core/Features/Assignments/AssignmentDueDates/View/AssignmentDueDatesView.swift @@ -44,7 +44,8 @@ public struct AssignmentDueDatesView: View { } } .background(Color.backgroundLightest) - .navigationTitle(model.title) + .navigationBarTitleView(model.title) + .navigationBarStyle(.color(nil)) } } diff --git a/Core/Core/Features/Assignments/AssignmentEditor/AssignmentAssigneeList.swift b/Core/Core/Features/Assignments/AssignmentEditor/AssignmentAssigneeList.swift index dda0cbdd31..67f695ac6d 100644 --- a/Core/Core/Features/Assignments/AssignmentEditor/AssignmentAssigneeList.swift +++ b/Core/Core/Features/Assignments/AssignmentEditor/AssignmentAssigneeList.swift @@ -65,7 +65,8 @@ struct AssigmentAssigneeList: View { } .background((isEmpty ? Color.backgroundLightest : Color.backgroundGrouped).edgesIgnoringSafeArea(.all)) } } - .navigationBarTitle(Text("Add Assignee", bundle: .core)) + .navigationBarTitleView(String(localized: "Add Assignee", bundle: .core)) + .navigationBarStyle(.modal) } @ViewBuilder diff --git a/Core/Core/Features/Assignments/AssignmentEditor/AssignmentAssigneePicker.swift b/Core/Core/Features/Assignments/AssignmentEditor/AssignmentAssigneePicker.swift index c3d1e90fc2..12da114e19 100644 --- a/Core/Core/Features/Assignments/AssignmentEditor/AssignmentAssigneePicker.swift +++ b/Core/Core/Features/Assignments/AssignmentEditor/AssignmentAssigneePicker.swift @@ -54,7 +54,7 @@ struct AssignmentAssigneePicker: View { var body: some View { form - .navigationBarTitle(Text("Assign to", bundle: .core)) + .navigationBarTitleView(String(localized: "Assign to", bundle: .core)) .navigationBarItems( trailing: Button(action: save, label: { Text("Done", bundle: .core).bold() @@ -62,6 +62,7 @@ struct AssignmentAssigneePicker: View { .disabled(isSpinning) .identifier("DiscussionEditor.doneButton") ) + .navigationBarStyle(.modal) .onAppear(perform: load) } diff --git a/Core/Core/Features/Assignments/AssignmentEditor/AssignmentEditorView.swift b/Core/Core/Features/Assignments/AssignmentEditor/AssignmentEditorView.swift index ebcc6a4256..290b56ad97 100644 --- a/Core/Core/Features/Assignments/AssignmentEditor/AssignmentEditorView.swift +++ b/Core/Core/Features/Assignments/AssignmentEditor/AssignmentEditorView.swift @@ -53,7 +53,8 @@ public struct AssignmentEditorView: View, ScreenViewTrackable { public var body: some View { form - .navigationBarTitle(Text("Edit Assignment", bundle: .core), displayMode: .inline) + .navigationBarTitleView(String(localized: "Edit Assignment", bundle: .core)) + .navigationBarTitleDisplayMode(.inline) .navBarItems(leading: { Button(action: { env.router.dismiss(controller) @@ -68,6 +69,7 @@ public struct AssignmentEditorView: View, ScreenViewTrackable { .disabled(isLoading || isSaving) .identifier("AssignmentEditor.doneButton") }) + .navigationBarStyle(.modal) .alert(item: $alert) { alert in switch alert { diff --git a/Core/Core/Features/CourseSync/CourseSyncSettings/View/CourseSyncSettingsView.swift b/Core/Core/Features/CourseSync/CourseSyncSettings/View/CourseSyncSettingsView.swift index 86e6af49b0..ddeae6b8af 100644 --- a/Core/Core/Features/CourseSync/CourseSyncSettings/View/CourseSyncSettingsView.swift +++ b/Core/Core/Features/CourseSync/CourseSyncSettings/View/CourseSyncSettingsView.swift @@ -43,7 +43,8 @@ struct CourseSyncSettingsView: View { } } .background(Color.backgroundLightest) - .navigationTitle(Text("Synchronization", bundle: .core)) + .navigationBarTitleView(String(localized: "Synchronization", bundle: .core)) + .navigationBarStyle(.modal) .confirmationAlert(isPresented: $viewModel.isShowingConfirmationDialog, presenting: viewModel.confirmAlert) } diff --git a/Core/Core/Features/Courses/SmartSearch/View/CourseSmartSearchFilterEditorView.swift b/Core/Core/Features/Courses/SmartSearch/View/CourseSmartSearchFilterEditorView.swift index 66c3b3fd50..1ac2c1741f 100644 --- a/Core/Core/Features/Courses/SmartSearch/View/CourseSmartSearchFilterEditorView.swift +++ b/Core/Core/Features/Courses/SmartSearch/View/CourseSmartSearchFilterEditorView.swift @@ -49,7 +49,7 @@ public struct CourseSmartSearchFilterEditorView: View { } } .listStyle(.plain) - .navigationTitle(Text("Search Preferences", bundle: .core)) + .navigationBarTitleView(String(localized: "Search Preferences", bundle: .core)) .navigationBarTitleDisplayMode(.inline) .toolbar { @@ -70,6 +70,7 @@ public struct CourseSmartSearchFilterEditorView: View { } } } + .navigationBarStyle(.modal) } .tint(contextColor) } diff --git a/Core/Core/Features/Courses/SmartSearch/View/CourseSmartSearchHelpView.swift b/Core/Core/Features/Courses/SmartSearch/View/CourseSmartSearchHelpView.swift index 7088bc63be..c918c3664d 100644 --- a/Core/Core/Features/Courses/SmartSearch/View/CourseSmartSearchHelpView.swift +++ b/Core/Core/Features/Courses/SmartSearch/View/CourseSmartSearchHelpView.swift @@ -91,7 +91,7 @@ public struct CourseSmartSearchHelpView: View { .padding() .padding(.bottom, spacing) } - .navigationTitle(Text("How it works", bundle: .core)) + .navigationBarTitleView(String(localized: "How it works", bundle: .core)) .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .topBarLeading) { @@ -103,6 +103,7 @@ public struct CourseSmartSearchHelpView: View { .tint(contextColor) } } + .navigationBarStyle(.modal) } private var spacing: CGFloat { diff --git a/Core/Core/Features/Dashboard/Settings/View/DashboardSettingsView.swift b/Core/Core/Features/Dashboard/Settings/View/DashboardSettingsView.swift index 68ca79b3da..17741ee84c 100644 --- a/Core/Core/Features/Dashboard/Settings/View/DashboardSettingsView.swift +++ b/Core/Core/Features/Dashboard/Settings/View/DashboardSettingsView.swift @@ -77,8 +77,8 @@ public struct DashboardSettingsView: View { .padding(.vertical, verticalPadding) } .background(Color.backgroundLightest.ignoresSafeArea()) + .navigationBarTitleView(String(localized: "Dashboard Settings", bundle: .core)) .navigationBarStyle(.modal) - .navigationTitle(Text("Dashboard Settings", bundle: .core)) } private func header(label: Text) -> some View { diff --git a/Core/Core/Features/DeveloperMenu/BaseScreenTesterScreen.swift b/Core/Core/Features/DeveloperMenu/BaseScreenTesterScreen.swift index 5c1f093bb3..31efbfc5ec 100644 --- a/Core/Core/Features/DeveloperMenu/BaseScreenTesterScreen.swift +++ b/Core/Core/Features/DeveloperMenu/BaseScreenTesterScreen.swift @@ -44,6 +44,7 @@ struct BaseScreenTesterScreen: View { } .padding() } - .navigationTitle(Text(verbatim: "Base Screen Test")) + .navigationBarTitleView("Base Screen Test") + .navigationBarStyle(.modal) } } diff --git a/Core/Core/Features/DeveloperMenu/DeveloperMenuView.swift b/Core/Core/Features/DeveloperMenu/DeveloperMenuView.swift index 49bf3130da..d4f07a373c 100644 --- a/Core/Core/Features/DeveloperMenu/DeveloperMenuView.swift +++ b/Core/Core/Features/DeveloperMenu/DeveloperMenuView.swift @@ -58,7 +58,7 @@ public struct DeveloperMenuView: View { } } .background(Color.backgroundLightest) - .navigationTitle("🛠 Developer Menu") + .navigationBarTitleView("🛠 Developer Menu") .navBarItems(trailing: { Button(action: { router.dismiss(controller) @@ -66,6 +66,7 @@ public struct DeveloperMenuView: View { Text("Done", bundle: .core).fontWeight(.regular) }) }) + .navigationBarStyle(.modal) .snackBar(viewModel: snackBarViewModel) .onAppear { setupItems() diff --git a/Core/Core/Features/DeveloperMenu/PushNotificationDebugView.swift b/Core/Core/Features/DeveloperMenu/PushNotificationDebugView.swift index a23b3d429d..ee13895a15 100644 --- a/Core/Core/Features/DeveloperMenu/PushNotificationDebugView.swift +++ b/Core/Core/Features/DeveloperMenu/PushNotificationDebugView.swift @@ -42,6 +42,7 @@ public struct PushNotificationDebugView: View { } .listStyle(.plain) .background(Color.backgroundLightest) - .navigationTitle("Push Notifications") + .navigationBarTitleView("Push Notifications") + .navigationBarStyle(.modal) } } diff --git a/Core/Core/Features/Files/FileUploadProgress/View/FileProgressListView.swift b/Core/Core/Features/Files/FileUploadProgress/View/FileProgressListView.swift index cb246e086e..7b1355a686 100644 --- a/Core/Core/Features/Files/FileUploadProgress/View/FileProgressListView.swift +++ b/Core/Core/Features/Files/FileUploadProgress/View/FileProgressListView.swift @@ -45,8 +45,9 @@ struct FileProgressListView: View where ViewModel: FileProgressListVi } } .background(Color.backgroundLightest) + .navigationBarTitleView(viewModel.title) .navBarItems(leading: barButton(viewModel.leftBarButton), trailing: barButton(viewModel.rightBarButton)) - .navigationTitle(viewModel.title) + .navigationBarStyle(.modal) .onReceive(viewModel.presentDialog) { env.router.show($0, from: controller, options: .modal()) } diff --git a/Core/Core/Features/Files/View/FileEditor/FileEditorView.swift b/Core/Core/Features/Files/View/FileEditor/FileEditorView.swift index bb5a3a01f6..664a56c15d 100644 --- a/Core/Core/Features/Files/View/FileEditor/FileEditorView.swift +++ b/Core/Core/Features/Files/View/FileEditor/FileEditorView.swift @@ -76,7 +76,11 @@ public struct FileEditorView: View { public var body: some View { form - .navigationBarTitle(isFile ? Text("Edit File", bundle: .core) : Text("Edit Folder", bundle: .core), displayMode: .inline) + .navigationBarTitleView(isFile + ? String(localized: "Edit File", bundle: .core) + : String(localized: "Edit Folder", bundle: .core) + ) + .navigationBarTitleDisplayMode(.inline) .navigationBarItems( leading: Button(action: dismiss, label: { Text("Cancel", bundle: .core) @@ -88,6 +92,8 @@ public struct FileEditorView: View { .disabled(isLoading || isSaving) .identifier("FileEditor.doneButton") ) + .navigationBarStyle(.modal) + .alert(item: $alertItem) { switch $0 { case .error(let error): @@ -106,6 +112,7 @@ public struct FileEditorView: View { }) } } + .onAppear(perform: load) } diff --git a/Core/Core/Features/Inbox/Addressbook/View/AddressbookRecipientView.swift b/Core/Core/Features/Inbox/Addressbook/View/AddressbookRecipientView.swift index c20d04943f..023a21db24 100644 --- a/Core/Core/Features/Inbox/Addressbook/View/AddressbookRecipientView.swift +++ b/Core/Core/Features/Inbox/Addressbook/View/AddressbookRecipientView.swift @@ -40,8 +40,9 @@ public struct AddressbookRecipientView: View, ScreenViewTrackable { placement: .navigationBarDrawer(displayMode: .always) ) .background(Color.backgroundLightest) - .navigationTitle(viewModel.title) + .navigationBarTitleView(viewModel.title) .navigationBarItems(trailing: doneButton) + .navigationBarStyle(.modal) } private var separator: some View { diff --git a/Core/Core/Features/Inbox/Addressbook/View/AddressbookRoleView.swift b/Core/Core/Features/Inbox/Addressbook/View/AddressbookRoleView.swift index aff2dc88ba..3142370a2b 100644 --- a/Core/Core/Features/Inbox/Addressbook/View/AddressbookRoleView.swift +++ b/Core/Core/Features/Inbox/Addressbook/View/AddressbookRoleView.swift @@ -59,8 +59,9 @@ struct AddressbookRoleView: View, ScreenViewTrackable { await viewModel.refresh() } .background(Color.backgroundLightest) - .navigationTitle(viewModel.title) + .navigationBarTitleView(viewModel.title) .navigationBarItems(trailing: doneButton) + .navigationBarStyle(.modal) } private var loadingIndicator: some View { diff --git a/Core/Core/Features/Inbox/AttachmentPicker/View/FilePickerView.swift b/Core/Core/Features/Inbox/AttachmentPicker/View/FilePickerView.swift index 794f01ac10..8d18374f52 100644 --- a/Core/Core/Features/Inbox/AttachmentPicker/View/FilePickerView.swift +++ b/Core/Core/Features/Inbox/AttachmentPicker/View/FilePickerView.swift @@ -33,9 +33,10 @@ public struct FilePickerView: View { } .font(.regular12) .foregroundColor(.textDarkest) - .navigationTitle(viewModel.title) + .navigationBarTitleView(viewModel.title) .navigationBarItems(trailing: cancelButton) .navigationBarGenericBackButton() + .navigationBarStyle(.modal) } var contentView: some View { diff --git a/Core/Core/Features/Inbox/MessageDetails/View/MessageDetailsView.swift b/Core/Core/Features/Inbox/MessageDetails/View/MessageDetailsView.swift index 9dc45a2134..50461cb043 100644 --- a/Core/Core/Features/Inbox/MessageDetails/View/MessageDetailsView.swift +++ b/Core/Core/Features/Inbox/MessageDetails/View/MessageDetailsView.swift @@ -50,8 +50,9 @@ public struct MessageDetailsView: View { } } .background(Color.backgroundLightest) - .navigationTitle(model.title) + .navigationBarTitleView(model.title) .navigationBarItems(trailing: moreButton) + .navigationBarStyle(.global) .snackBar(viewModel: model.snackBarViewModel) } diff --git a/Core/Core/Features/Login/LoginUsePolicy/LoginUsePolicyView.swift b/Core/Core/Features/Login/LoginUsePolicy/LoginUsePolicyView.swift index a1a2394423..dfb4541c4f 100644 --- a/Core/Core/Features/Login/LoginUsePolicy/LoginUsePolicyView.swift +++ b/Core/Core/Features/Login/LoginUsePolicy/LoginUsePolicyView.swift @@ -60,8 +60,7 @@ struct LoginUsePolicyView: View { Spacer() } .background(Color.backgroundLightest.edgesIgnoringSafeArea(.all)) - .navigationBarStyle(.color(Brand.shared.primary)) - .navigationTitle(Text("Acceptable Use Policy", bundle: .core)) + .navigationBarTitleView(String(localized: "Acceptable Use Policy", bundle: .core)) .navigationBarItems( leading: Button(action: { controller.value.dismiss(animated: true) { @@ -83,6 +82,7 @@ struct LoginUsePolicyView: View { .opacity(viewModel.isAccepted ? 1 : 0.4) }).disabled(!viewModel.isAccepted) ) + .navigationBarStyle(.color(Brand.shared.primary)) .alert(isPresented: $viewModel.showError) { Alert(title: Text(viewModel.errorText ?? String(localized: "Something went wrong", bundle: .core))) } diff --git a/Core/Core/Features/Modules/ModuleList/View/ModuleFilePermissionEditorView.swift b/Core/Core/Features/Modules/ModuleList/View/ModuleFilePermissionEditorView.swift index 845575d3f7..9c5c23c1b8 100644 --- a/Core/Core/Features/Modules/ModuleList/View/ModuleFilePermissionEditorView.swift +++ b/Core/Core/Features/Modules/ModuleList/View/ModuleFilePermissionEditorView.swift @@ -46,8 +46,9 @@ struct ModuleFilePermissionEditorView: View { } } .background(Color.backgroundLightest) - .navigationTitle(Text("Edit Permissions", bundle: .core)) + .navigationBarTitleView(String(localized: "Edit Permissions", bundle: .core)) .navigationBarItems(leading: cancelNavButton) + .navigationBarStyle(.modal) } private var form: some View { diff --git a/Core/Core/Features/Pages/PageEditor/PageEditorView.swift b/Core/Core/Features/Pages/PageEditor/PageEditorView.swift index bcb2af7d49..e8d15a8cfe 100644 --- a/Core/Core/Features/Pages/PageEditor/PageEditorView.swift +++ b/Core/Core/Features/Pages/PageEditor/PageEditorView.swift @@ -48,7 +48,11 @@ public struct PageEditorView: View { public var body: some View { form - .navigationBarTitle(url == nil ? Text("New Page", bundle: .core) : Text("Edit Page", bundle: .core), displayMode: .inline) + .navigationBarTitleView(url == nil + ? String(localized: "New Page", bundle: .core) + : String(localized: "Edit Page", bundle: .core) + ) + .navigationBarTitleDisplayMode(.inline) .navigationBarItems( leading: Button(action: { env.router.dismiss(controller) @@ -62,6 +66,7 @@ public struct PageEditorView: View { .disabled(isLoading || isSaving) .identifier("PageEditor.doneButton") ) + .navigationBarStyle(.modal) .alert(isPresented: $showError) { Alert(title: Text(error!.localizedDescription)) diff --git a/Core/Core/Features/Planner/CalendarEvent/View/EditCalendarEventScreen.swift b/Core/Core/Features/Planner/CalendarEvent/View/EditCalendarEventScreen.swift index 85193aab87..36f841df76 100644 --- a/Core/Core/Features/Planner/CalendarEvent/View/EditCalendarEventScreen.swift +++ b/Core/Core/Features/Planner/CalendarEvent/View/EditCalendarEventScreen.swift @@ -142,7 +142,7 @@ struct EditCalendarEventScreen: View, ScreenViewTrackable { .frame(maxWidth: geometry.size.width, minHeight: geometry.size.height) } } - .navigationTitle(viewModel.pageTitle) + .navigationBarTitleView(viewModel.pageTitle) .navBarItems( leading: InstUI.NavigationBarButton.cancel { viewModel.didTapCancel.send() @@ -160,6 +160,7 @@ struct EditCalendarEventScreen: View, ScreenViewTrackable { presenting: viewModel.editConfirmation ) ) + .navigationBarStyle(.modal) .errorAlert(isPresented: $viewModel.shouldShowSaveError, presenting: viewModel.saveErrorAlert) } } diff --git a/Core/Core/Features/Planner/CalendarEvent/View/EditCustomFrequencyScreen.swift b/Core/Core/Features/Planner/CalendarEvent/View/EditCustomFrequencyScreen.swift index de907ae926..181c5460dd 100644 --- a/Core/Core/Features/Planner/CalendarEvent/View/EditCustomFrequencyScreen.swift +++ b/Core/Core/Features/Planner/CalendarEvent/View/EditCustomFrequencyScreen.swift @@ -74,7 +74,7 @@ struct EditCustomFrequencyScreen: View, ScreenViewTrackable { } .frame(maxWidth: geometry.size.width) } - .navigationTitle(viewModel.pageTitle) + .navigationBarTitleView(viewModel.pageTitle) .navBarItems( trailing: .init( isEnabled: viewModel.isSaveButtonEnabled, @@ -85,6 +85,7 @@ struct EditCustomFrequencyScreen: View, ScreenViewTrackable { } ) ) + .navigationBarStyle(.modal) .dropDownDetailsContainer(state: $weekDayDropDownState) { WeekDaysSelectionListView(selection: $viewModel.daysOfTheWeek) } diff --git a/Core/Core/Features/Planner/CalendarEvent/View/SelectEventFrequencyScreen.swift b/Core/Core/Features/Planner/CalendarEvent/View/SelectEventFrequencyScreen.swift index 3c87a7a01f..00c54fb40f 100644 --- a/Core/Core/Features/Planner/CalendarEvent/View/SelectEventFrequencyScreen.swift +++ b/Core/Core/Features/Planner/CalendarEvent/View/SelectEventFrequencyScreen.swift @@ -46,7 +46,8 @@ struct SelectEventFrequencyScreen: View, ScreenViewTrackable { } .frame(minHeight: geometry.size.height) } - .navigationTitle(viewModel.pageTitle) + .navigationBarTitleView(viewModel.pageTitle) + .navigationBarStyle(.modal) .onDisappear { viewModel.didTapBack.send() } diff --git a/Core/Core/Features/Planner/CalendarFilter/View/CalendarFilterScreen.swift b/Core/Core/Features/Planner/CalendarFilter/View/CalendarFilterScreen.swift index 10618c5577..9987935129 100644 --- a/Core/Core/Features/Planner/CalendarFilter/View/CalendarFilterScreen.swift +++ b/Core/Core/Features/Planner/CalendarFilter/View/CalendarFilterScreen.swift @@ -40,11 +40,12 @@ public struct CalendarFilterScreen: View, ScreenViewTrackable { groupFilters } } - .navigationTitle(viewModel.pageTitle) + .navigationBarTitleView(viewModel.pageTitle) .toolbar { doneButton selectAllButton } + .navigationBarStyle(.modal) .snackBar(viewModel: viewModel.snackbarViewModel) // Without this the refreshable scroll view won't trigger the refresh // because the pull gesture is swallowed by the modal dialog dimiss gesture. diff --git a/Core/Core/Features/Planner/CalendarSelector/View/SelectCalendarScreen.swift b/Core/Core/Features/Planner/CalendarSelector/View/SelectCalendarScreen.swift index 0f1364ed7b..651d58efc0 100644 --- a/Core/Core/Features/Planner/CalendarSelector/View/SelectCalendarScreen.swift +++ b/Core/Core/Features/Planner/CalendarSelector/View/SelectCalendarScreen.swift @@ -41,7 +41,8 @@ struct SelectCalendarScreen: View { } } } - .navigationTitle(viewModel.pageTitle) + .navigationBarTitleView(viewModel.pageTitle) + .navigationBarStyle(.modal) } } diff --git a/Core/Core/Features/Planner/CalendarToDo/View/CalendarToDoDetailsScreen.swift b/Core/Core/Features/Planner/CalendarToDo/View/CalendarToDoDetailsScreen.swift index e4447fb8eb..0f63625b94 100644 --- a/Core/Core/Features/Planner/CalendarToDo/View/CalendarToDoDetailsScreen.swift +++ b/Core/Core/Features/Planner/CalendarToDo/View/CalendarToDoDetailsScreen.swift @@ -30,7 +30,7 @@ public struct CalendarToDoDetailsScreen: View { InstUI.BaseScreen(state: viewModel.state, config: viewModel.screenConfig) { _ in eventContent } - .navigationTitle(viewModel.navigationTitle) + .navigationBarTitleView(viewModel.navigationTitle) .navBarItems( trailing: .moreIcon( isBackgroundContextColor: true, diff --git a/Core/Core/Features/Planner/CalendarToDo/View/EditCalendarToDoScreen.swift b/Core/Core/Features/Planner/CalendarToDo/View/EditCalendarToDoScreen.swift index e5d7d061ea..0b47d17a08 100644 --- a/Core/Core/Features/Planner/CalendarToDo/View/EditCalendarToDoScreen.swift +++ b/Core/Core/Features/Planner/CalendarToDo/View/EditCalendarToDoScreen.swift @@ -87,7 +87,7 @@ struct EditCalendarToDoScreen: View, ScreenViewTrackable { } .frame(maxWidth: geometry.size.width, minHeight: geometry.size.height) } - .navigationTitle(viewModel.pageTitle) + .navigationBarTitleView(viewModel.pageTitle) .navBarItems( leading: .cancel { viewModel.didTapCancel.send() @@ -101,6 +101,7 @@ struct EditCalendarToDoScreen: View, ScreenViewTrackable { } ) ) + .navigationBarStyle(.modal) .errorAlert(isPresented: $viewModel.shouldShowSaveError, presenting: viewModel.saveErrorAlert) } } diff --git a/Core/Core/Features/Quizzes/QuizDetails/Teacher/View/TeacherQuizEditorView.swift b/Core/Core/Features/Quizzes/QuizDetails/Teacher/View/TeacherQuizEditorView.swift index f36aec35dc..cfb69fe249 100644 --- a/Core/Core/Features/Quizzes/QuizDetails/Teacher/View/TeacherQuizEditorView.swift +++ b/Core/Core/Features/Quizzes/QuizDetails/Teacher/View/TeacherQuizEditorView.swift @@ -36,7 +36,8 @@ public struct TeacherQuizEditorView: View public var body: some View { form - .navigationBarTitle(Text("Edit Quiz Details", bundle: .core), displayMode: .inline) + .navigationBarTitleView(String(localized: "Edit Quiz Details", bundle: .core)) + .navigationBarTitleDisplayMode(.inline) .navBarItems(leading: cancelButton, trailing: { Button(action: doneTapped, label: { Text("Done", bundle: .core) @@ -44,6 +45,7 @@ public struct TeacherQuizEditorView: View }) .disabled(viewModel.state != .ready) }) + .navigationBarStyle(.modal) .alert(item: $alert) { alert in switch alert { case .error(let error): diff --git a/Core/Core/Features/Quizzes/QuizPreview/View/QuizPreviewView.swift b/Core/Core/Features/Quizzes/QuizPreview/View/QuizPreviewView.swift index 95af70dd08..b8c0a8ce63 100644 --- a/Core/Core/Features/Quizzes/QuizPreview/View/QuizPreviewView.swift +++ b/Core/Core/Features/Quizzes/QuizPreview/View/QuizPreviewView.swift @@ -54,7 +54,8 @@ public struct QuizPreviewView: View { .animation(.default, value: quizHTMLLoaded) } } - .navigationTitle(viewModel.navigationTitle) + .navigationBarTitleView(viewModel.navigationTitle) + .navigationBarStyle(.modal) } private var loadingIndicator: some View { diff --git a/Core/Core/Features/Syllabus/SyllabusEditorView.swift b/Core/Core/Features/Syllabus/SyllabusEditorView.swift index 3b1fad9de2..049bdcc5fd 100644 --- a/Core/Core/Features/Syllabus/SyllabusEditorView.swift +++ b/Core/Core/Features/Syllabus/SyllabusEditorView.swift @@ -45,7 +45,8 @@ public struct SyllabusEditorView: View { public var body: some View { form - .navigationBarTitle(Text("Edit Syllabus", bundle: .core), displayMode: .inline) + .navigationBarTitleView(String(localized: "Edit Syllabus", bundle: .core)) + .navigationBarTitleDisplayMode(.inline) .navigationBarItems( leading: Button(action: { env.router.dismiss(controller) @@ -60,6 +61,7 @@ public struct SyllabusEditorView: View { }) .disabled(isLoading || isSaving) ) + .navigationBarStyle(.modal) .alert(isPresented: $showError) { Alert(title: Text(error!.localizedDescription)) diff --git a/Core/Core/Features/WebSitePreview/View/WebSitePreviewView.swift b/Core/Core/Features/WebSitePreview/View/WebSitePreviewView.swift index 1dd8ae0478..fb41200712 100644 --- a/Core/Core/Features/WebSitePreview/View/WebSitePreviewView.swift +++ b/Core/Core/Features/WebSitePreview/View/WebSitePreviewView.swift @@ -31,7 +31,8 @@ public struct WebSitePreviewView: View { headersSection launchButton } - .navigationTitle("WebSite Preview") + .navigationBarTitleView("WebSite Preview") + .navigationBarStyle(.modal) .onAppear { viewModel.viewController = controller } diff --git a/Core/Core/Features/WebSitePreview/ViewModel/WebSitePreviewViewModel.swift b/Core/Core/Features/WebSitePreview/ViewModel/WebSitePreviewViewModel.swift index 8e646c139b..b1425ee50d 100644 --- a/Core/Core/Features/WebSitePreview/ViewModel/WebSitePreviewViewModel.swift +++ b/Core/Core/Features/WebSitePreview/ViewModel/WebSitePreviewViewModel.swift @@ -73,7 +73,8 @@ class WebSitePreviewViewModel: ObservableObject { AppEnvironment.shared.router.show( CoreHostingController( WebView(request: request) - .navigationTitle("WebSite Preview") + .navigationBarTitleView("WebSite Preview") + .navigationBarStyle(.modal) ), from: viewController ) } diff --git a/Student/Student/Assignments/AssignmentDetails/Reminders/View/AssignmentReminderDatePickerView.swift b/Student/Student/Assignments/AssignmentDetails/Reminders/View/AssignmentReminderDatePickerView.swift index a45abcfeb3..3082cb0981 100644 --- a/Student/Student/Assignments/AssignmentDetails/Reminders/View/AssignmentReminderDatePickerView.swift +++ b/Student/Student/Assignments/AssignmentDetails/Reminders/View/AssignmentReminderDatePickerView.swift @@ -43,8 +43,9 @@ public struct AssignmentReminderDatePickerView: View { .animation(.default, value: viewModel.customPickerVisible) } .background(Color.backgroundLightest) - .navigationTitle(Text("Reminder", bundle: .student)) + .navigationBarTitleView(String(localized: "Reminder", bundle: .student)) .navBarItems(leading: cancelButton, trailing: doneButton) + .navigationBarStyle(.modal) } @ViewBuilder From 46f6048fd495a4de85aa6321c46cdccfd36c3944 Mon Sep 17 00:00:00 2001 From: Richard Harangozo Date: Mon, 10 Feb 2025 15:05:29 +0100 Subject: [PATCH 08/13] Remove custom navigationTitle(), replace usages with navigationBarTitleView() --- .../View/EmbeddedWebPageContainerScreen.swift | 2 +- .../UINavigationControllerTheme.swift | 26 ------------------- .../AssignmentDetailsView.swift | 7 +++-- .../AllCourses/View/AllCoursesView.swift | 2 +- .../View/CourseDetailsView.swift | 4 +-- .../View/CourseSettingsView.swift | 6 ++++- .../K5/View/Subject/K5SubjectView.swift | 2 +- .../Features/Grades/View/GradeListView.swift | 6 ++++- .../ContextCard/Course/ContextCardView.swift | 6 ++++- .../Group/GroupContextCardView.swift | 6 ++++- .../View/CalendarEventDetailsScreen.swift | 2 +- .../Teacher/View/TeacherQuizDetailsView.swift | 4 +-- .../StudentAnnotationSubmissionView.swift | 4 +-- .../View/QuizSubmissionListView.swift | 3 ++- 14 files changed, 37 insertions(+), 43 deletions(-) diff --git a/Core/Core/Common/CommonUI/EmbeddedWebPage/View/EmbeddedWebPageContainerScreen.swift b/Core/Core/Common/CommonUI/EmbeddedWebPage/View/EmbeddedWebPageContainerScreen.swift index 7784ccc28e..42ee7d2d80 100644 --- a/Core/Core/Common/CommonUI/EmbeddedWebPage/View/EmbeddedWebPageContainerScreen.swift +++ b/Core/Core/Common/CommonUI/EmbeddedWebPage/View/EmbeddedWebPageContainerScreen.swift @@ -55,7 +55,7 @@ public struct EmbeddedWebPageContainerScreen: View { viewModel.viewController = viewController.value } } - .navigationTitle(viewModel.navTitle, subtitle: viewModel.subTitle) + .navigationBarTitleView(title: viewModel.navTitle, subtitle: viewModel.subTitle) .navigationBarStyle(.color(viewModel.contextColor)) } } diff --git a/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/UINavigationControllerTheme.swift b/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/UINavigationControllerTheme.swift index 5d6ef413d3..85d16da1ee 100644 --- a/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/UINavigationControllerTheme.swift +++ b/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/UINavigationControllerTheme.swift @@ -36,28 +36,6 @@ struct NavigationBarStyleModifier: ViewModifier { } } -struct TitleSubtitleModifier: ViewModifier { - let title: String - let subtitle: String? - - @Environment(\.viewController) var controller - - func body(content: Content) -> some View { - let view = controller.value.navigationItem.titleView as? TitleSubtitleView ?? { - let view = TitleSubtitleView.create() - controller.value.navigationItem.titleView = view - return view - }() - view.title = title - view.subtitle = subtitle - var combinedTitle = title - if let subtitle = subtitle, subtitle != "" { - combinedTitle += ", \(subtitle)" - } - return content.navigationBarTitle(Text(combinedTitle)) - } -} - struct RightBarButtonItemModifier: ViewModifier { let barButtonItems: () -> [UIBarButtonItemWithCompletion] @@ -97,10 +75,6 @@ extension View { modifier(NavigationBarStyleModifier(style: style)) } - public func navigationTitle(_ title: String, subtitle: String?) -> some View { - modifier(TitleSubtitleModifier(title: title, subtitle: subtitle)) - } - public func navigationBarTitleView(title: String, subtitle: String?) -> some View { toolbar { ToolbarItem(placement: .principal) { diff --git a/Core/Core/Features/Assignments/AssignmentDetails/AssignmentDetailsView.swift b/Core/Core/Features/Assignments/AssignmentDetails/AssignmentDetailsView.swift index 2170f136b0..ef0ab12b7a 100644 --- a/Core/Core/Features/Assignments/AssignmentDetails/AssignmentDetailsView.swift +++ b/Core/Core/Features/Assignments/AssignmentDetails/AssignmentDetailsView.swift @@ -48,9 +48,12 @@ public struct AssignmentDetailsView: View, ScreenViewTrackable { public var body: some View { states .background(Color.backgroundLightest) - .navigationBarStyle(.color(course.first?.color)) - .navigationTitle(String(localized: "Assignment Details", bundle: .core), subtitle: course.first?.name) + .navigationBarTitleView( + title: String(localized: "Assignment Details", bundle: .core), + subtitle: course.first?.name + ) .rightBarButtonItems(editButton) + .navigationBarStyle(.color(course.first?.color)) .onAppear { refreshAssignments() refreshCourses() diff --git a/Core/Core/Features/Courses/AllCourses/View/AllCoursesView.swift b/Core/Core/Features/Courses/AllCourses/View/AllCoursesView.swift index 105a5c26be..27628b8ce8 100644 --- a/Core/Core/Features/Courses/AllCourses/View/AllCoursesView.swift +++ b/Core/Core/Features/Courses/AllCourses/View/AllCoursesView.swift @@ -50,8 +50,8 @@ public struct AllCoursesView: View, ScreenViewTrackable { } } .background(Color.backgroundLightest.edgesIgnoringSafeArea(.all)) + .navigationBarTitleView(String(localized: "All Courses", bundle: .core)) .navigationBarStyle(.global) - .navigationTitle(String(localized: "All Courses", bundle: .core), subtitle: nil) } @ViewBuilder diff --git a/Core/Core/Features/Courses/CourseDetails/View/CourseDetailsView.swift b/Core/Core/Features/Courses/CourseDetails/View/CourseDetailsView.swift index 37c088f38f..919882f076 100644 --- a/Core/Core/Features/Courses/CourseDetails/View/CourseDetailsView.swift +++ b/Core/Core/Features/Courses/CourseDetails/View/CourseDetailsView.swift @@ -53,10 +53,10 @@ public struct CourseDetailsView: View, ScreenViewTrackable { } } .background(Color.backgroundLightest.edgesIgnoringSafeArea(.all)) - .navigationBarStyle(.color(viewModel.courseColor)) - .navigationTitle(viewModel.navigationBarTitle, subtitle: nil) + .navigationBarTitleView(viewModel.navigationBarTitle) .navigationBarGenericBackButton() .navigationBarItems(trailing: viewModel.showSettings ? settingsButton : nil) + .navigationBarStyle(.color(viewModel.courseColor)) .onAppear { viewModel.viewDidAppear() viewModel.splitModeObserver.splitViewController = controller.value.splitViewController diff --git a/Core/Core/Features/Courses/CourseDetails/View/CourseSettingsView.swift b/Core/Core/Features/Courses/CourseDetails/View/CourseSettingsView.swift index 922fc20b0b..5e4fb51c4f 100644 --- a/Core/Core/Features/Courses/CourseDetails/View/CourseSettingsView.swift +++ b/Core/Core/Features/Courses/CourseDetails/View/CourseSettingsView.swift @@ -44,7 +44,10 @@ public struct CourseSettingsView: View, ScreenViewTrackable { editor(width: geometry.size.width) } } - .navigationTitle(String(localized: "Customize Course", bundle: .core), subtitle: viewModel.courseName) + .navigationBarTitleView( + title: String(localized: "Customize Course", bundle: .core), + subtitle: viewModel.courseName + ) .navBarItems( leading: { Button(action: cancelTapped) { @@ -58,6 +61,7 @@ public struct CourseSettingsView: View, ScreenViewTrackable { .disabled(viewModel.state != .ready) } ) + .navigationBarStyle(.modal) .onAppear(perform: viewModel.viewDidAppear) .alert(isPresented: $viewModel.showError) { Alert(title: Text(viewModel.errorText ?? String(localized: "Something went wrong", bundle: .core))) diff --git a/Core/Core/Features/Courses/K5/View/Subject/K5SubjectView.swift b/Core/Core/Features/Courses/K5/View/Subject/K5SubjectView.swift index 49ee473aec..0e76e4607e 100644 --- a/Core/Core/Features/Courses/K5/View/Subject/K5SubjectView.swift +++ b/Core/Core/Features/Courses/K5/View/Subject/K5SubjectView.swift @@ -51,8 +51,8 @@ public struct K5SubjectView: View, ScreenViewTrackable { Divider() } } + .navigationBarTitleView(viewModel.courseTitle ?? "") .navigationBarStyle(.color(viewModel.courseColor)) - .navigationTitle(viewModel.courseTitle ?? "", subtitle: nil) } public init(context: Context, selectedTabId: String? = nil) { diff --git a/Core/Core/Features/Grades/View/GradeListView.swift b/Core/Core/Features/Grades/View/GradeListView.swift index ff9d2d56db..36718237da 100644 --- a/Core/Core/Features/Grades/View/GradeListView.swift +++ b/Core/Core/Features/Grades/View/GradeListView.swift @@ -85,7 +85,10 @@ public struct GradeListView: View, ScreenViewTrackable { } } .background(Color.backgroundLightest) - .navigationTitle(String(localized: "Grades", bundle: .core), subtitle: viewModel.courseName) + .navigationBarTitleView( + title: String(localized: "Grades", bundle: .core), + subtitle: viewModel.courseName + ) .toolbar { RevertWhatIfScoreButton(isWhatIfScoreModeOn: viewModel.isWhatIfScoreModeOn) { viewModel.isShowingRevertDialog = true @@ -94,6 +97,7 @@ public struct GradeListView: View, ScreenViewTrackable { filterButton } } + .navigationBarStyle(.color(nil)) .confirmationAlert( isPresented: $viewModel.isShowingRevertDialog, presenting: viewModel.confirmRevertAlertViewModel diff --git a/Core/Core/Features/People/ContextCard/Course/ContextCardView.swift b/Core/Core/Features/People/ContextCard/Course/ContextCardView.swift index 1eb2a4c975..a84614a6d1 100644 --- a/Core/Core/Features/People/ContextCard/Course/ContextCardView.swift +++ b/Core/Core/Features/People/ContextCard/Course/ContextCardView.swift @@ -29,8 +29,12 @@ public struct ContextCardView: View { public var body: some View { contextCard .background(Color.backgroundLightest) + .navigationBarTitleView( + title: model.user.first?.name ?? "", + subtitle: model.course.first?.name + ) .navigationBarItems(trailing: emailButton) - .navigationTitle(model.user.first?.name ?? "", subtitle: model.course.first?.name ?? "") + .navigationBarStyle(model.isModal ? .modal : .color(nil)) .onAppear { model.viewAppeared() } diff --git a/Core/Core/Features/People/ContextCard/Group/GroupContextCardView.swift b/Core/Core/Features/People/ContextCard/Group/GroupContextCardView.swift index e098c7cf7a..c4f1f6843a 100644 --- a/Core/Core/Features/People/ContextCard/Group/GroupContextCardView.swift +++ b/Core/Core/Features/People/ContextCard/Group/GroupContextCardView.swift @@ -28,8 +28,12 @@ public struct GroupContextCardView: View { public var body: some View { contextCard + .navigationBarTitleView( + title: model.user.first?.name ?? "", + subtitle: model.group.first?.name + ) .navigationBarItems(trailing: emailButton) - .navigationTitle(model.user.first?.name ?? "", subtitle: model.group.first?.name) + .navigationBarStyle(.color(nil)) .onAppear { model.viewAppeared() } diff --git a/Core/Core/Features/Planner/CalendarEvent/View/CalendarEventDetailsScreen.swift b/Core/Core/Features/Planner/CalendarEvent/View/CalendarEventDetailsScreen.swift index 5c80e9e32c..b5c67d0b1d 100644 --- a/Core/Core/Features/Planner/CalendarEvent/View/CalendarEventDetailsScreen.swift +++ b/Core/Core/Features/Planner/CalendarEvent/View/CalendarEventDetailsScreen.swift @@ -35,7 +35,7 @@ public struct CalendarEventDetailsScreen: View, ScreenViewTrackable { ) { _ in eventContent } - .navigationTitle(viewModel.pageTitle, subtitle: viewModel.pageSubtitle) + .navigationBarTitleView(title: viewModel.pageTitle, subtitle: viewModel.pageSubtitle) .navBarItems( trailing: viewModel.shouldShowMenuButton ? InstUI.NavigationBarButton.moreIcon( diff --git a/Core/Core/Features/Quizzes/QuizDetails/Teacher/View/TeacherQuizDetailsView.swift b/Core/Core/Features/Quizzes/QuizDetails/Teacher/View/TeacherQuizDetailsView.swift index 798dae6b56..5046960e6c 100644 --- a/Core/Core/Features/Quizzes/QuizDetails/Teacher/View/TeacherQuizDetailsView.swift +++ b/Core/Core/Features/Quizzes/QuizDetails/Teacher/View/TeacherQuizDetailsView.swift @@ -31,8 +31,7 @@ public struct TeacherQuizDetailsView: Vi public var body: some View { states .background(Color.backgroundLightest) - .navigationBarStyle(.color(viewModel.courseColor)) - .navigationTitle(viewModel.title, subtitle: viewModel.subtitle) + .navigationBarTitleView(title: viewModel.title, subtitle: viewModel.subtitle) .rightBarButtonItems { [ UIBarButtonItemWithCompletion( @@ -43,6 +42,7 @@ public struct TeacherQuizDetailsView: Vi ) ] } + .navigationBarStyle(.color(viewModel.courseColor)) .onAppear { viewModel.viewDidAppear() } diff --git a/Student/Student/Submissions/StudentAnnotationSubmission/StudentAnnotationSubmissionView.swift b/Student/Student/Submissions/StudentAnnotationSubmission/StudentAnnotationSubmissionView.swift index d5c24ff790..4d2108e74c 100644 --- a/Student/Student/Submissions/StudentAnnotationSubmission/StudentAnnotationSubmissionView.swift +++ b/Student/Student/Submissions/StudentAnnotationSubmission/StudentAnnotationSubmissionView.swift @@ -29,9 +29,9 @@ public struct StudentAnnotationSubmissionView: View { public var body: some View { DocViewer(filename: "", previewURL: viewModel.documentURL, fallbackURL: viewModel.documentURL) - .navigationBarStyle(.color(viewModel.navBar.color)) - .navigationTitle(viewModel.navBar.title, subtitle: viewModel.navBar.subtitle) + .navigationBarTitleView(title: viewModel.navBar.title, subtitle: viewModel.navBar.subtitle) .navBarItems(leading: closeButton, trailing: doneButton) + .navigationBarStyle(.color(viewModel.navBar.color)) .onReceive(viewModel.dismissView) { viewcontroller.value.dismiss(animated: true) } diff --git a/Teacher/Teacher/Submissions/QuizSubmissionList/View/QuizSubmissionListView.swift b/Teacher/Teacher/Submissions/QuizSubmissionList/View/QuizSubmissionListView.swift index d06acde230..447fffa388 100644 --- a/Teacher/Teacher/Submissions/QuizSubmissionList/View/QuizSubmissionListView.swift +++ b/Teacher/Teacher/Submissions/QuizSubmissionList/View/QuizSubmissionListView.swift @@ -65,8 +65,9 @@ public struct QuizSubmissionListView: View, ScreenViewTrackable { } } .background(Color.backgroundLightest) - .navigationTitle(model.title, subtitle: model.subTitle) + .navigationBarTitleView(title: model.title, subtitle: model.subTitle) .navigationBarItems(trailing: messageUsersButton) + .navigationBarStyle(.color(nil)) .alert(isPresented: $model.showError) { Alert(title: Text("Practice quizzes & surveys do not have detail views.", bundle: .teacher)) } From f5c9d3f4767c2870775b431a0659018536650980 Mon Sep 17 00:00:00 2001 From: Richard Harangozo Date: Mon, 10 Feb 2025 15:39:33 +0100 Subject: [PATCH 09/13] Cleanup refs: MBL-18460 affects: Student, Teacher, Parent release note: none --- .../UINavigationControllerTheme.swift | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/UINavigationControllerTheme.swift b/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/UINavigationControllerTheme.swift index 85d16da1ee..8f50b67fb3 100644 --- a/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/UINavigationControllerTheme.swift +++ b/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/UINavigationControllerTheme.swift @@ -67,14 +67,23 @@ struct NavBarBackButtonModifier: ViewModifier { } extension View { - /// Sets the UINavigationBar's background color, title color and font, button color and font. - /// Only affects Views inside a UINavigationController. - /// Title style does not affect custom titleViews, like `TitleSubtitleView`. - /// Button style does not affect custom buttons, like `InstUI.NavigationBarButton`. + /// Sets the navigation bar's background color, title color & font, button color & font. + /// - Warning: Make sure to call this method AFTER calling `navigationBarTitleView()` to affect it. + /// - Parameters: + /// - style: + /// - `.global` is used only on a few screens, typically on root screens of each tab. + /// - `.modal` is primarily used on modal screens, but also on some screen which doesn't belong to a context, but not considered global. + /// - `.color()` is used on non-modal screens within a context (typically a course or group), and in some other cases. + /// - Use `.color(nil)` to keep the navigation bar's current context background color but ensure the proper title color is set. public func navigationBarStyle(_ style: UINavigationBar.Style) -> some View { modifier(NavigationBarStyleModifier(style: style)) } + /// Sets the navigation bar's title and subtitle, using the proper fonts and arrangement. + /// - Warning: Make sure to call `navigationBarStyle()` _**AFTER**_ this method to set the proper text colors. + /// - Parameters: + /// - title: The line is always displayed, even if this is empty. (This should not happen normally.) + /// - subtitle: The subtitle line is only displayed if this is not empty. public func navigationBarTitleView(title: String, subtitle: String?) -> some View { toolbar { ToolbarItem(placement: .principal) { @@ -83,13 +92,14 @@ extension View { } } + /// Sets the navigation bar's title, using the proper font. Please use this one instead of the native `navigationTitle()` method. + /// - Warning: Make sure to call `navigationBarStyle()` _**AFTER**_ this method to set the proper text color. public func navigationBarTitleView(_ title: String) -> some View { navigationBarTitleView(title: title, subtitle: nil) } - /// Sets the UINavigationBar's background color, button color to match the `Brand.shared` colors, + /// Sets the navigation bar's background color, button color to match the `Brand.shared` colors, /// sets the button font and sets the brand logo as the titleView. - /// Only affects Views inside a UINavigationController. public func navigationBarGlobal() -> some View { modifier(GlobalNavigationBarModifier()) } From 712efeb080ace8251a2d8a3a3f37ae5e69f5efb4 Mon Sep 17 00:00:00 2001 From: Richard Harangozo Date: Mon, 10 Feb 2025 17:58:43 +0100 Subject: [PATCH 10/13] Fix SmartSearch removing titleView in CorseDetails --- .../Search/View/CoreSearchHostingController.swift | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Core/Core/Features/Search/View/CoreSearchHostingController.swift b/Core/Core/Features/Search/View/CoreSearchHostingController.swift index be199087e5..a6091934a3 100644 --- a/Core/Core/Features/Search/View/CoreSearchHostingController.swift +++ b/Core/Core/Features/Search/View/CoreSearchHostingController.swift @@ -42,6 +42,7 @@ public class CoreSearchHostingController< private(set) var selectedFilter: ViewsProvider.Filter? private var leftBarButtonItemsToRestore: [UIBarButtonItem]? + private var titleViewToRestore: UIView? private var searchFieldState: SearchFieldState = .removed private var subscriptions: Set = [] @@ -148,6 +149,7 @@ public class CoreSearchHostingController< searchInteractor .isEnabled + .removeDuplicates() .receive(on: DispatchQueue.main) .sink { [weak self] isEnabled in self?.setupOrRemoveSearchBar(isEnabled) @@ -157,6 +159,11 @@ public class CoreSearchHostingController< private func setupOrRemoveSearchBar(_ isSearchEnabled: Bool) { if isSearchEnabled == (searchFieldState != .removed) { return } + + if titleViewToRestore == nil && navigationItem.titleView !== searchFieldView { + titleViewToRestore = navigationItem.titleView + } + if isSearchEnabled { hideSearchBarAndShowSearchButton() } else { @@ -191,9 +198,9 @@ public class CoreSearchHostingController< } private func hideSearchBarAndShowSearchButton() { - navigationItem.titleView = nil navigationItem.hidesBackButton = false navigationItem.leftBarButtonItems = leftBarButtonItemsToRestore + navigationItem.titleView = titleViewToRestore navigationItem.rightBarButtonItems = [searchBarItem] searchFieldState = .hidden @@ -201,9 +208,9 @@ public class CoreSearchHostingController< } private func removeSearchBarAndButton() { - navigationItem.titleView = nil navigationItem.hidesBackButton = false navigationItem.leftBarButtonItems = leftBarButtonItemsToRestore + navigationItem.titleView = titleViewToRestore navigationItem.rightBarButtonItems = nil searchFieldState = .removed } From 7872de4e1da4ef8fc6918bbdaaccee1139dc259e Mon Sep 17 00:00:00 2001 From: Richard Harangozo Date: Mon, 10 Feb 2025 19:30:25 +0100 Subject: [PATCH 11/13] Move related files together, add tests --- .../NavigationBarColorConfiguration.swift | 2 +- .../NavigationBar/NavigationBarStyle.swift | 25 +++++++ .../SwiftUI/NavigationBarViewModifiers.swift} | 6 +- .../UIKit}/ColoredNavViewProtocol.swift | 0 .../UIKit}/TitleSubtitleView.swift | 0 .../UIKit}/TitleSubtitleView.xib | 0 .../UIKit/UINavigationBarExtensions.swift | 6 +- .../CoreHostingController.swift | 2 +- ...NavigationBarColorConfigurationTests.swift | 65 +++++++++++++++++++ .../UIKit}/TitleSubtitleViewTests.swift | 0 .../UINavigationBarExtensionsTests.swift | 0 11 files changed, 96 insertions(+), 10 deletions(-) rename Core/Core/Common/CommonUI/{SwiftUIViews/UIKitSwiftUIBridging => NavigationBar}/NavigationBarColorConfiguration.swift (96%) create mode 100644 Core/Core/Common/CommonUI/NavigationBar/NavigationBarStyle.swift rename Core/Core/Common/CommonUI/{SwiftUIViews/UIKitSwiftUIBridging/UINavigationControllerTheme.swift => NavigationBar/SwiftUI/NavigationBarViewModifiers.swift} (96%) rename Core/Core/Common/CommonUI/{Presenter => NavigationBar/UIKit}/ColoredNavViewProtocol.swift (100%) rename Core/Core/Common/CommonUI/{UIViews => NavigationBar/UIKit}/TitleSubtitleView.swift (100%) rename Core/Core/Common/CommonUI/{UIViews => NavigationBar/UIKit}/TitleSubtitleView.xib (100%) rename Core/Core/Common/{Extensions => CommonUI/NavigationBar}/UIKit/UINavigationBarExtensions.swift (97%) create mode 100644 Core/CoreTests/Common/CommonUI/NavigationBar/NavigationBarColorConfigurationTests.swift rename Core/CoreTests/Common/CommonUI/{UIViews => NavigationBar/UIKit}/TitleSubtitleViewTests.swift (100%) rename Core/CoreTests/Common/{Extensions => CommonUI/NavigationBar}/UIKit/UINavigationBarExtensionsTests.swift (100%) diff --git a/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/NavigationBarColorConfiguration.swift b/Core/Core/Common/CommonUI/NavigationBar/NavigationBarColorConfiguration.swift similarity index 96% rename from Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/NavigationBarColorConfiguration.swift rename to Core/Core/Common/CommonUI/NavigationBar/NavigationBarColorConfiguration.swift index 0086a6e7ab..23fb9d3cb8 100644 --- a/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/NavigationBarColorConfiguration.swift +++ b/Core/Core/Common/CommonUI/NavigationBar/NavigationBarColorConfiguration.swift @@ -30,7 +30,7 @@ struct NavigationBarColorConfiguration { self.tint = tint } - init(style: UINavigationBar.Style, brand: Brand = .shared) { + init(style: NavigationBarStyle, brand: Brand = .shared) { switch style { case .modal: self.init( diff --git a/Core/Core/Common/CommonUI/NavigationBar/NavigationBarStyle.swift b/Core/Core/Common/CommonUI/NavigationBar/NavigationBarStyle.swift new file mode 100644 index 0000000000..b444e0dc02 --- /dev/null +++ b/Core/Core/Common/CommonUI/NavigationBar/NavigationBarStyle.swift @@ -0,0 +1,25 @@ +// +// This file is part of Canvas. +// Copyright (C) 2025-present Instructure, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// + +import UIKit + +public enum NavigationBarStyle: Equatable { + case modal + case global + case color(UIColor?) +} diff --git a/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/UINavigationControllerTheme.swift b/Core/Core/Common/CommonUI/NavigationBar/SwiftUI/NavigationBarViewModifiers.swift similarity index 96% rename from Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/UINavigationControllerTheme.swift rename to Core/Core/Common/CommonUI/NavigationBar/SwiftUI/NavigationBarViewModifiers.swift index 8f50b67fb3..77a420aec4 100644 --- a/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/UINavigationControllerTheme.swift +++ b/Core/Core/Common/CommonUI/NavigationBar/SwiftUI/NavigationBarViewModifiers.swift @@ -19,11 +19,11 @@ import SwiftUI protocol NavigationBarStyled: AnyObject { - var navigationBarStyle: UINavigationBar.Style { get set } + var navigationBarStyle: NavigationBarStyle { get set } } struct NavigationBarStyleModifier: ViewModifier { - let style: UINavigationBar.Style + let style: NavigationBarStyle @Environment(\.viewController) var controller @@ -75,7 +75,7 @@ extension View { /// - `.modal` is primarily used on modal screens, but also on some screen which doesn't belong to a context, but not considered global. /// - `.color()` is used on non-modal screens within a context (typically a course or group), and in some other cases. /// - Use `.color(nil)` to keep the navigation bar's current context background color but ensure the proper title color is set. - public func navigationBarStyle(_ style: UINavigationBar.Style) -> some View { + public func navigationBarStyle(_ style: NavigationBarStyle) -> some View { modifier(NavigationBarStyleModifier(style: style)) } diff --git a/Core/Core/Common/CommonUI/Presenter/ColoredNavViewProtocol.swift b/Core/Core/Common/CommonUI/NavigationBar/UIKit/ColoredNavViewProtocol.swift similarity index 100% rename from Core/Core/Common/CommonUI/Presenter/ColoredNavViewProtocol.swift rename to Core/Core/Common/CommonUI/NavigationBar/UIKit/ColoredNavViewProtocol.swift diff --git a/Core/Core/Common/CommonUI/UIViews/TitleSubtitleView.swift b/Core/Core/Common/CommonUI/NavigationBar/UIKit/TitleSubtitleView.swift similarity index 100% rename from Core/Core/Common/CommonUI/UIViews/TitleSubtitleView.swift rename to Core/Core/Common/CommonUI/NavigationBar/UIKit/TitleSubtitleView.swift diff --git a/Core/Core/Common/CommonUI/UIViews/TitleSubtitleView.xib b/Core/Core/Common/CommonUI/NavigationBar/UIKit/TitleSubtitleView.xib similarity index 100% rename from Core/Core/Common/CommonUI/UIViews/TitleSubtitleView.xib rename to Core/Core/Common/CommonUI/NavigationBar/UIKit/TitleSubtitleView.xib diff --git a/Core/Core/Common/Extensions/UIKit/UINavigationBarExtensions.swift b/Core/Core/Common/CommonUI/NavigationBar/UIKit/UINavigationBarExtensions.swift similarity index 97% rename from Core/Core/Common/Extensions/UIKit/UINavigationBarExtensions.swift rename to Core/Core/Common/CommonUI/NavigationBar/UIKit/UINavigationBarExtensions.swift index f712fb82b5..ec3b22213d 100644 --- a/Core/Core/Common/Extensions/UIKit/UINavigationBarExtensions.swift +++ b/Core/Core/Common/CommonUI/NavigationBar/UIKit/UINavigationBarExtensions.swift @@ -20,11 +20,7 @@ import UIKit extension UINavigationBar { - public enum Style: Equatable { - case modal - case global - case color(UIColor?) - } + public typealias Style = NavigationBarStyle func useStyle(_ style: Style) { switch style { diff --git a/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/CoreHostingController.swift b/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/CoreHostingController.swift index 39c28e5828..289071eb8c 100644 --- a/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/CoreHostingController.swift +++ b/Core/Core/Common/CommonUI/SwiftUIViews/UIKitSwiftUIBridging/CoreHostingController.swift @@ -51,7 +51,7 @@ public class CoreHostingController: UIHostingController UIStatusBarStyle)? // MARK: - Public Properties - public var navigationBarStyle = UINavigationBar.Style.color(nil) // not applied until changed + public var navigationBarStyle = NavigationBarStyle.color(nil) // not applied until changed public var defaultViewRoute: DefaultViewRouteParameters? { didSet { showDefaultDetailViewIfNeeded() diff --git a/Core/CoreTests/Common/CommonUI/NavigationBar/NavigationBarColorConfigurationTests.swift b/Core/CoreTests/Common/CommonUI/NavigationBar/NavigationBarColorConfigurationTests.swift new file mode 100644 index 0000000000..b03b64e14f --- /dev/null +++ b/Core/CoreTests/Common/CommonUI/NavigationBar/NavigationBarColorConfigurationTests.swift @@ -0,0 +1,65 @@ +// +// This file is part of Canvas. +// Copyright (C) 2025-present Instructure, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// + +import XCTest +import SwiftUI +@testable import Core + +class NavigationBarColorConfigurationTests: XCTestCase { + + let brand = Brand( + response: .make( + button_primary_bgd: "#FFFF00", + link_color: "#FF0000", + nav_text_color: "#0000FF" + ), + headerImage: nil + ) + + func test_colors_whenStyleIsModal() { + let testee = NavigationBarColorConfiguration(style: .modal, brand: brand) + + XCTAssertEqual(testee.title.hexString, Color.textDarkest.hexString) + XCTAssertEqual(testee.subtitle.hexString, Color.textDark.hexString) + XCTAssertEqual(testee.tint.hexString, brand.linkColor.hexString) + } + + func test_colors_whenStyleIsGlobal() { + let testee = NavigationBarColorConfiguration(style: .global, brand: brand) + + XCTAssertEqual(testee.title.hexString, brand.navTextColor.hexString) + XCTAssertEqual(testee.subtitle.hexString, brand.navTextColor.hexString) + XCTAssertEqual(testee.tint.hexString, brand.navTextColor.hexString) + } + + func test_colors_whenStyleIsColor() { + let testee = NavigationBarColorConfiguration(style: .color(.purple), brand: brand) + + XCTAssertEqual(testee.title.hexString, Color.textLightest.hexString) + XCTAssertEqual(testee.subtitle.hexString, Color.textLightest.hexString) + XCTAssertEqual(testee.tint.hexString, Color.textLightest.hexString) + } + + func test_colors_whenStyleIsColorWithNil() { + let testee = NavigationBarColorConfiguration(style: .color(nil), brand: brand) + + XCTAssertEqual(testee.title.hexString, Color.textLightest.hexString) + XCTAssertEqual(testee.subtitle.hexString, Color.textLightest.hexString) + XCTAssertEqual(testee.tint.hexString, Color.textLightest.hexString) + } +} diff --git a/Core/CoreTests/Common/CommonUI/UIViews/TitleSubtitleViewTests.swift b/Core/CoreTests/Common/CommonUI/NavigationBar/UIKit/TitleSubtitleViewTests.swift similarity index 100% rename from Core/CoreTests/Common/CommonUI/UIViews/TitleSubtitleViewTests.swift rename to Core/CoreTests/Common/CommonUI/NavigationBar/UIKit/TitleSubtitleViewTests.swift diff --git a/Core/CoreTests/Common/Extensions/UIKit/UINavigationBarExtensionsTests.swift b/Core/CoreTests/Common/CommonUI/NavigationBar/UIKit/UINavigationBarExtensionsTests.swift similarity index 100% rename from Core/CoreTests/Common/Extensions/UIKit/UINavigationBarExtensionsTests.swift rename to Core/CoreTests/Common/CommonUI/NavigationBar/UIKit/UINavigationBarExtensionsTests.swift From 38ea15385d7fdcc526c057d5eb973a4324f06618 Mon Sep 17 00:00:00 2001 From: Richard Harangozo Date: Mon, 10 Feb 2025 21:30:01 +0100 Subject: [PATCH 12/13] Increase subtitle font size to 14 --- .../Common/CommonUI/InstUI/Views/NavigationBarTitleView.swift | 2 +- .../Common/CommonUI/NavigationBar/UIKit/TitleSubtitleView.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/Core/Common/CommonUI/InstUI/Views/NavigationBarTitleView.swift b/Core/Core/Common/CommonUI/InstUI/Views/NavigationBarTitleView.swift index 29cd3422cc..397d08d56e 100644 --- a/Core/Core/Common/CommonUI/InstUI/Views/NavigationBarTitleView.swift +++ b/Core/Core/Common/CommonUI/InstUI/Views/NavigationBarTitleView.swift @@ -43,7 +43,7 @@ extension InstUI { if let subtitle, subtitle.isNotEmpty { Text(subtitle) - .font(.regular12) + .font(.regular14) .foregroundColor(navBarColors.subtitle) } } diff --git a/Core/Core/Common/CommonUI/NavigationBar/UIKit/TitleSubtitleView.swift b/Core/Core/Common/CommonUI/NavigationBar/UIKit/TitleSubtitleView.swift index 4cf17a888c..52975bb297 100644 --- a/Core/Core/Common/CommonUI/NavigationBar/UIKit/TitleSubtitleView.swift +++ b/Core/Core/Common/CommonUI/NavigationBar/UIKit/TitleSubtitleView.swift @@ -49,7 +49,7 @@ public class TitleSubtitleView: UIView { view.titleLabel.text = "" view.subtitleLabel.text = "" view.titleLabel.font = .scaledNamedFont(.semibold16) - view.subtitleLabel.font = .scaledNamedFont(.regular12) + view.subtitleLabel.font = .scaledNamedFont(.regular14) view.titleLabel.accessibilityElementsHidden = true view.subtitleLabel.accessibilityElementsHidden = true view.accessibilityTraits = [.header] From bc5726cd29e717c61deef1633de564cb2185f3bf Mon Sep 17 00:00:00 2001 From: Richard Harangozo Date: Tue, 11 Feb 2025 09:43:54 +0100 Subject: [PATCH 13/13] Fix swiftlint issue --- .../CommonUI/NavigationBar/NavigationBarColorConfiguration.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Core/Core/Common/CommonUI/NavigationBar/NavigationBarColorConfiguration.swift b/Core/Core/Common/CommonUI/NavigationBar/NavigationBarColorConfiguration.swift index 23fb9d3cb8..dd789ad0c4 100644 --- a/Core/Core/Common/CommonUI/NavigationBar/NavigationBarColorConfiguration.swift +++ b/Core/Core/Common/CommonUI/NavigationBar/NavigationBarColorConfiguration.swift @@ -47,4 +47,3 @@ struct NavigationBarColorConfiguration { } } } -