// /* * Copyright (c) 2025 BWI GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import SwiftUI // MARK: Migraion Info View struct MigrationInfoView: View { @Environment(\.dismiss) var dismissView @State private var selectedTab = 1 @EnvironmentObject var themeService: BWIThemeService @State var redrawKey = UUID() @State var migrationFinished = SharedKeychain.load(account: "migration_finished") == "true" let username: String let getUserName: (() -> String?)? var body: some View { TabView(selection: $selectedTab) { if migrationFinished { MigrationSuccessView() .tag(1) } else { MigrationInfoViewOne() .tag(1) if !BWIBuildSettings.shared.isManagedViaMDM { MigrationInfoViewTwo(username: username, getUserName: getUserName) .tag(2) } } } .id(redrawKey) .tabViewStyle(.page) .tabViewStyle(.page(indexDisplayMode: .always)) .background { Color(themeService.theme.backgroundColor) .edgesIgnoringSafeArea(/*@START_MENU_TOKEN@*/.all/*@END_MENU_TOKEN@*/) } .onAppear() { FeatureBannerVisibilityService().markAsRead() NotificationCenter.default.post(name: .bwiMarkTopBannerAsRead, object: self, userInfo: ["type" : "feature_banner"]) } .overlay { VStack() { HStack() { Spacer() Button { dismissView() } label: { Image(Asset.Images.closeButton.name) .renderingMode(.template) .resizable() .frame(width: 36, height: 36) .foregroundColor(Color(themeService.theme.colors.tertiaryContent)) .padding(20) } .accessibilityLabel(BWIL10n.bwiAllCommonClose) .isHidden(BWIBuildSettings.shared.BuMXMigrationInfoLevel > 2) } Spacer() HStack() { Button { withAnimation { selectedTab = 1 } } label: { Image(systemName: "arrow.left") .resizable() .frame(width: 24, height: 24) .foregroundStyle(Color(themeService.theme.colors.tertiaryContent)) .padding(EdgeInsets(top: 20, leading: 30, bottom: 10, trailing: 30)) } .opacity(selectedTab == 1 ? 0 : 1) Spacer() Button { withAnimation { selectedTab = 2 } } label: { Image(systemName: "arrow.right") .resizable() .frame(width: 24, height: 24) .foregroundStyle(Color(themeService.theme.colors.tertiaryContent)) .padding(EdgeInsets(top: 20, leading: 30, bottom: 10, trailing: 30)) } .opacity(selectedTab == 2 ? 0 : 1) } .isHidden(migrationFinished || BWIBuildSettings.shared.isManagedViaMDM) if BWIBuildSettings.shared.isManagedViaMDM && BWIBuildSettings.shared.BuMXMigrationInfoLevel < 3 { Button { dismissView() } label: { Text(BWIL10n.bwiAllCommonGotIt) .foregroundColor(Color(ThemeService.shared().theme.backgroundColor)) } .buttonStyle(PrimaryActionButtonStyle()) .padding() } } } .onAppear { setupIndicatorColors() } .onChange(of: themeService.isCurrentThemeDark) { _ in setupIndicatorColors() redrawKey = UUID() } .onChange(of: themeService.isCurrentThemeDark) { _ in setupIndicatorColors() redrawKey = UUID() } } private func setupIndicatorColors() { UIPageControl.appearance().currentPageIndicatorTintColor = themeService.theme.textPrimaryColor UIPageControl.appearance().pageIndicatorTintColor = themeService.theme.textSecondaryColor } } // MARK: Migraion Info View one struct MigrationInfoViewOne: View { @EnvironmentObject var themeService: BWIThemeService var body: some View { GeometryReader { geo in ScrollView(.vertical) { VStack(alignment: .center, spacing: 20) { Image("migration_detail_1") .resizable() .aspectRatio(contentMode: .fit) .frame(height: geo.size.height * 0.35, alignment: .bottom) .frame(maxWidth: geo.size.width - 60) .padding(EdgeInsets(top: 56, leading: 30, bottom: 0, trailing: 30)) .accessibilityHidden(true) infoText .frame(width: geo.size.width) Spacer() } .frame(minHeight: geo.size.height) .frame(width: geo.size.width) } .frame(width: geo.size.width, height: geo.size.height) } } var infoText: some View { VStack(alignment: .leading) { Text(BWIL10n.bwiMobileDialogMMoreTitle) .font(.title) .bold() .lineLimit(nil) .fixedSize(horizontal: false, vertical: true) .padding(.bottom, 20) if BWIBuildSettings.shared.isManagedViaMDM { if BWIBuildSettings.shared.BuMXMigrationInfoLevel == 3 { Text(BWIL10n.bwiMobileDialogM3MoreText1Mdm) + Text(BWIL10n.bwiMobileDialogM1MoreText2).bold() + Text(BWIL10n.bwiMobileDialogM1MoreText3) } else { Text(BWIL10n.bwiMobileDialogM1MoreText3) } } else { if BWIBuildSettings.shared.BuMXMigrationInfoLevel < 3 { Text(BWIL10n.bwiMobileDialogM1MoreText1) + Text(BWIL10n.bwiMobileDialogM1MoreText2).bold() + Text(BWIL10n.bwiMobileDialogM1MoreText3) } else { Text(BWIL10n.bwiMobileDialogM3MoreText1) + Text(BWIL10n.bwiMobileDialogM1MoreText2).bold() + Text(BWIL10n.bwiMobileDialogM1MoreText3) } } VStack(alignment: .leading) { HStack(alignment: .top) { Text("•") Text(BWIL10n.bwiMobileDialogM1MoreTextBullet1) } HStack(alignment: .top) { Text("•") Text(BWIL10n.bwiMobileDialogM1MoreTextBullet2) } HStack(alignment: .top) { Text("•") Text(BWIL10n.bwiMobileDialogM1MoreTextBullet3) } HStack(alignment: .top) { Text("•") Text(BWIL10n.bwiMobileDialogM1MoreTextBullet4) } } .multilineTextAlignment(.leading) .padding(.leading, 16) } .lineLimit(nil) .fixedSize(horizontal: false, vertical: true) .padding(EdgeInsets(top: 10, leading: 30, bottom: 0, trailing: 30)) .accessibilityElement(children: .ignore) .accessibilityLabel(accessibilityLabel) } var accessibilityLabel: String { if BWIBuildSettings.shared.BuMXMigrationInfoLevel < 3 && !BWIBuildSettings.shared.isManagedViaMDM { return BWIL10n.bwiMobileDialogMMoreTitle + ". " + BWIL10n.bwiMobileDialogM1MoreText1 + BWIL10n.bwiMobileDialogM1MoreText2 + BWIL10n.bwiMobileDialogM1MoreText3 + ", " + BWIL10n.bwiMobileDialogM1MoreTextBullet1 + ", " + BWIL10n.bwiMobileDialogM1MoreTextBullet2 + ", " + BWIL10n.bwiMobileDialogM1MoreTextBullet3 + ", " + BWIL10n.bwiMobileDialogM1MoreTextBullet4 } else { return BWIL10n.bwiMobileDialogMMoreTitle + ". " + BWIL10n.bwiMobileDialogM3MoreText1 + BWIL10n.bwiMobileDialogM1MoreText2 + BWIL10n.bwiMobileDialogM1MoreText3 + ", " + BWIL10n.bwiMobileDialogM1MoreTextBullet1 + ", " + BWIL10n.bwiMobileDialogM1MoreTextBullet2 + ", " + BWIL10n.bwiMobileDialogM1MoreTextBullet3 + ", " + BWIL10n.bwiMobileDialogM1MoreTextBullet4 } } } // MARK: Migraion Info View two struct MigrationInfoViewTwo: View { let username: String let getUserName: (() -> String?)? @State var lazyLoadedUserName: String? = nil @State var showSuccessToast: Bool = false @EnvironmentObject var themeService: BWIThemeService var body: some View { GeometryReader { geo in ScrollView(.vertical) { VStack(alignment: .center, spacing: 20) { VStack() { Image("bumx_logo") .resizable() .frame(width: 200, height: 200) .clipShape(.rect(cornerRadius: 36)) .overlay( RoundedRectangle(cornerRadius: 36) .stroke(.gray, lineWidth: 0.4) ) .accessibilityHidden(true) } .frame(height: geo.size.height * 0.35, alignment: .center) .padding(EdgeInsets(top: 56, leading: 30, bottom: 10, trailing: 30)) instructionsList successToast Spacer() downloadNewAppButton .frame(maxWidth: 350) .frame(width: geo.size.width - 100) .padding(.bottom, 100) } .frame(minHeight: geo.size.height) .frame(width: geo.size.width) .onAppear { lazyLoadedUserName = username.isEmpty ? getUserName?() : username } } .frame(width: geo.size.width, height: geo.size.height) } } var instructionsList: some View { VStack() { VStack(alignment: .center) { Text(BWIL10n.bwiMobileDialogMMore2Title) .font(.title) .bold() .padding(.bottom, 20) } VStack(alignment: .leading) { VStack(alignment: .leading) { HStack() { if lazyLoadedUserName != nil { Text("1.") Button(action: { UIPasteboard.general.string = lazyLoadedUserName ?? "" guard !showSuccessToast else { return } withAnimation { showSuccessToast = true } DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { withAnimation { showSuccessToast = false } } }, label: { Text(BWIL10n.bwiMobileDialogMMore2Text1) .underline() .foregroundColor(Color(themeService.theme.textPrimaryColor)) Image(systemName: "square.on.square") .resizable() .foregroundColor(Color(themeService.theme.textPrimaryColor)) .frame(width: 15, height: 15) }) } } HStack() { // bwi #7555 I know, but its only legacy if lazyLoadedUserName != nil { Text("2.") } else { Text("1.") } Text(BWIL10n.bwiMobileDialogMMore2Text2) } .padding(.bottom, 1) HStack() { if lazyLoadedUserName != nil { Text("3.") } else { Text("2.") } Text(BWIL10n.bwiMobileDialogMMore2Text3) } } } } .lineLimit(nil) .fixedSize(horizontal: false, vertical: true) .padding(EdgeInsets(top: 10, leading: 30, bottom: 0, trailing: 30)) } var downloadNewAppButton: some View { Button(action: { guard let bumxAppStoreURL = URL(string: "itms-apps://itunes.apple.com/app/id6738500048") else { return } guard UIApplication.shared.canOpenURL(bumxAppStoreURL) else { return } UIApplication.shared.open(bumxAppStoreURL, options: [:], completionHandler: nil) }, label: { Text(BWIL10n.bwiMobileDialogMMoreButton) .foregroundColor(Color(ThemeService.shared().theme.backgroundColor)) }) .buttonStyle(PrimaryActionButtonStyle()) } var successToast: some View { HStack() { Image(systemName: "checkmark") .resizable() .frame(width: 15, height: 15) .accessibilityHidden(true) Text(BWIL10n.bwiMobileDialogMMoreSuccess) } .foregroundColor(themeService.isCurrentThemeDark ? .black : .white) .padding(10) .background(themeService.isCurrentThemeDark ? .white : .black) .clipShape(RoundedRectangle(cornerRadius: 10)) .opacity(showSuccessToast ? 1 : 0) .transition(.opacity) } } // MARK: Migraion Success View struct MigrationSuccessView: View { @EnvironmentObject var themeService: BWIThemeService var body: some View { GeometryReader { geo in ScrollView(.vertical) { VStack(alignment: .center, spacing: 20) { VStack() { Image("bumx_logo") .resizable() .frame(width: 200, height: 200) .clipShape(.rect(cornerRadius: 36)) .overlay( RoundedRectangle(cornerRadius: 36) .stroke(.gray, lineWidth: 0.4) ) .accessibilityHidden(true) } .frame(height: geo.size.height * 0.35, alignment: .center) .padding(EdgeInsets(top: 56, leading: 30, bottom: 10, trailing: 30)) VStack { Text(BWIL10n.bwiMobileDialogM3SuccessChangeTitle) .font(.title) .bold() .padding(.bottom, 20) Text(BWIL10n.bwiMobileDialogM3SuccessText) .fixedSize(horizontal: false, vertical: true) .padding(EdgeInsets(top: 10, leading: 30, bottom: 0, trailing: 30)) } Spacer() } .frame(minHeight: geo.size.height) .frame(width: geo.size.width) } .frame(width: geo.size.width, height: geo.size.height) } } }