diff --git a/Authorization/Authorization/Presentation/Login/SignInView.swift b/Authorization/Authorization/Presentation/Login/SignInView.swift index fd98fde7c..3bdab24ca 100644 --- a/Authorization/Authorization/Presentation/Login/SignInView.swift +++ b/Authorization/Authorization/Presentation/Login/SignInView.swift @@ -13,6 +13,8 @@ public struct SignInView: View { @State private var email: String = "" @State private var password: String = "" + @Environment (\.isHorizontal) private var isHorizontal + @ObservedObject private var viewModel: SignInViewModel @@ -32,7 +34,8 @@ public struct SignInView: View { CoreAssets.appLogo.swiftUIImage .resizable() .frame(maxWidth: 189, maxHeight: 54) - .padding(.vertical, 40) + .padding(.top, isHorizontal ? 20 : 40) + .padding(.bottom, isHorizontal ? 10 : 40) ScrollView { VStack { @@ -152,6 +155,7 @@ public struct SignInView: View { .hideNavigationBar() .navigationBarBackButtonHidden(true) .navigationBarHidden(true) + .ignoresSafeArea(.all, edges: .horizontal) .background(Theme.Colors.background.ignoresSafeArea(.all)) } } diff --git a/Authorization/Authorization/Presentation/Registration/SignUpView.swift b/Authorization/Authorization/Presentation/Registration/SignUpView.swift index 2ce5f263c..d4dc67acf 100644 --- a/Authorization/Authorization/Presentation/Registration/SignUpView.swift +++ b/Authorization/Authorization/Presentation/Registration/SignUpView.swift @@ -13,6 +13,8 @@ public struct SignUpView: View { @State private var disclosureGroupOpen: Bool = false + @Environment (\.isHorizontal) private var isHorizontal + @ObservedObject private var viewModel: SignUpViewModel @@ -44,6 +46,7 @@ public struct SignUpView: View { .backButtonStyle(color: .white) }) .foregroundColor(Theme.Colors.styledButtonText) + .padding(.leading, isHorizontal ? 48 : 0) }.frame(minWidth: 0, maxWidth: .infinity, @@ -135,6 +138,7 @@ public struct SignUpView: View { } } } + .ignoresSafeArea(.all, edges: .horizontal) .background(Theme.Colors.background.ignoresSafeArea(.all)) .hideNavigationBar() } diff --git a/Authorization/Authorization/Presentation/Reset Password/ResetPasswordView.swift b/Authorization/Authorization/Presentation/Reset Password/ResetPasswordView.swift index 17f7466c0..ef4d1c7fb 100644 --- a/Authorization/Authorization/Presentation/Reset Password/ResetPasswordView.swift +++ b/Authorization/Authorization/Presentation/Reset Password/ResetPasswordView.swift @@ -14,6 +14,8 @@ public struct ResetPasswordView: View { @State private var isRecovered: Bool = false + @Environment (\.isHorizontal) private var isHorizontal + @ObservedObject private var viewModel: ResetPasswordViewModel @@ -35,7 +37,7 @@ public struct ResetPasswordView: View { leftButtonColor: .white, leftButtonAction: { viewModel.router.back() - }) + }).padding(.leading, isHorizontal ? 48 : 0) ScrollView { VStack { @@ -149,7 +151,10 @@ public struct ResetPasswordView: View { } } } + .ignoresSafeArea(.all, edges: .horizontal) + .background(Theme.Colors.background.ignoresSafeArea(.all)) + .hideNavigationBar() } } diff --git a/Core/Core/Extensions/UIApplicationExtension.swift b/Core/Core/Extensions/UIApplicationExtension.swift index 616b9f466..9c6f88c6f 100644 --- a/Core/Core/Extensions/UIApplicationExtension.swift +++ b/Core/Core/Extensions/UIApplicationExtension.swift @@ -58,6 +58,10 @@ extension UINavigationController: UIGestureRecognizerDelegate { } public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { - return viewControllers.count > 1 + if #available(iOS 17, *) { + return false + } else { + return viewControllers.count > 1 + } } } diff --git a/Core/Core/Extensions/ViewExtension.swift b/Core/Core/Extensions/ViewExtension.swift index d6584cbcf..91a66ed92 100644 --- a/Core/Core/Extensions/ViewExtension.swift +++ b/Core/Core/Extensions/ViewExtension.swift @@ -89,11 +89,59 @@ public extension View { .padding(.horizontal, 48) } + @ViewBuilder func frameLimit(sizePortrait: CGFloat = 560, sizeLandscape: CGFloat = 648) -> some View { - return HStack { - Spacer(minLength: 0) - self.frame(maxWidth: UIDevice.current.orientation == .portrait ? sizePortrait : sizeLandscape) - Spacer(minLength: 0) + if UIDevice.current.userInterfaceIdiom == .pad { + HStack { + Spacer(minLength: 0) + self.frame(maxWidth: UIDevice.current.orientation.isPortrait ? sizePortrait : sizeLandscape) + Spacer(minLength: 0) + } + } else { self } + } + + @ViewBuilder + func adaptiveHStack( + spacing: CGFloat = 0, + currentOrientation: UIInterfaceOrientation, + @ViewBuilder content: () -> Content + ) -> some View { + if currentOrientation.isLandscape && UIDevice.current.userInterfaceIdiom != .pad { + VStack(alignment: .center, spacing: spacing, content: content) + } else if currentOrientation.isPortrait && UIDevice.current.userInterfaceIdiom != .pad { + HStack(spacing: spacing, content: content) + } else if UIDevice.current.userInterfaceIdiom != .phone { + HStack(spacing: spacing, content: content) + } + } + + @ViewBuilder + func adaptiveStack( + spacing: CGFloat = 0, + isHorizontal: Bool, + @ViewBuilder content: () -> Content + ) -> some View { + if isHorizontal, UIDevice.current.userInterfaceIdiom != .pad { + HStack(spacing: spacing, content: content) + } else { + VStack(alignment: .center, spacing: spacing, content: content) + } + } + + @ViewBuilder + func adaptiveNavigationStack( + spacing: CGFloat = 0, + isHorizontal: Bool, + @ViewBuilder content: () -> Content + ) -> some View { + if UIDevice.current.userInterfaceIdiom == .pad { + HStack(spacing: spacing, content: content) + } else { + if isHorizontal { + HStack(alignment: .top, spacing: spacing, content: content) + } else { + VStack(alignment: .center, spacing: spacing, content: content) + } } } @@ -118,6 +166,26 @@ public extension View { } } + func roundedBackgroundWeb( + _ color: Color = Theme.Colors.background, + strokeColor: Color = Theme.Colors.backgroundStroke, + ipadMaxHeight: CGFloat = .infinity, + maxIpadWidth: CGFloat = 420 + ) -> some View { + var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom } + return VStack { + VStack {}.frame(height: 1) + ZStack { + self + .frame(maxWidth: maxIpadWidth, maxHeight: idiom == .pad ? ipadMaxHeight : .infinity) + RoundedCorners(tl: 24, tr: 24) + .stroke(style: StrokeStyle(lineWidth: 1)) + .foregroundColor(strokeColor) + .offset(y: -1) + } + } + } + func hideNavigationBar() -> some View { if #available(iOS 16.0, *) { return self.navigationBarHidden(true) @@ -199,3 +267,14 @@ public extension Image { .foregroundColor(color) } } + +public extension EnvironmentValues { + var isHorizontal: Bool { + if UIDevice.current.userInterfaceIdiom != .pad { + if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene { + return windowScene.windows.first?.windowScene?.interfaceOrientation.isLandscape ?? true + } + } + return false + } +} diff --git a/Core/Core/View/Base/AlertView.swift b/Core/Core/View/Base/AlertView.swift index f230eba9b..15afa8774 100644 --- a/Core/Core/View/Base/AlertView.swift +++ b/Core/Core/View/Base/AlertView.swift @@ -33,6 +33,8 @@ public struct AlertView: View { private var nextSectionTapped: (() -> Void) = {} private let type: AlertViewType + @Environment(\.isHorizontal) private var isHorizontal + public init( alertTitle: String, alertMessage: String, @@ -68,44 +70,82 @@ public struct AlertView: View { } public var body: some View { - GeometryReader { reader in - ZStack(alignment: .center) { - Color.black.opacity(0.5) - .onTapGesture { - onCloseTapped() - } - VStack(alignment: .center, spacing: 20) { - if case let .action(_, image) = type { - image.padding(.top, 48) - } + ZStack(alignment: .center) { + Color.black.opacity(0.5) + .onTapGesture { + onCloseTapped() + } + ZStack(alignment: .topTrailing) { + adaptiveStack(spacing: isHorizontal ? 10 : 20, isHorizontal: (type == .leaveProfile && isHorizontal)) { if type == .logOut { - CoreAssets.logOut.swiftUIImage - .padding(.top, 54) + HStack { + Spacer(minLength: 100) + CoreAssets.logOut.swiftUIImage + .padding(.top, isHorizontal ? 20 : 54) + Spacer(minLength: 100) + } Text(alertMessage) .font(Theme.Fonts.titleLarge) - .padding(.vertical, 40) + .padding(.vertical, isHorizontal ? 6 : 40) .multilineTextAlignment(.center) .padding(.horizontal, 40) .frame(maxWidth: 250) } else if type == .leaveProfile { - CoreAssets.leaveProfile.swiftUIImage - .padding(.top, 54) - Text(alertTitle) - .font(Theme.Fonts.titleLarge) - .padding(.horizontal, 40) - Text(alertMessage) - .font(Theme.Fonts.bodyMedium) - .multilineTextAlignment(.center) - .padding(.horizontal, 40) + VStack(spacing: 20) { + CoreAssets.leaveProfile.swiftUIImage + .padding(.top, isHorizontal ? 20 : 54) + Text(alertTitle) + .font(Theme.Fonts.titleLarge) + .padding(.horizontal, 40) + Text(alertMessage) + .font(Theme.Fonts.bodyMedium) + .multilineTextAlignment(.center) + .padding(.horizontal, 40) + + }.padding(.bottom, 20) } else { - Text(alertTitle) - .font(Theme.Fonts.titleLarge) - .padding(.horizontal, 40) - Text(alertMessage) - .font(Theme.Fonts.bodyMedium) - .multilineTextAlignment(.center) - .padding(.horizontal, 40) - .frame(maxWidth: 250) + HStack { + VStack(alignment: .center, spacing: 10) { + if case let .action(_, image) = type { + image.padding(.top, 48) + } + Text(alertTitle) + .font(Theme.Fonts.titleLarge) + .padding(.horizontal, 40) + Text(alertMessage) + .font(Theme.Fonts.bodyMedium) + .multilineTextAlignment(.center) + .padding(.horizontal, 40) + .frame(maxWidth: 250) + } + if isHorizontal { + if case let .action(action, _) = type { + VStack(spacing: 20) { + if nextSectionName != nil { + UnitButtonView(type: .nextSection, action: { nextSectionTapped() }) + .frame(maxWidth: 215) + } + UnitButtonView(type: .custom(action), + bgColor: .clear, + action: { okTapped() }) + .frame(maxWidth: 215) + + if let nextSectionName { + Group { + Text(CoreLocalization.Courseware.nextSectionDescriptionFirst) + + Text(nextSectionName) + + Text(CoreLocalization.Courseware.nextSectionDescriptionLast) + }.frame(maxWidth: 215) + .padding(.horizontal, 40) + .multilineTextAlignment(.center) + .font(Theme.Fonts.labelSmall) + .foregroundColor(CoreAssets.textSecondary.swiftUIColor) + } + }.padding(.top, 70) + .padding(.trailing, 20) + } + } + } } HStack { switch type { @@ -116,28 +156,31 @@ public struct AlertView: View { .frame(maxWidth: 135) .saturation(0) case let .action(action, _): - VStack(spacing: 20) { - if nextSectionName != nil { - UnitButtonView(type: .nextSection, action: { nextSectionTapped() }) - .frame(maxWidth: 215) - } - UnitButtonView(type: .custom(action), - bgColor: .clear, - action: { okTapped() }) + if !isHorizontal { + VStack(spacing: 20) { + if nextSectionName != nil { + UnitButtonView(type: .nextSection, action: { nextSectionTapped() }) + .frame(maxWidth: 215) + } + UnitButtonView(type: .custom(action), + bgColor: .clear, + action: { okTapped() }) .frame(maxWidth: 215) - - if let nextSectionName { - Group { - Text(CoreLocalization.Courseware.nextSectionDescriptionFirst) + - Text(nextSectionName) + - Text(CoreLocalization.Courseware.nextSectionDescriptionLast) - }.frame(maxWidth: 215) - .padding(.horizontal, 40) - .multilineTextAlignment(.center) - .font(Theme.Fonts.labelSmall) - .foregroundColor(Theme.Colors.textSecondary) + + if let nextSectionName { + Group { + Text(CoreLocalization.Courseware.nextSectionDescriptionFirst) + + Text(nextSectionName) + + Text(CoreLocalization.Courseware.nextSectionDescriptionLast) + }.frame(maxWidth: 215) + .padding(.horizontal, 40) + .multilineTextAlignment(.center) + .font(Theme.Fonts.labelSmall) + .foregroundColor(Theme.Colors.textSecondary) + } } - + } else { + EmptyView() } case .logOut: Button(action: { @@ -199,7 +242,7 @@ public struct AlertView: View { .foregroundColor(.clear) ) .frame(maxWidth: 215) - .padding(.bottom, 24) + .padding(.bottom, isHorizontal ? 10 : 24) Button(action: { onCloseTapped() }, label: { @@ -227,33 +270,37 @@ public struct AlertView: View { .foregroundColor(Theme.Colors.textPrimary) ) .frame(maxWidth: 215) - } + }.padding(.trailing, isHorizontal ? 20 : 0) } } .padding(.top, 5) - .padding(.bottom, type.contentPadding) + .padding(.bottom, isHorizontal ? 16 : type.contentPadding) } - .background( - Theme.Shapes.cardShape - .fill(Theme.Colors.cardViewBackground) - .shadow(radius: 24) - .frame(width: reader.size.width < 420 - ? reader.size.width - 80 - : 360) - ) - .overlay( - RoundedRectangle(cornerRadius: 12) - .stroke(style: .init(lineWidth: 1, lineCap: .round, lineJoin: .round, miterLimit: 1)) - .foregroundColor(Theme.Colors.backgroundStroke) - .frame(width: reader.size.width < 420 - ? reader.size.width - 80 - : 360) - ) - .padding() - } - - .ignoresSafeArea() + Button(action: { + onCloseTapped() + }, label: { + Image(systemName: "xmark") + .padding(.trailing, 40) + .padding(.top, 24) + }) + + }.frame(maxWidth: type == .logOut ? 390 : nil) + .background( + Theme.Shapes.cardShape + .fill(Theme.Colors.cardViewBackground) + .shadow(radius: 24) + .fixedSize(horizontal: false, vertical: false) + ) + .overlay( + RoundedRectangle(cornerRadius: 12) + .stroke(style: .init(lineWidth: 1, lineCap: .round, lineJoin: .round, miterLimit: 1)) + .foregroundColor(Theme.Colors.backgroundStroke) + .fixedSize(horizontal: false, vertical: false) + ) + .frame(maxWidth: isHorizontal ? nil : 390) + .padding(40) } + .ignoresSafeArea() } } @@ -261,9 +308,9 @@ public struct AlertView: View { struct AlertView_Previews: PreviewProvider { static var previews: some View { AlertView( - alertTitle: "Warning", - alertMessage: "Something goes wrong. Do you want to exterminate your phone, right now", - nextSectionName: "Ahmad tea is a power", + alertTitle: "Congratulations!", + alertMessage: "You've passed the course", + nextSectionName: "Continue", mainAction: "Back to outline", image: CoreAssets.goodWork.swiftUIImage, onCloseTapped: {}, @@ -272,6 +319,23 @@ struct AlertView_Previews: PreviewProvider { ) .previewLayout(.sizeThatFits) .background(Color.gray) + + AlertView(alertTitle: "Comfirm log out", + alertMessage: "Are you sure you want to log out?", + positiveAction: "Yes", + onCloseTapped: {}, + okTapped: {}, + type: .logOut) + + AlertView(alertTitle: "Leave profile?", + alertMessage: "Changes you have made not be saved.", + positiveAction: "Yes", + onCloseTapped: {}, + okTapped: {}, + type: .leaveProfile) + + .previewLayout(.sizeThatFits) + .background(Color.gray) } } //swiftlint:enable all diff --git a/Core/Core/View/Base/FlexibleKeyboardInputView.swift b/Core/Core/View/Base/FlexibleKeyboardInputView.swift index e8d6d0d8a..8747a260f 100644 --- a/Core/Core/View/Base/FlexibleKeyboardInputView.swift +++ b/Core/Core/View/Base/FlexibleKeyboardInputView.swift @@ -11,6 +11,7 @@ public struct FlexibleKeyboardInputView: View { @State private var commentText: String = "" @State private var commentSize: CGFloat = .init(64) + @Environment (\.isHorizontal) private var isHorizontal public var sendText: ((String) -> Void) private let hint: String @@ -24,87 +25,82 @@ public struct FlexibleKeyboardInputView: View { public var body: some View { VStack { - Spacer() - VStack(alignment: .leading) { - - ScrollView { - HStack(alignment: .top, spacing: 6) { - Text("\(commentText) ").foregroundColor(.clear).padding(8) - .lineLimit(3) - .frame(maxWidth: .infinity) - .background( - GeometryReader { reader in - Color.clear.preference( - key: ViewSizePreferenceKey.self, - value: reader.size - ) + VStack { + Spacer() + VStack(alignment: .leading) { + + ScrollView { + HStack(alignment: .top, spacing: 6) { + Text("\(commentText) ").foregroundColor(.clear).padding(8) + .lineLimit(3) + .frame(maxWidth: .infinity) + .background( + GeometryReader { reader in + Color.clear.preference( + key: ViewSizePreferenceKey.self, + value: reader.size + ) + } + ) + .onPreferenceChange(ViewSizePreferenceKey.self) { size in + commentSize = size.height } - ) - .onPreferenceChange(ViewSizePreferenceKey.self) { size in - commentSize = size.height - } - .overlay( - TextEditor(text: $commentText) - .padding(.horizontal, 8) - .foregroundColor(Theme.Colors.textPrimary) - .hideScrollContentBackground() - .frame(maxHeight: commentSize) - .background( - ZStack(alignment: .leading) { + .overlay( + TextEditor(text: $commentText) + .padding(.horizontal, 8) + .foregroundColor(Theme.Colors.textPrimary) + .hideScrollContentBackground() + .frame(maxHeight: commentSize) + .background( + ZStack(alignment: .leading) { + Theme.Shapes.textInputShape + .fill(Theme.Colors.textInputBackground) + Text(commentText.count == 0 ? hint : "") + .foregroundColor(Theme.Colors.textSecondary) + .font(Theme.Fonts.labelLarge) + .padding(.leading, 14) + } + ) + .overlay( Theme.Shapes.textInputShape - .fill(Theme.Colors.textInputBackground) - Text(commentText.count == 0 ? hint : "") - .foregroundColor(Theme.Colors.textSecondary) - .font(Theme.Fonts.labelLarge) - .padding(.leading, 14) - } - ) - .overlay( - Theme.Shapes.textInputShape - .stroke(lineWidth: 1) - .fill( - Theme.Colors.textInputStroke - ) - ) - ).padding(8) - Button(action: { - if commentText.trimmingCharacters(in: .whitespacesAndNewlines).count > 0 { - sendText(commentText) - self.commentText = "" - } - }, label: { - VStack { + .stroke(lineWidth: 1) + .fill( + Theme.Colors.textInputStroke + ) + ) + ).padding(8) + Button(action: { if commentText.trimmingCharacters(in: .whitespacesAndNewlines).count > 0 { - ZStack { - Circle() - .frame(width: 36, height: 36) - .foregroundColor(.accentColor) - CoreAssets.send.swiftUIImage - .offset(y: 1) - } - } else { - CoreAssets.sendDisabled.swiftUIImage + sendText(commentText) + self.commentText = "" } - } - .frame(width: 36, height: 36) - .foregroundColor(.white) - }).padding(.top, 8) - } - } - .padding(.leading, 6) - .padding(.trailing, 14) - }.frame(maxWidth: .infinity, maxHeight: commentSize + 16) - .background( - Theme.Colors.commentCellBackground - .ignoresSafeArea() - ) - .overlay( - GeometryReader { proxy in - Rectangle() - .size(width: proxy.size.width, height: 1) - .foregroundColor(Theme.Colors.cardViewStroke) + }, label: { + VStack { + commentText.trimmingCharacters(in: .whitespacesAndNewlines).count > 0 + ? CoreAssets.send.swiftUIImage + : CoreAssets.sendDisabled.swiftUIImage + } + .frame(width: 36, height: 36) + .foregroundColor(.white) + }).padding(.top, 8) + + }.padding(.horizontal, isHorizontal ? 50 : 16) + } - ) + .padding(.leading, 6) + .padding(.trailing, 14) + }.frame(maxWidth: .infinity, maxHeight: commentSize + 16) + .background( + Theme.Colors.commentCellBackground + ) + .overlay( + GeometryReader { proxy in + Rectangle() + .size(width: proxy.size.width, height: 1) + .foregroundColor(Theme.Colors.cardViewStroke) + } + ) + } } } } diff --git a/Core/Core/View/Base/PickerMenu.swift b/Core/Core/View/Base/PickerMenu.swift index a967ffdde..09271213c 100644 --- a/Core/Core/View/Base/PickerMenu.swift +++ b/Core/Core/View/Base/PickerMenu.swift @@ -25,6 +25,7 @@ public struct PickerMenu: View { @State private var search: String = "" @State public var selectedItem: PickerItem = PickerItem(key: "", value: "") + @Environment (\.isHorizontal) private var isHorizontal private let ipadPickerWidth: CGFloat = 300 private var items: [PickerItem] private let titleText: String @@ -90,7 +91,11 @@ public struct PickerMenu: View { } .pickerStyle(.wheel) } - .frame(minWidth: 0, maxWidth: idiom == .pad ? ipadPickerWidth : .infinity) + .frame(minWidth: 0, + maxWidth: (idiom == .pad || (idiom == .phone && isHorizontal)) + ? ipadPickerWidth + : .infinity) + .padding() .background(Theme.Colors.textInputBackground.cornerRadius(16)) .padding(.horizontal, 16) @@ -106,13 +111,17 @@ public struct PickerMenu: View { }) { Text(CoreLocalization.Picker.accept) .foregroundColor(Theme.Colors.textPrimary) - .frame(minWidth: 0, maxWidth: idiom == .pad ? ipadPickerWidth : .infinity) + .frame(minWidth: 0, + maxWidth: (idiom == .pad || (idiom == .phone && isHorizontal)) + ? ipadPickerWidth + : .infinity) .padding() .background(Theme.Colors.textInputBackground.cornerRadius(16)) .padding(.horizontal, 16) } .padding(.bottom, 4) .disabled(acceptButtonDisabled) + } .avoidKeyboard(dismissKeyboardByTap: true) .transition(.move(edge: .bottom)) diff --git a/Core/Core/View/Base/UnitButtonView.swift b/Core/Core/View/Base/UnitButtonView.swift index 67a49d0da..e6d658c49 100644 --- a/Core/Core/View/Base/UnitButtonView.swift +++ b/Core/Core/View/Base/UnitButtonView.swift @@ -99,12 +99,13 @@ public struct UnitButtonView: View { HStack { Text(type.stringValue()) .foregroundColor(Theme.Colors.styledButtonText) - .padding(.leading, 16) + .padding(.leading, 8) .font(Theme.Fonts.labelLarge) + .scaledToFit() Spacer() CoreAssets.check.swiftUIImage.renderingMode(.template) .foregroundColor(Theme.Colors.styledButtonText) - .padding(.trailing, 16) + .padding(.trailing, 8) } case .finish: HStack { diff --git a/Course/Course/Presentation/Details/CourseDetailsView.swift b/Course/Course/Presentation/Details/CourseDetailsView.swift index 168dd8de1..2b846d526 100644 --- a/Course/Course/Presentation/Details/CourseDetailsView.swift +++ b/Course/Course/Presentation/Details/CourseDetailsView.swift @@ -14,6 +14,7 @@ public struct CourseDetailsView: View { @ObservedObject private var viewModel: CourseDetailsViewModel @Environment(\.colorScheme) var colorScheme + @Environment(\.isHorizontal) var isHorizontal @State private var isOverviewRendering = true private var title: String private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom } @@ -54,7 +55,7 @@ public struct CourseDetailsView: View { if let courseDetails = viewModel.courseDetails { // MARK: - iPad - if idiom == .pad && viewModel.isHorisontal { + if viewModel.isHorisontal { HStack(alignment: .top) { VStack(alignment: .leading) { @@ -303,7 +304,7 @@ private struct CourseBannerView: View { .onFailureImage(CoreAssets.noCourseImage.image) .resizable() .aspectRatio(16/9, contentMode: .fill) - .frame(width: idiom == .pad ? 312 : proxy.size.width - 12) + .frame(width: 312) .opacity(animate ? 1 : 0) .onAppear { withAnimation(.linear(duration: 0.5)) { diff --git a/Course/Course/Presentation/Unit/CourseNavigationView.swift b/Course/Course/Presentation/Unit/CourseNavigationView.swift index ba9d34a5a..505d865e0 100644 --- a/Course/Course/Presentation/Unit/CourseNavigationView.swift +++ b/Course/Course/Presentation/Unit/CourseNavigationView.swift @@ -59,7 +59,7 @@ struct CourseNavigationView: View { if viewModel.verticals.count > viewModel.verticalIndex + 1 { return viewModel.verticals[viewModel.verticalIndex + 1].displayName } else if sequentials.count > viewModel.sequentialIndex + 1 { - return sequentials[viewModel.sequentialIndex + 1].childs.first?.displayName + return sequentials[viewModel.sequentialIndex + 1].childs.first?.displayName ?? "" } else if chapters.count > viewModel.chapterIndex + 1 { return chapters[viewModel.chapterIndex + 1].childs.first?.childs.first?.displayName } else { @@ -72,7 +72,7 @@ struct CourseNavigationView: View { okTapped: { playerStateSubject.send(VideoPlayerState.pause) playerStateSubject.send(VideoPlayerState.kill) - + viewModel.trackFinishVerticalBackToOutlineClicked() viewModel.router.dismiss(animated: false) viewModel.router.back(animated: true) @@ -122,6 +122,7 @@ struct CourseNavigationView: View { sequentialIndex: sequentialIndex) } ) + playerStateSubject.send(VideoPlayerState.pause) viewModel.analytics.finishVerticalClicked( courseId: viewModel.courseID, courseName: viewModel.courseName, @@ -143,8 +144,7 @@ struct CourseNavigationView: View { }) } } - }.frame(minWidth: 0, maxWidth: .infinity) - .padding(.horizontal, 24) + }.padding(.horizontal, 24) } } diff --git a/Course/Course/Presentation/Unit/CourseUnitView.swift b/Course/Course/Presentation/Unit/CourseUnitView.swift index 411d8ac15..484fdda9f 100644 --- a/Course/Course/Presentation/Unit/CourseUnitView.swift +++ b/Course/Course/Presentation/Unit/CourseUnitView.swift @@ -26,7 +26,7 @@ public struct CourseUnitView: View { @State var offsetView: CGFloat = 0 @State var showDiscussion: Bool = false @Environment(\.presentationMode) private var presentationMode - + @Environment(\.isHorizontal) private var isHorizontal private let sectionName: String public let playerStateSubject = CurrentValueSubject(nil) @@ -44,67 +44,74 @@ public struct CourseUnitView: View { ZStack(alignment: .bottom) { GeometryReader { reader in VStack(spacing: 0) { - VStack {}.frame(height: 100) - LazyVStack(spacing: 0) { - let data = Array(viewModel.verticals[viewModel.verticalIndex].childs.enumerated()) - ForEach(data, id: \.offset) { index, block in - VStack(spacing: 0) { - if index >= viewModel.index - 1 && index <= viewModel.index + 1 { - switch LessonType.from(block) { - // MARK: YouTube - case let .youtube(url, blockID): - if viewModel.connectivity.isInternetAvaliable { - YouTubeView( - name: block.displayName, - url: url, - courseID: viewModel.courseID, - blockID: blockID, - playerStateSubject: playerStateSubject, - languages: block.subtitles ?? [], - isOnScreen: index == viewModel.index - ).frameLimit() - Spacer(minLength: 100) - } else { - NoInternetView(playerStateSubject: playerStateSubject) - } - // MARK: Encoded Video - case let .video(encodedUrl, blockID): - let url = viewModel.urlForVideoFileOrFallback( - blockId: blockID, - url: encodedUrl - ) - if viewModel.connectivity.isInternetAvaliable || url?.isFileURL == true { - EncodedVideoView( - name: block.displayName, - url: url, - courseID: viewModel.courseID, - blockID: blockID, - playerStateSubject: playerStateSubject, - languages: block.subtitles ?? [], - isOnScreen: index == viewModel.index - ).frameLimit() - Spacer(minLength: 100) - } else { - NoInternetView(playerStateSubject: playerStateSubject) + VStack {CoreAssets.background.swiftUIColor}.frame(width: reader.size.width, + height: isHorizontal ? 75 : 50) + LazyVStack(alignment: .leading, spacing: 0) { + let data = Array(viewModel.verticals[viewModel.verticalIndex].childs.enumerated()) + ForEach(data, id: \.offset) { index, block in + VStack(spacing: 0) { + if index >= viewModel.index - 1 && index <= viewModel.index + 1 { + switch LessonType.from(block) { + // MARK: YouTube + case let .youtube(url, blockID): + if viewModel.connectivity.isInternetAvaliable { + YouTubeView( + name: block.displayName, + url: url, + courseID: viewModel.courseID, + blockID: blockID, + playerStateSubject: playerStateSubject, + languages: block.subtitles ?? [], + isOnScreen: index == viewModel.index + ).frameLimit() + + if !isHorizontal { + Spacer(minLength: 150) } - // MARK: Web - case .web(let url): - if viewModel.connectivity.isInternetAvaliable { - WebView(url: url, viewModel: viewModel) - } else { - NoInternetView(playerStateSubject: playerStateSubject) + } else { + NoInternetView(playerStateSubject: playerStateSubject) + } + // MARK: Encoded Video + case let .video(encodedUrl, blockID): + let url = viewModel.urlForVideoFileOrFallback( + blockId: blockID, + url: encodedUrl + ) + if viewModel.connectivity.isInternetAvaliable || url?.isFileURL == true { + EncodedVideoView( + name: block.displayName, + url: url, + courseID: viewModel.courseID, + blockID: blockID, + playerStateSubject: playerStateSubject, + languages: block.subtitles ?? [], + isOnScreen: index == viewModel.index + ).frameLimit() + + if !isHorizontal { + Spacer(minLength: 150) } - // MARK: Unknown - case .unknown(let url): - if viewModel.connectivity.isInternetAvaliable { + } else { + NoInternetView(playerStateSubject: playerStateSubject) + } + // MARK: Web + case .web(let url): + if viewModel.connectivity.isInternetAvaliable { + WebView(url: url, viewModel: viewModel) + } else { + NoInternetView(playerStateSubject: playerStateSubject) + } + // MARK: Unknown + case .unknown(let url): + if viewModel.connectivity.isInternetAvaliable { UnknownView(url: url, viewModel: viewModel) Spacer() - } else { - NoInternetView(playerStateSubject: playerStateSubject) - } - // MARK: Discussion - case let .discussion(blockID, blockKey, title): - if viewModel.connectivity.isInternetAvaliable { + } else { + NoInternetView(playerStateSubject: playerStateSubject) + } + // MARK: Discussion + case let .discussion(blockID, blockKey, title): + if viewModel.connectivity.isInternetAvaliable { VStack { if showDiscussion { DiscussionView( @@ -121,40 +128,70 @@ public struct CourseUnitView: View { } } }.frameLimit() - } else { - NoInternetView(playerStateSubject: playerStateSubject) - } + } else { + NoInternetView(playerStateSubject: playerStateSubject) } - } else { - EmptyView() } + } else { + EmptyView() } - .frame(height: reader.size.height) - .id(index) } + .frame( + width: isHorizontal ? reader.size.width - 16 : reader.size.width, + height: reader.size.height + ) + .id(index) } - .offset(y: offsetView) - .clipped() - .onChange(of: viewModel.index, perform: { index in - DispatchQueue.main.async { - withAnimation(Animation.easeInOut(duration: 0.2)) { - offsetView = -(reader.size.height * CGFloat(index)) - DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { - showDiscussion = viewModel.selectedLesson().type == .discussion - } + } + .offset(y: offsetView) + .clipped() + .onAppear { + offsetView = -(reader.size.height * CGFloat(viewModel.index)) + } + .onAppear { + NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillShowNotification, + object: nil, queue: .main) { _ in + offsetView = -(reader.size.height * CGFloat(viewModel.index)) + } + NotificationCenter.default.addObserver(forName: UIResponder.keyboardDidShowNotification, + object: nil, queue: .main) { _ in + offsetView = -(reader.size.height * CGFloat(viewModel.index)) + } + NotificationCenter.default.addObserver(forName: UIResponder.keyboardDidHideNotification, + object: nil, queue: .main) { _ in + offsetView = -(reader.size.height * CGFloat(viewModel.index)) + } + } + .onChange(of: UIDevice.current.orientation, perform: { _ in + offsetView = -(reader.size.height * CGFloat(viewModel.index)) + }) + .onChange(of: viewModel.verticalIndex, perform: { index in + DispatchQueue.main.async { + withAnimation(Animation.easeInOut(duration: 0.2)) { + offsetView = -(reader.size.height * CGFloat(index)) + } + } + + }) + .onChange(of: viewModel.index, perform: { index in + DispatchQueue.main.async { + withAnimation(Animation.easeInOut(duration: 0.2)) { + offsetView = -(reader.size.height * CGFloat(index)) + DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { + showDiscussion = viewModel.selectedLesson().type == .discussion } } - - }) + } + + }) }.frame(maxWidth: .infinity) .clipped() // MARK: Progress Dots - if viewModel.verticals[viewModel.verticalIndex].childs.count > 1 { LessonProgressView(viewModel: viewModel) - } } + // MARK: - Alert if showAlert { ZStack(alignment: .bottomLeading) { @@ -179,17 +216,59 @@ public struct CourseUnitView: View { // MARK: - Course Navigation VStack { - CourseNavigationView( - sectionName: sectionName, - viewModel: viewModel, - playerStateSubject: playerStateSubject - ).padding(.bottom, 30) - .frameLimit(sizePortrait: 420) - }.frame(maxWidth: .infinity) - .onRightSwipeGesture { - playerStateSubject.send(VideoPlayerState.kill) - viewModel.router.back() + ZStack { + GeometryReader { reader in + VStack { + HStack { + let currentBlock = viewModel.verticals[viewModel.verticalIndex] + .childs[viewModel.index] + if currentBlock.type == .video { + let title = currentBlock.displayName + Text(title) + .lineLimit(1) + .font(Theme.Fonts.titleLarge) + .foregroundStyle(Theme.Colors.textPrimary) + .padding(.leading, isHorizontal ? 30 : 42) + .padding(.top, isHorizontal ? 14 : 2) + Spacer() + } + }.frame(maxWidth: isHorizontal ? reader.size.width * 0.5 : nil) + Spacer() + } + } + VStack { + NavigationBar( + title: "", + leftButtonAction: { + viewModel.router.back() + playerStateSubject.send(VideoPlayerState.kill) + }).padding(.top, isHorizontal ? 10 : 0) + .padding(.leading, isHorizontal ? -16 : 0) + Spacer() + } + HStack(alignment: .center) { + if isHorizontal { + Spacer() + } + VStack { + if !isHorizontal { + Spacer() + } + CourseNavigationView( + sectionName: sectionName, + viewModel: viewModel, + playerStateSubject: playerStateSubject + ) + if isHorizontal { + Spacer() + } + }//.frame(height: isHorizontal ? nil : 44) + + .padding(.bottom, isHorizontal ? 0 : 50) + .padding(.top, isHorizontal ? 12 : 0) + }.frameLimit(sizePortrait: 420) } + }.frame(maxWidth: .infinity) } .onDisappear { if !presentationMode.wrappedValue.isPresented { @@ -197,19 +276,23 @@ public struct CourseUnitView: View { } } } + .ignoresSafeArea(.all, edges: .bottom) + .onRightSwipeGesture { + playerStateSubject.send(VideoPlayerState.kill) + viewModel.router.back() + } .onAppear { DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { showDiscussion = viewModel.selectedLesson().type == .discussion } } - .navigationBarHidden(false) - .navigationBarBackButtonHidden(false) + .navigationBarHidden(true) + .navigationBarBackButtonHidden(true) .navigationTitle("") - .ignoresSafeArea() - .background( - Theme.Colors.background - .ignoresSafeArea() - ) + .background( + Theme.Colors.background + .ignoresSafeArea() + ) } } diff --git a/Course/Course/Presentation/Unit/Subviews/EncodedVideoView.swift b/Course/Course/Presentation/Unit/Subviews/EncodedVideoView.swift index 1bdc629fa..d790664cd 100644 --- a/Course/Course/Presentation/Unit/Subviews/EncodedVideoView.swift +++ b/Course/Course/Presentation/Unit/Subviews/EncodedVideoView.swift @@ -21,11 +21,6 @@ struct EncodedVideoView: View { let isOnScreen: Bool var body: some View { - VStack(alignment: .leading, spacing: 8) { - Text(name) - .font(Theme.Fonts.titleLarge) - .padding(.horizontal, 24) - let vm = Container.shared.resolve( EncodedVideoPlayerViewModel.self, arguments: url, @@ -35,7 +30,5 @@ struct EncodedVideoView: View { playerStateSubject )! EncodedVideoPlayer(viewModel: vm, isOnScreen: isOnScreen) - Spacer(minLength: 100) - } } } diff --git a/Course/Course/Presentation/Unit/Subviews/LessonProgressView.swift b/Course/Course/Presentation/Unit/Subviews/LessonProgressView.swift index 57a881589..da2010150 100644 --- a/Course/Course/Presentation/Unit/Subviews/LessonProgressView.swift +++ b/Course/Course/Presentation/Unit/Subviews/LessonProgressView.swift @@ -11,6 +11,8 @@ import Core struct LessonProgressView: View { @ObservedObject var viewModel: CourseUnitViewModel + @Environment (\.isHorizontal) private var isHorizontal + init(viewModel: CourseUnitViewModel) { self.viewModel = viewModel } @@ -36,7 +38,7 @@ struct LessonProgressView: View { } Spacer() } - .padding(.trailing, 6) + .padding(.trailing, isHorizontal ? 0 : 6) } } } diff --git a/Course/Course/Presentation/Unit/Subviews/WebView.swift b/Course/Course/Presentation/Unit/Subviews/WebView.swift index 9cdc59269..5f1d45388 100644 --- a/Course/Course/Presentation/Unit/Subviews/WebView.swift +++ b/Course/Course/Presentation/Unit/Subviews/WebView.swift @@ -18,6 +18,6 @@ struct WebView: View { WebUnitView(url: url, viewModel: Container.shared.resolve(WebUnitViewModel.self)!) Spacer(minLength: 5) } - .roundedBackground(strokeColor: .clear, maxIpadWidth: .infinity) + .roundedBackgroundWeb(strokeColor: Theme.Colors.textInputUnfocusedStroke, maxIpadWidth: .infinity) } } diff --git a/Course/Course/Presentation/Unit/Subviews/YouTubeView.swift b/Course/Course/Presentation/Unit/Subviews/YouTubeView.swift index 94080fc32..49f1cfb3d 100644 --- a/Course/Course/Presentation/Unit/Subviews/YouTubeView.swift +++ b/Course/Course/Presentation/Unit/Subviews/YouTubeView.swift @@ -21,12 +21,6 @@ struct YouTubeView: View { let isOnScreen: Bool var body: some View { - VStack(alignment: .leading, spacing: 8) { - VStack(alignment: .leading) { - Text(name) - .font(Theme.Fonts.titleLarge) - .padding(.horizontal, 24) - let vm = Container.shared.resolve( YouTubeVideoPlayerViewModel.self, arguments: url, @@ -36,8 +30,6 @@ struct YouTubeView: View { playerStateSubject )! YouTubeVideoPlayer(viewModel: vm, isOnScreen: isOnScreen) - Spacer(minLength: 100) - }.background(Theme.Colors.background) - } + .background(Theme.Colors.background) } } diff --git a/Course/Course/Presentation/Video/EncodedVideoPlayer.swift b/Course/Course/Presentation/Video/EncodedVideoPlayer.swift index 0afcfba01..233469250 100644 --- a/Course/Course/Presentation/Video/EncodedVideoPlayer.swift +++ b/Course/Course/Presentation/Video/EncodedVideoPlayer.swift @@ -41,6 +41,8 @@ public struct EncodedVideoPlayer: View { } } + @Environment(\.isHorizontal) private var isHorizontal + public init( viewModel: EncodedVideoPlayerViewModel, isOnScreen: Bool @@ -51,83 +53,70 @@ public struct EncodedVideoPlayer: View { public var body: some View { ZStack { - VStack(alignment: .leading) { - PlayerViewController( - videoURL: viewModel.url, - controller: viewModel.controller, - progress: { progress in - if progress >= 0.8 { - if !isViewedOnce { - Task { - await viewModel.blockCompletionRequest() - } - isViewedOnce = true + GeometryReader {reader in + VStack { + HStack { + VStack { + PlayerViewController( + videoURL: viewModel.url, + controller: viewModel.controller, + progress: { progress in + if progress >= 0.8 { + if !isViewedOnce { + Task { + await viewModel.blockCompletionRequest() + } + isViewedOnce = true + } + } + }, seconds: { seconds in + currentTime = seconds + }) + .aspectRatio(16 / 9, contentMode: .fit) + .frame(minWidth: isHorizontal ? reader.size.width * 0.6 : 380) + .cornerRadius(12) + if isHorizontal { + Spacer() } } - }, seconds: { seconds in - if !pause { - currentTime = seconds - } - }) - .aspectRatio(16 / 9, contentMode: .fit) - .cornerRadius(12) - .padding(.horizontal, 6) - .onReceive(NotificationCenter.Publisher( - center: .default, - name: UIDevice.orientationDidChangeNotification) - ) { _ in - if isOnScreen { - self.orientation = UIDevice.current.orientation - if self.orientation.isLandscape { - viewModel.controller.enterFullScreen(animated: true) - viewModel.controller.player?.play() - isOrientationChanged = true - } else { - if isOrientationChanged { - viewModel.controller.exitFullScreen(animated: true) - viewModel.controller.player?.pause() - isOrientationChanged = false - } + if isHorizontal { + SubtittlesView( + languages: viewModel.languages, + currentTime: $currentTime, + viewModel: viewModel, + scrollTo: { date in + viewModel.controller.player?.seek( + to: CMTime( + seconds: date.secondsSinceMidnight(), + preferredTimescale: 10000 + ) + ) + pauseScrolling() + currentTime = (date.secondsSinceMidnight() + 1) + }) } } - } - SubtittlesView(languages: viewModel.languages, - currentTime: $currentTime, - viewModel: viewModel, scrollTo: { date in - viewModel.controller.player?.seek(to: CMTime(seconds: date.secondsSinceMidnight(), - preferredTimescale: 10000)) - pauseScrolling() - currentTime = (date.secondsSinceMidnight() + 1) - }) - Spacer() - if !orientation.isLandscape || idiom != .pad { - VStack {}.onAppear { - isLoading = false - alertMessage = CourseLocalization.Alert.rotateDevice - } - } - } - - // MARK: - Alert - if showAlert, let alertMessage { - VStack(alignment: .center) { - Spacer() - HStack(spacing: 6) { - CoreAssets.rotateDevice.swiftUIImage.renderingMode(.template) - Text(alertMessage) - }.shadowCardStyle(bgColor: Theme.Colors.snackbarInfoAlert, - textColor: .white) - .transition(.move(edge: .bottom)) - .onAppear { - doAfter(Theme.Timeout.snackbarMessageLongTimeout) { - self.alertMessage = nil - showAlert = false - } + if !isHorizontal { + SubtittlesView( + languages: viewModel.languages, + currentTime: $currentTime, + viewModel: viewModel, + scrollTo: { date in + viewModel.controller.player?.seek( + to: CMTime( + seconds: date.secondsSinceMidnight(), + preferredTimescale: 10000 + ) + ) + pauseScrolling() + currentTime = (date.secondsSinceMidnight() + 1) + }) } } } - } + }.padding(.horizontal, isHorizontal ? 0 : 8) } + private func pauseScrolling() { pause = true DispatchQueue.main.asyncAfter(deadline: .now() + 2) { diff --git a/Course/Course/Presentation/Video/SubtittlesView.swift b/Course/Course/Presentation/Video/SubtittlesView.swift index eb3496bdc..e7dca9735 100644 --- a/Course/Course/Presentation/Video/SubtittlesView.swift +++ b/Course/Course/Presentation/Video/SubtittlesView.swift @@ -96,7 +96,7 @@ public struct SubtittlesView: View { })) } }.padding(.horizontal, 24) - .padding(.top, 34) + .padding(.top, 16) } } diff --git a/Course/Course/Presentation/Video/YouTubeVideoPlayer.swift b/Course/Course/Presentation/Video/YouTubeVideoPlayer.swift index 1afa10778..9d12b3183 100644 --- a/Course/Course/Presentation/Video/YouTubeVideoPlayer.swift +++ b/Course/Course/Presentation/Video/YouTubeVideoPlayer.swift @@ -27,79 +27,50 @@ public struct YouTubeVideoPlayer: View { } } + @Environment(\.isHorizontal) private var isHorizontal + public init(viewModel: YouTubeVideoPlayerViewModel, isOnScreen: Bool) { self._viewModel = StateObject(wrappedValue: { viewModel }()) self.isOnScreen = isOnScreen } public var body: some View { - ZStack { - VStack { - YouTubePlayerView( - viewModel.youtubePlayer, - transaction: .init(animation: .easeIn), - overlay: { _ in }) - .onAppear { - alertMessage = CourseLocalization.Alert.rotateDevice - } - .cornerRadius(12) - .padding(.horizontal, 6) - .aspectRatio(16 / 8.8, contentMode: .fit) - .onReceive(NotificationCenter.Publisher( - center: .default, name: UIDevice.orientationDidChangeNotification - )) { _ in - if isOnScreen { - let orientation = UIDevice.current.orientation - if orientation.isPortrait { - viewModel.youtubePlayer.update(configuration: YouTubePlayer.Configuration(configure: { - $0.playInline = true - $0.autoPlay = viewModel.play - $0.startTime = Int(viewModel.currentTime) - })) - } else { - viewModel.youtubePlayer.update(configuration: YouTubePlayer.Configuration(configure: { - $0.playInline = false - $0.autoPlay = true - $0.startTime = Int(viewModel.currentTime) - })) + ZStack { + GeometryReader { reader in + adaptiveStack(isHorizontal: isHorizontal) { + VStack { + YouTubePlayerView( + viewModel.youtubePlayer, + transaction: .init(animation: .easeIn), + overlay: { _ in }) + .onAppear { + alertMessage = CourseLocalization.Alert.rotateDevice + } + .cornerRadius(12) + .padding(.horizontal, isHorizontal ? 0 : 8) + .aspectRatio(16 / 8.8, contentMode: .fit) + .frame(minWidth: isHorizontal ? reader.size.width * 0.6 : 380) + // Adjust the width based on the horizontal state + if isHorizontal { + Spacer() + } } + SubtittlesView( + languages: viewModel.languages, + currentTime: $viewModel.currentTime, + viewModel: viewModel, scrollTo: { date in + viewModel.youtubePlayer.seek(to: date.secondsSinceMidnight(), allowSeekAhead: true) + viewModel.pauseScrolling() + viewModel.currentTime = date.secondsSinceMidnight() + 1 + } + ) } } - SubtittlesView( - languages: viewModel.languages, - currentTime: $viewModel.currentTime, - viewModel: viewModel, scrollTo: { date in - viewModel.youtubePlayer.seek(to: date.secondsSinceMidnight(), allowSeekAhead: true) - viewModel.pauseScrolling() - viewModel.currentTime = date.secondsSinceMidnight() + 1 - } - ) - } - - if viewModel.isLoading { - ProgressBar(size: 40, lineWidth: 8) - } - - // MARK: - Alert - if showAlert, let alertMessage { - VStack(alignment: .center) { - Spacer() - HStack(spacing: 6) { - CoreAssets.rotateDevice.swiftUIImage.renderingMode(.template) - Text(alertMessage) - }.shadowCardStyle(bgColor: Theme.Colors.snackbarInfoAlert, - textColor: .white) - .transition(.move(edge: .bottom)) - .onAppear { - doAfter(Theme.Timeout.snackbarMessageLongTimeout) { - self.alertMessage = nil - showAlert = false - } - } + if viewModel.isLoading { + ProgressBar(size: 40, lineWidth: 8) } } } - } } #if DEBUG diff --git a/Discussion/Discussion/Presentation/Comments/Responses/ResponsesView.swift b/Discussion/Discussion/Presentation/Comments/Responses/ResponsesView.swift index fe0e43058..54423455a 100644 --- a/Discussion/Discussion/Presentation/Comments/Responses/ResponsesView.swift +++ b/Discussion/Discussion/Presentation/Comments/Responses/ResponsesView.swift @@ -162,7 +162,7 @@ public struct ResponsesView: View { } } } - ) + ).ignoresSafeArea(.all, edges: .horizontal) } } } @@ -197,6 +197,7 @@ public struct ResponsesView: View { } } } + .ignoresSafeArea(.all, edges: .horizontal) .navigationBarHidden(false) .navigationBarBackButtonHidden(false) .navigationTitle(title) diff --git a/Discussion/Discussion/Presentation/Comments/Thread/ThreadView.swift b/Discussion/Discussion/Presentation/Comments/Thread/ThreadView.swift index 0ca873e41..9be26d613 100644 --- a/Discussion/Discussion/Presentation/Comments/Thread/ThreadView.swift +++ b/Discussion/Discussion/Presentation/Comments/Thread/ThreadView.swift @@ -162,7 +162,7 @@ public struct ThreadView: View { } } } - ) + ).ignoresSafeArea(.all, edges: .horizontal) } } .onReceive(viewModel.addPostSubject, perform: { newComment in @@ -217,6 +217,7 @@ public struct ThreadView: View { } } } + .ignoresSafeArea(.all, edges: .horizontal) .navigationBarHidden(false) .navigationBarBackButtonHidden(false) .navigationTitle(title) diff --git a/OpenEdX.xcodeproj/project.pbxproj b/OpenEdX.xcodeproj/project.pbxproj index 8aecb5c84..4fc81e931 100644 --- a/OpenEdX.xcodeproj/project.pbxproj +++ b/OpenEdX.xcodeproj/project.pbxproj @@ -509,8 +509,7 @@ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleLightContent; - INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -598,8 +597,7 @@ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleLightContent; - INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -693,8 +691,7 @@ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleLightContent; - INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -782,8 +779,7 @@ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleLightContent; - INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -931,8 +927,7 @@ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleLightContent; - INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -966,8 +961,7 @@ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UIStatusBarStyle = UIStatusBarStyleLightContent; - INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", diff --git a/OpenEdX.xcodeproj/xcshareddata/xcschemes/OpenEdXDev.xcscheme b/OpenEdX.xcodeproj/xcshareddata/xcschemes/OpenEdXDev.xcscheme index 3a38de2f5..c2f6ffa2b 100644 --- a/OpenEdX.xcodeproj/xcshareddata/xcschemes/OpenEdXDev.xcscheme +++ b/OpenEdX.xcodeproj/xcshareddata/xcschemes/OpenEdXDev.xcscheme @@ -103,6 +103,7 @@ buildConfiguration = "DebugDev" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "uk" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/OpenEdX/AppDelegate.swift b/OpenEdX/AppDelegate.swift index 3c3cad303..bb7c92a3b 100644 --- a/OpenEdX/AppDelegate.swift +++ b/OpenEdX/AppDelegate.swift @@ -21,9 +21,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } var window: UIWindow? - - private var orientationLock: UIInterfaceOrientationMask = .portrait - + private var assembler: Assembler? private var lastForceLogoutTime: TimeInterval = 0 @@ -54,19 +52,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return true } - - func application( - _ application: UIApplication, - supportedInterfaceOrientationsFor window: UIWindow? - ) -> UIInterfaceOrientationMask { - //Allows external windows, such as WebView Player, to work in any orientation - if window == self.window { - return UIDevice.current.userInterfaceIdiom == .phone ? orientationLock : .all - } else { - return UIDevice.current.userInterfaceIdiom == .phone ? .allButUpsideDown : .all - } - } - + private func initDI() { let navigation = UINavigationController() navigation.modalPresentationStyle = .fullScreen diff --git a/Profile/Profile/Presentation/EditProfile/EditProfileView.swift b/Profile/Profile/Presentation/EditProfile/EditProfileView.swift index f0a439d48..5e0530e48 100644 --- a/Profile/Profile/Presentation/EditProfile/EditProfileView.swift +++ b/Profile/Profile/Presentation/EditProfile/EditProfileView.swift @@ -209,7 +209,7 @@ public struct EditProfileView: View { CoreAssets.arrowLeft.swiftUIImage .renderingMode(.template) .foregroundColor(Theme.Colors.accentColor) - }).opacity(viewModel.isChanged ? 1 : 0.3) + }) }) ToolbarItem(placement: .navigationBarTrailing, content: { Button(action: { diff --git a/Profile/Profile/Presentation/EditProfile/ProfileBottomSheet.swift b/Profile/Profile/Presentation/EditProfile/ProfileBottomSheet.swift index 9a3f09330..00a6c13f6 100644 --- a/Profile/Profile/Presentation/EditProfile/ProfileBottomSheet.swift +++ b/Profile/Profile/Presentation/EditProfile/ProfileBottomSheet.swift @@ -38,6 +38,8 @@ struct ProfileBottomSheet: View { private var removePhoto: () -> Void @Binding private var showingBottomSheet: Bool + @Environment (\.isHorizontal) private var isHorizontal + init( showingBottomSheet: Binding, openGallery: @escaping () -> Void, @@ -96,7 +98,12 @@ struct ProfileBottomSheet: View { }).padding(.top, 34) }.padding(.horizontal, 24) - }.frame(maxWidth: idiom == .pad ? 330 : .infinity, maxHeight: 290, alignment: .topLeading) + } + .frame(minWidth: 0, + maxWidth: (idiom == .pad || (idiom == .phone && isHorizontal)) + ? 330 + : .infinity, + maxHeight: 290, alignment: .topLeading) .background(Theme.Colors.cardViewBackground) .cornerRadius(8) .padding(.horizontal, 22)