diff --git a/Scribe.xcodeproj/project.pbxproj b/Scribe.xcodeproj/project.pbxproj index f4f452d4..4571f854 100644 --- a/Scribe.xcodeproj/project.pbxproj +++ b/Scribe.xcodeproj/project.pbxproj @@ -884,6 +884,7 @@ D1F0367227AAE12200CD7921 /* InterfaceVariables.swift in Sources */ = {isa = PBXBuildFile; fileRef = D190B2492741B31F00705659 /* InterfaceVariables.swift */; }; D1F0367327AAE1B400CD7921 /* CommandVariables.swift in Sources */ = {isa = PBXBuildFile; fileRef = D190B2462741B24F00705659 /* CommandVariables.swift */; }; E937C5CF2E9FF94E00F94F99 /* TranslationData.sqlite in Resources */ = {isa = PBXBuildFile; fileRef = D1E3850F2C977FD100DCE538 /* TranslationData.sqlite */; }; + E96111482F04EC6B001E4F95 /* InstallationDownload.swift in Sources */ = {isa = PBXBuildFile; fileRef = E96111472F04EC62001E4F95 /* InstallationDownload.swift */; }; E996495A2E98A12100200F53 /* GRDB in Frameworks */ = {isa = PBXBuildFile; productRef = E99649592E98A12100200F53 /* GRDB */; }; E996495B2E98A31B00200F53 /* KeyboardProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 198369CB2C7980BA00C1B583 /* KeyboardProvider.swift */; }; E996495C2E98A32900200F53 /* KeyboardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D190B2592742565500705659 /* KeyboardViewController.swift */; }; @@ -1238,6 +1239,7 @@ D1D8B23D2AE408C50070B817 /* French.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = French.entitlements; sourceTree = ""; }; D1E3850F2C977FD100DCE538 /* TranslationData.sqlite */ = {isa = PBXFileReference; lastKnownFileType = file; path = TranslationData.sqlite; sourceTree = ""; }; D1FF8ED12C6C282500EF50AC /* English.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = English.entitlements; sourceTree = ""; }; + E96111472F04EC62001E4F95 /* InstallationDownload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallationDownload.swift; sourceTree = ""; }; E9FAC3882E9894F9008E00AC /* IDCommandVariables.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IDCommandVariables.swift; sourceTree = ""; }; E9FAC3892E9894F9008E00AC /* IDInterfaceVariables.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IDInterfaceVariables.swift; sourceTree = ""; }; E9FAC38A2E9894F9008E00AC /* IDKeyboardViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IDKeyboardViewController.swift; sourceTree = ""; }; @@ -1254,6 +1256,7 @@ /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ + E943457E2F05638700DFDB20 /* Button */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Button; sourceTree = ""; }; E9DADB332EF3CF9B00702783 /* ConfirmDialog */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = ConfirmDialog; sourceTree = ""; }; /* End PBXFileSystemSynchronizedRootGroup section */ @@ -1398,6 +1401,7 @@ 1406B7882A2DFE4C001DF45B /* InstallationTab */ = { isa = PBXGroup; children = ( + E96111472F04EC62001E4F95 /* InstallationDownload.swift */, 1455C87A2BD0EE4200F12BD4 /* DownloadDataTable.swift */, 1455C87B2BD0EE4200F12BD4 /* DownloadDataVC.swift */, 38BD213522D5907F00C6795D /* InstallationVC.swift */, @@ -1496,6 +1500,7 @@ 38BD213222D5907F00C6795D /* Scribe */ = { isa = PBXGroup; children = ( + E943457E2F05638700DFDB20 /* Button */, E9DADB332EF3CF9B00702783 /* ConfirmDialog */, 147797A92A2CD2B50044A53E /* AboutTab */, D1A2DCAF27AD378F0057A10D /* AppTexts */, @@ -1920,6 +1925,7 @@ E9FAC3992E989712008E00AC /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( + E943457E2F05638700DFDB20 /* Button */, E9DADB332EF3CF9B00702783 /* ConfirmDialog */, ); name = Scribe; @@ -2617,6 +2623,7 @@ 147797B02A2CD3370044A53E /* InfoChildTableViewCell.swift in Sources */, D1A2DCB427AD3EB50057A10D /* AppUISymbols.swift in Sources */, D171945427AF04E50038660B /* KeyboardViewController.swift in Sources */, + E96111482F04EC6B001E4F95 /* InstallationDownload.swift in Sources */, EDEE62252B2DE65A00A0B9C1 /* UIEdgeInsetsExtensions.swift in Sources */, D1CDED772A859E4800098546 /* DACommandVariables.swift in Sources */, D171943827AEF0560038660B /* KeyboardStyling.swift in Sources */, diff --git a/Scribe/Button/CTAButton.swift b/Scribe/Button/CTAButton.swift new file mode 100644 index 00000000..d97dfa2d --- /dev/null +++ b/Scribe/Button/CTAButton.swift @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +/** + * Call to Action Button. + */ + +import SwiftUI + +struct CTAButton: View { + let title: String + let action: () -> Void + @Environment(\.colorScheme) var colorScheme + + var body: some View { + Button(action: action) { + Text(title) + .font(.system(size: 20, weight: .bold)) + .foregroundColor(Color("lightTextDarkCTA")) + .frame(maxWidth: .infinity) + .padding(.vertical, 16) + .background(Color("appBtn")) + .cornerRadius(12) + .overlay( + RoundedRectangle(cornerRadius: 12) + .stroke(colorScheme == .dark ? Color("scribeCTA") : Color.clear, lineWidth: 1) + ) + .shadow( + color: Color(red: 0.247, green: 0.247, blue: 0.275, opacity: 0.25), + radius: 3, + x: 0, + y: 3 + ) + } + .buttonStyle(CTAButtonStyle()) + } +} + +struct CTAButtonStyle: ButtonStyle { + func makeBody(configuration: Configuration) -> some View { + configuration.label + .scaleEffect(configuration.isPressed ? 0.98 : 1.0) + .opacity(configuration.isPressed ? 0.8 : 1.0) + .animation(.easeOut(duration: 0.15), value: configuration.isPressed) + } +} diff --git a/Scribe/InstallationTab/InstallationDownload.swift b/Scribe/InstallationTab/InstallationDownload.swift new file mode 100644 index 00000000..4c566965 --- /dev/null +++ b/Scribe/InstallationTab/InstallationDownload.swift @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +/** + * Download button for the Installation tab. + */ + +import SwiftUI + +struct CardView: View { + let title: String + let mainText: String + let subtitle: String + let action: () -> Void + + var body: some View { + VStack(alignment: .leading, spacing: 6) { + Text(title) + .font(.system(size: 19, weight: .semibold)) + .foregroundColor(.primary) + + VStack(alignment: .leading, spacing: 6) { + HStack { + Text(mainText) + .font(.body) + .foregroundColor(.primary) + + Spacer() + + Image(systemName: "chevron.right") + .foregroundColor(.gray) + } + + Text(subtitle) + .font(.subheadline) + .foregroundColor(.secondary) + } + .padding() + .background(Color(.systemBackground)) + .cornerRadius(12) + .onTapGesture(perform: action) + } + .padding(.horizontal) + } +} + +struct InstallationDownload: View { + var body: some View { + CardView( + title: NSLocalizedString("app.installation.download.title", value: "Language data", comment: ""), + mainText: NSLocalizedString("app.installation.download.main_text", value: "Download keyboard data", comment: ""), + subtitle: NSLocalizedString("app.installation.download.subtitle", value: "Add new data to Scribe keyboards.", comment: "") + ) { + print("Card tapped") + } + } +} diff --git a/Scribe/InstallationTab/InstallationVC.swift b/Scribe/InstallationTab/InstallationVC.swift index 962ad916..2da16057 100644 --- a/Scribe/InstallationTab/InstallationVC.swift +++ b/Scribe/InstallationTab/InstallationVC.swift @@ -39,6 +39,7 @@ class InstallationVC: UIViewController { @IBOutlet var logoSpace: UIView! @IBOutlet var installationHeaderLabel: UILabel! + private var downloadButtonController: UIHostingController? private let installationTipCardState: Bool = { let userDefault = UserDefaults.standard @@ -96,7 +97,9 @@ class InstallationVC: UIViewController { setCurrentUI() showTipCardView() - addPopupButton() + showDownloadButton() + showCTAButton() + // addPopupButton() } /// Includes a call to checkDarkModeSetColors to set brand colors and a call to set the UI for the app screen. @@ -137,13 +140,13 @@ class InstallationVC: UIViewController { topIconPhone.isHidden = true topIconPad.isHidden = false for constraint in settingsCorner.constraints where constraint.identifier == "settingsCorner" { - constraint.constant = 125 + constraint.constant = 125 } } else { topIconPhone.isHidden = false topIconPad.isHidden = true for constraint in settingsCorner.constraints where constraint.identifier == "settingsCorner" { - constraint.constant = 70 + constraint.constant = 70 } } } @@ -329,97 +332,144 @@ extension InstallationVC { ) } - private func addPopupButton() { - let popupButton = UIButton(type: .system) - popupButton.setTitle("Open Popup", for: .normal) - popupButton.titleLabel?.font = UIFont.systemFont(ofSize: fontSize) - popupButton.backgroundColor = .systemBlue - popupButton.setTitleColor(.white, for: .normal) - popupButton.layer.cornerRadius = 10 - popupButton.translatesAutoresizingMaskIntoConstraints = false - - popupButton.addTarget(self, action: #selector(showPopup), for: .touchUpInside) - view.addSubview(popupButton) - - NSLayoutConstraint.activate([ - popupButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20), - popupButton.centerXAnchor.constraint(equalTo: view.centerXAnchor), - popupButton.widthAnchor.constraint(equalToConstant: 150), - popupButton.heightAnchor.constraint(equalToConstant: 44) - ]) - } + private func showDownloadButton() { + let downloadButton = InstallationDownload() + + let hostingController = UIHostingController(rootView: downloadButton) + hostingController.view.backgroundColor = .clear + + addChild(hostingController) + view.addSubview(hostingController.view) + + hostingController.view.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + hostingController.view.topAnchor.constraint(equalTo: appTextBackground.bottomAnchor, constant: 20), + hostingController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), + hostingController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor), + hostingController.view.heightAnchor.constraint(equalToConstant: 120) + ]) - @objc private func showPopup() { - var sourceLanguage = "English" - var destLanguage = "German" - var infoText = "The data you will download will allow you to translate from \(sourceLanguage) to \(destLanguage). Do you want to change the language you'll translate from?" - var changeText = "Change language" - var confirmText = "Use \(sourceLanguage)" - - let popupView = ConfirmTranslationSource( - infoText: infoText, - changeButtonText: changeText, - confirmButtonText: confirmText, - onDismiss: {self.dismiss(animated: true)}, - onChange: { - self.dismiss(animated: true) { - if let translationLangController = self.storyboard?.instantiateViewController( - identifier: "SelectionViewTemplateViewController" - ) as? SelectionViewTemplateViewController { - - var data = SettingsTableData.translateLangSettingsData - let langCode = "de" - - // Remove the current keyboard language from translation. - let langCodeIndex = SettingsTableData.translateLangSettingsData[0].section.firstIndex(where: { s in - s.sectionState == .specificLang(langCode) - }) ?? -1 - if langCodeIndex >= 0 { - data[0].section.remove(at: langCodeIndex) - } - - let sectionTitle = getKeyInDict(givenValue: langCode, dict: languagesAbbrDict) - - let parentSection = Section( - sectionTitle: sectionTitle, - imageString: nil, - hasToggle: false, - hasNestedNavigation: true, - sectionState: .translateLang, - shortDescription: nil, - externalLink: false - ) - - translationLangController.configureTable( - for: data, - parentSection: parentSection, - langCode: langCode - ) - - translationLangController.edgesForExtendedLayout = .all - - // COPY the navigation bar appearance from Settings tab. - if let settingsNavController = self.tabBarController?.viewControllers?[1] as? UINavigationController { - // Copy all the styling from Settings' nav controller. - self.navigationController?.navigationBar.standardAppearance = settingsNavController.navigationBar.standardAppearance - self.navigationController?.navigationBar.scrollEdgeAppearance = settingsNavController.navigationBar.scrollEdgeAppearance - self.navigationController?.navigationBar.tintColor = settingsNavController.navigationBar.tintColor - self.navigationController?.navigationBar.barTintColor = settingsNavController.navigationBar.barTintColor - } - - self.navigationController?.setNavigationBarHidden(false, animated: false) - self.navigationController?.pushViewController(translationLangController, animated: true) - } - } - }, - onConfirm: {self.dismiss(animated: true)}, - - ) - let hostingController = UIHostingController(rootView: popupView) - hostingController.modalPresentationStyle = .overFullScreen - hostingController.modalTransitionStyle = .crossDissolve - hostingController.view.backgroundColor = .clear - - present(hostingController, animated: true) + hostingController.didMove(toParent: self) + self.downloadButtonController = hostingController + } + + private func showCTAButton() { + let ctaButton = CTAButton( + title: NSLocalizedString("app.installation.quick_tutorial", value: "Quick tutorial", comment: ""), + action: { } + ) + + let hostingController = UIHostingController(rootView: ctaButton) + hostingController.view.backgroundColor = .clear + + addChild(hostingController) + view.addSubview(hostingController.view) + + hostingController.view.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + hostingController.view.topAnchor.constraint(equalTo: downloadButtonController!.view.bottomAnchor, constant: 20), + hostingController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), + hostingController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16), + hostingController.view.heightAnchor.constraint(equalToConstant: 60) + ]) + + hostingController.didMove(toParent: self) + } + + private func addPopupButton() { + let popupButton = UIButton(type: .system) + popupButton.setTitle("Open Popup", for: .normal) + popupButton.titleLabel?.font = UIFont.systemFont(ofSize: fontSize) + popupButton.backgroundColor = .systemBlue + popupButton.setTitleColor(.white, for: .normal) + popupButton.layer.cornerRadius = 10 + popupButton.translatesAutoresizingMaskIntoConstraints = false + + popupButton.addTarget(self, action: #selector(showPopup), for: .touchUpInside) + view.addSubview(popupButton) + + NSLayoutConstraint.activate([ + popupButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20), + popupButton.centerXAnchor.constraint(equalTo: view.centerXAnchor), + popupButton.widthAnchor.constraint(equalToConstant: 150), + popupButton.heightAnchor.constraint(equalToConstant: 44) + ]) + } + + @objc private func showPopup() { + var sourceLanguage = "English" + var destLanguage = "German" + var infoText = "The data you will download will allow you to translate from \(sourceLanguage) to \(destLanguage). Do you want to change the language you'll translate from?" + var changeText = "Change language" + var confirmText = "Use \(sourceLanguage)" + + let popupView = ConfirmTranslationSource( + infoText: infoText, + changeButtonText: changeText, + confirmButtonText: confirmText, + onDismiss: {self.dismiss(animated: true)}, + onChange: { + self.dismiss(animated: true) { + if let translationLangController = self.storyboard?.instantiateViewController( + identifier: "SelectionViewTemplateViewController" + ) as? SelectionViewTemplateViewController { + + var data = SettingsTableData.translateLangSettingsData + let langCode = "de" + + // Remove the current keyboard language from translation. + let langCodeIndex = SettingsTableData.translateLangSettingsData[0].section.firstIndex(where: { s in + s.sectionState == .specificLang(langCode) + }) ?? -1 + if langCodeIndex >= 0 { + data[0].section.remove(at: langCodeIndex) + } + + let sectionTitle = getKeyInDict(givenValue: langCode, dict: languagesAbbrDict) + + let parentSection = Section( + sectionTitle: sectionTitle, + imageString: nil, + hasToggle: false, + hasNestedNavigation: true, + sectionState: .translateLang, + shortDescription: nil, + externalLink: false + ) + + translationLangController.configureTable( + for: data, + parentSection: parentSection, + langCode: langCode + ) + + translationLangController.edgesForExtendedLayout = .all + + // COPY the navigation bar appearance from Settings tab. + if let settingsNavController = self.tabBarController?.viewControllers?[1] as? UINavigationController { + // Copy all the styling from Settings' nav controller. + self.navigationController?.navigationBar.standardAppearance = settingsNavController.navigationBar.standardAppearance + self.navigationController?.navigationBar.scrollEdgeAppearance = settingsNavController.navigationBar.scrollEdgeAppearance + self.navigationController?.navigationBar.tintColor = settingsNavController.navigationBar.tintColor + self.navigationController?.navigationBar.barTintColor = settingsNavController.navigationBar.barTintColor + } + + self.navigationController?.setNavigationBarHidden(false, animated: false) + self.navigationController?.pushViewController(translationLangController, animated: true) + } + } + }, + onConfirm: {self.dismiss(animated: true)}, + + ) + let hostingController = UIHostingController(rootView: popupView) + hostingController.modalPresentationStyle = .overFullScreen + hostingController.modalTransitionStyle = .crossDissolve + hostingController.view.backgroundColor = .clear + + present(hostingController, animated: true) + } }