diff --git a/Scribe.xcodeproj/project.pbxproj b/Scribe.xcodeproj/project.pbxproj index de717464..dd2feda4 100644 --- a/Scribe.xcodeproj/project.pbxproj +++ b/Scribe.xcodeproj/project.pbxproj @@ -10,12 +10,9 @@ 140158992A430DD000D14E52 /* ENThirdPartyLicenses.swift in Sources */ = {isa = PBXBuildFile; fileRef = 140158982A430DD000D14E52 /* ENThirdPartyLicenses.swift */; }; 1401589B2A45A07200D14E52 /* ENWikimediaAndScribe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1401589A2A45A07200D14E52 /* ENWikimediaAndScribe.swift */; }; 140158A22A4EDB2200D14E52 /* TableViewTemplateViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 140158A12A4EDB2200D14E52 /* TableViewTemplateViewController.swift */; }; - 140158A42A4F06CA00D14E52 /* CustomChildTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 140158A32A4F06CA00D14E52 /* CustomChildTableView.swift */; }; 1406B7872A2DFCDD001DF45B /* AboutTableData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1406B7862A2DFCDD001DF45B /* AboutTableData.swift */; }; 1406B78C2A3209CF001DF45B /* AppExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1406B78B2A3209CF001DF45B /* AppExtensions.swift */; }; 146B70BD2A853A3800710BD4 /* SwipeableTabBarController in Frameworks */ = {isa = PBXBuildFile; productRef = 146B70BC2A853A3800710BD4 /* SwipeableTabBarController */; }; - 147797AC2A2CD2F70044A53E /* ParentTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 147797AA2A2CD2F70044A53E /* ParentTableViewCell.swift */; }; - 147797AD2A2CD2F70044A53E /* ParentTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 147797AB2A2CD2F70044A53E /* ParentTableViewCell.xib */; }; 147797B02A2CD3370044A53E /* InfoChildTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 147797AE2A2CD3370044A53E /* InfoChildTableViewCell.swift */; }; 147797B12A2CD3370044A53E /* InfoChildTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 147797AF2A2CD3370044A53E /* InfoChildTableViewCell.xib */; }; 147797B32A2CD5AB0044A53E /* ParentTableCellModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 147797B22A2CD5AB0044A53E /* ParentTableCellModel.swift */; }; @@ -727,6 +724,10 @@ D1CDEDDC2A85AE9D00098546 /* NBInterfaceVariables.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1CDED802A85A12400098546 /* NBInterfaceVariables.swift */; }; D1F0367227AAE12200CD7921 /* InterfaceVariables.swift in Sources */ = {isa = PBXBuildFile; fileRef = D190B2492741B31F00705659 /* InterfaceVariables.swift */; }; D1F0367327AAE1B400CD7921 /* CommandVariables.swift in Sources */ = {isa = PBXBuildFile; fileRef = D190B2462741B24F00705659 /* CommandVariables.swift */; }; + ED2486F32B0B4E8C0038AE6A /* AboutTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED2486F12B0B4E8C0038AE6A /* AboutTableViewCell.swift */; }; + ED2486F42B0B4E8C0038AE6A /* AboutTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = ED2486F22B0B4E8C0038AE6A /* AboutTableViewCell.xib */; }; + EDB4601B2AF9FD8200BEA967 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = EDB4601A2AF9FD8200BEA967 /* Localizable.xcstrings */; }; + EDB460212B03B3E400BEA967 /* BaseTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDB460202B03B3E400BEA967 /* BaseTableViewController.swift */; }; EDC364692AE408F20001E456 /* InterfaceConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC364682AE408F20001E456 /* InterfaceConstants.swift */; }; EDC3646A2AE408FA0001E456 /* InterfaceConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC364682AE408F20001E456 /* InterfaceConstants.swift */; }; EDC3646B2AE408FC0001E456 /* InterfaceConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC364682AE408F20001E456 /* InterfaceConstants.swift */; }; @@ -739,6 +740,7 @@ EDC364722AE408FF0001E456 /* InterfaceConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC364682AE408F20001E456 /* InterfaceConstants.swift */; }; EDC364732AE409000001E456 /* InterfaceConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC364682AE408F20001E456 /* InterfaceConstants.swift */; }; EDC364742AE409000001E456 /* InterfaceConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC364682AE408F20001E456 /* InterfaceConstants.swift */; }; + EDEE62252B2DE65A00A0B9C1 /* UIEdgeInsetsExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDEE62242B2DE65A00A0B9C1 /* UIEdgeInsetsExtensions.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -845,12 +847,9 @@ 140158982A430DD000D14E52 /* ENThirdPartyLicenses.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ENThirdPartyLicenses.swift; sourceTree = ""; }; 1401589A2A45A07200D14E52 /* ENWikimediaAndScribe.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ENWikimediaAndScribe.swift; sourceTree = ""; }; 140158A12A4EDB2200D14E52 /* TableViewTemplateViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewTemplateViewController.swift; sourceTree = ""; }; - 140158A32A4F06CA00D14E52 /* CustomChildTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomChildTableView.swift; sourceTree = ""; }; 1406B7862A2DFCDD001DF45B /* AboutTableData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutTableData.swift; sourceTree = ""; }; 1406B78B2A3209CF001DF45B /* AppExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppExtensions.swift; sourceTree = ""; }; 144B56F22A568AC200C2F447 /* Scribe.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Scribe.entitlements; sourceTree = ""; }; - 147797AA2A2CD2F70044A53E /* ParentTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParentTableViewCell.swift; sourceTree = ""; }; - 147797AB2A2CD2F70044A53E /* ParentTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ParentTableViewCell.xib; sourceTree = ""; }; 147797AE2A2CD3370044A53E /* InfoChildTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoChildTableViewCell.swift; sourceTree = ""; }; 147797AF2A2CD3370044A53E /* InfoChildTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = InfoChildTableViewCell.xib; sourceTree = ""; }; 147797B22A2CD5AB0044A53E /* ParentTableCellModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParentTableCellModel.swift; sourceTree = ""; }; @@ -967,13 +966,20 @@ D1CDED802A85A12400098546 /* NBInterfaceVariables.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NBInterfaceVariables.swift; sourceTree = ""; }; D1CDED822A85A12C00098546 /* NBCommandVariables.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NBCommandVariables.swift; sourceTree = ""; }; D1D8B2372AE4084D0070B817 /* Swedish.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Swedish.entitlements; sourceTree = ""; }; - EDC364682AE408F20001E456 /* InterfaceConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterfaceConstants.swift; sourceTree = ""; }; D1D8B2382AE408620070B817 /* Spanish.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Spanish.entitlements; sourceTree = ""; }; D1D8B2392AE408720070B817 /* Russian.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Russian.entitlements; sourceTree = ""; }; D1D8B23A2AE408850070B817 /* Portuguese.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Portuguese.entitlements; sourceTree = ""; }; D1D8B23B2AE4089C0070B817 /* Italian.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Italian.entitlements; sourceTree = ""; }; D1D8B23C2AE408AC0070B817 /* German.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = German.entitlements; sourceTree = ""; }; D1D8B23D2AE408C50070B817 /* French.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = French.entitlements; sourceTree = ""; }; + ED2486F12B0B4E8C0038AE6A /* AboutTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutTableViewCell.swift; sourceTree = ""; }; + ED2486F22B0B4E8C0038AE6A /* AboutTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AboutTableViewCell.xib; sourceTree = ""; }; + EDB4601A2AF9FD8200BEA967 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; + EDB4601C2AF9FDD600BEA967 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/AppScreen.strings; sourceTree = ""; }; + EDB4601D2AF9FDD600BEA967 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/LaunchScreen.strings; sourceTree = ""; }; + EDB460202B03B3E400BEA967 /* BaseTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseTableViewController.swift; sourceTree = ""; }; + EDC364682AE408F20001E456 /* InterfaceConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterfaceConstants.swift; sourceTree = ""; }; + EDEE62242B2DE65A00A0B9C1 /* UIEdgeInsetsExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIEdgeInsetsExtensions.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1092,28 +1098,18 @@ 1406B7832A2DFCA2001DF45B /* Components */ = { isa = PBXGroup; children = ( + ED2486F02B0B4E610038AE6A /* UITableViewCells */, 1406B7852A2DFCBE001DF45B /* InfoChildTableViewCell */, - 1406B7842A2DFCAB001DF45B /* ParentTableViewCell */, 140158A12A4EDB2200D14E52 /* TableViewTemplateViewController.swift */, ); path = Components; sourceTree = ""; }; - 1406B7842A2DFCAB001DF45B /* ParentTableViewCell */ = { - isa = PBXGroup; - children = ( - 147797AA2A2CD2F70044A53E /* ParentTableViewCell.swift */, - 147797AB2A2CD2F70044A53E /* ParentTableViewCell.xib */, - ); - path = ParentTableViewCell; - sourceTree = ""; - }; 1406B7852A2DFCBE001DF45B /* InfoChildTableViewCell */ = { isa = PBXGroup; children = ( 147797AE2A2CD3370044A53E /* InfoChildTableViewCell.swift */, 147797AF2A2CD3370044A53E /* InfoChildTableViewCell.xib */, - 140158A32A4F06CA00D14E52 /* CustomChildTableView.swift */, ); path = InfoChildTableViewCell; sourceTree = ""; @@ -1213,21 +1209,24 @@ 38BD213222D5907F00C6795D /* Scribe */ = { isa = PBXGroup; children = ( - 144B56F22A568AC200C2F447 /* Scribe.entitlements */, - 1406B7882A2DFE4C001DF45B /* InstallationTab */, - 147797B62A2CFB560044A53E /* SettingsTab */, + EDEE62232B2DE64A00A0B9C1 /* Extensions */, 147797A92A2CD2B50044A53E /* AboutTab */, - D1A2DCAF27AD378F0057A10D /* AppTexts */, - 1406B7832A2DFCA2001DF45B /* Components */, - 147797B22A2CD5AB0044A53E /* ParentTableCellModel.swift */, 38BD213322D5907F00C6795D /* AppDelegate.swift */, - D17193C327AEAD7D0038660B /* AppStyling.swift */, - D1A2DCB327AD3EB50057A10D /* AppUISymbols.swift */, 1406B78B2A3209CF001DF45B /* AppExtensions.swift */, 38BD213722D5907F00C6795D /* AppScreen.storyboard */, - 38BD213F22D5908100C6795D /* LaunchScreen.storyboard */, + D17193C327AEAD7D0038660B /* AppStyling.swift */, + D1A2DCAF27AD378F0057A10D /* AppTexts */, + D1A2DCB327AD3EB50057A10D /* AppUISymbols.swift */, 38BD213D22D5908100C6795D /* Assets.xcassets */, + 1406B7832A2DFCA2001DF45B /* Components */, 38BD214222D5908100C6795D /* Info.plist */, + 1406B7882A2DFE4C001DF45B /* InstallationTab */, + 38BD213F22D5908100C6795D /* LaunchScreen.storyboard */, + 147797B22A2CD5AB0044A53E /* ParentTableCellModel.swift */, + EDB460222B03BF7000BEA967 /* Resources */, + 144B56F22A568AC200C2F447 /* Scribe.entitlements */, + 147797B62A2CFB560044A53E /* SettingsTab */, + EDB4601F2B03B3B400BEA967 /* Views */, ); path = Scribe; sourceTree = ""; @@ -1477,6 +1476,39 @@ path = Keyboards; sourceTree = ""; }; + ED2486F02B0B4E610038AE6A /* UITableViewCells */ = { + isa = PBXGroup; + children = ( + ED2486F12B0B4E8C0038AE6A /* AboutTableViewCell.swift */, + ED2486F22B0B4E8C0038AE6A /* AboutTableViewCell.xib */, + ); + path = UITableViewCells; + sourceTree = ""; + }; + EDB4601F2B03B3B400BEA967 /* Views */ = { + isa = PBXGroup; + children = ( + EDB460202B03B3E400BEA967 /* BaseTableViewController.swift */, + ); + path = Views; + sourceTree = ""; + }; + EDB460222B03BF7000BEA967 /* Resources */ = { + isa = PBXGroup; + children = ( + EDB4601A2AF9FD8200BEA967 /* Localizable.xcstrings */, + ); + path = Resources; + sourceTree = ""; + }; + EDEE62232B2DE64A00A0B9C1 /* Extensions */ = { + isa = PBXGroup; + children = ( + EDEE62242B2DE65A00A0B9C1 /* UIEdgeInsetsExtensions.swift */, + ); + path = Extensions; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -1791,6 +1823,7 @@ knownRegions = ( en, Base, + fr, ); mainGroup = 38BD212722D5907E00C6795D; packageReferences = ( @@ -1825,8 +1858,9 @@ files = ( 38BD214122D5908100C6795D /* LaunchScreen.storyboard in Resources */, 147797B12A2CD3370044A53E /* InfoChildTableViewCell.xib in Resources */, + EDB4601B2AF9FD8200BEA967 /* Localizable.xcstrings in Resources */, + ED2486F42B0B4E8C0038AE6A /* AboutTableViewCell.xib in Resources */, 38BD213922D5907F00C6795D /* AppScreen.storyboard in Resources */, - 147797AD2A2CD2F70044A53E /* ParentTableViewCell.xib in Resources */, 38BD213E22D5908100C6795D /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1992,6 +2026,7 @@ 147797B02A2CD3370044A53E /* InfoChildTableViewCell.swift in Sources */, D1A2DCB427AD3EB50057A10D /* AppUISymbols.swift in Sources */, D171945427AF04E50038660B /* KeyboardViewController.swift in Sources */, + EDEE62252B2DE65A00A0B9C1 /* UIEdgeInsetsExtensions.swift in Sources */, D1CDED772A859E4800098546 /* DACommandVariables.swift in Sources */, D171943827AEF0560038660B /* KeyboardStyling.swift in Sources */, D111E9BA27AFE7B200746F92 /* Annotate.swift in Sources */, @@ -2000,12 +2035,12 @@ D1CDED792A859FB600098546 /* ENCommandVariables.swift in Sources */, 140158A22A4EDB2200D14E52 /* TableViewTemplateViewController.swift in Sources */, D1CDED7F2A85A05C00098546 /* HECommandVariables.swift in Sources */, - 147797AC2A2CD2F70044A53E /* ParentTableViewCell.swift in Sources */, 38BD213422D5907F00C6795D /* AppDelegate.swift in Sources */, 30489C1E2936DAB700B59393 /* ToolTipView.swift in Sources */, 3045396F293B9DF2003AE55B /* ToolTipViewTheme.swift in Sources */, D1F0367327AAE1B400CD7921 /* CommandVariables.swift in Sources */, D16DD3A529E78A1500FB9022 /* Utilities.swift in Sources */, + EDB460212B03B3E400BEA967 /* BaseTableViewController.swift in Sources */, D1CDED752A859DDD00098546 /* DAInterfaceVariables.swift in Sources */, D17193E827AECAE60038660B /* ESInterfaceVariables.swift in Sources */, D156BC0929CCDBE8007E7362 /* DEAppText.swift in Sources */, @@ -2022,12 +2057,12 @@ D171942027AECD170038660B /* SVCommandVariables.swift in Sources */, 30453967293B9D31003AE55B /* ViewThemeable.swift in Sources */, D1362A39274C106A00C00E48 /* ColorVariables.swift in Sources */, + ED2486F32B0B4E8C0038AE6A /* AboutTableViewCell.swift in Sources */, D1A2DCB127AD37BD0057A10D /* ENAppText.swift in Sources */, D1CDED832A85A12C00098546 /* NBCommandVariables.swift in Sources */, D171944927AEF7290038660B /* KeyboardKeys.swift in Sources */, 147797B32A2CD5AB0044A53E /* ParentTableCellModel.swift in Sources */, 1401589B2A45A07200D14E52 /* ENWikimediaAndScribe.swift in Sources */, - 140158A42A4F06CA00D14E52 /* CustomChildTableView.swift in Sources */, 3045396B293B9DC9003AE55B /* ToolTipViewDatasourceable.swift in Sources */, D156BC0829CCDBE8007E7362 /* DEPrivacyPolicy.swift in Sources */, D1B071A027C6A1AA00FD7DBD /* KeyAltChars.swift in Sources */, @@ -2776,6 +2811,7 @@ isa = PBXVariantGroup; children = ( 38BD213822D5907F00C6795D /* Base */, + EDB4601C2AF9FDD600BEA967 /* fr */, ); name = AppScreen.storyboard; sourceTree = ""; @@ -2784,6 +2820,7 @@ isa = PBXVariantGroup; children = ( 38BD214022D5908100C6795D /* Base */, + EDB4601D2AF9FDD600BEA967 /* fr */, ); name = LaunchScreen.storyboard; sourceTree = ""; @@ -2795,6 +2832,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -2857,6 +2895,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; diff --git a/Scribe/AboutTab/AboutTableData.swift b/Scribe/AboutTab/AboutTableData.swift index a395f598..fa2dd593 100644 --- a/Scribe/AboutTab/AboutTableData.swift +++ b/Scribe/AboutTab/AboutTableData.swift @@ -9,28 +9,28 @@ struct AboutTableData { ParentTableCellModel( headingTitle: "Community", section: [ - Section(sectionTitle: "See the code on GitHub", imageString: "github", hasToggle: false, sectionState: .github), - Section(sectionTitle: "Chat with the team on Matrix", imageString: "matrix", hasToggle: false, sectionState: .matrix), - Section(sectionTitle: "Wikimedia and Scribe", imageString: "wikimedia", hasToggle: false, sectionState: .wikimedia), - Section(sectionTitle: "Share Scribe", imageString: "square.and.arrow.up", hasToggle: false, sectionState: .shareScribe), + Section(sectionTitle: "See the code on GitHub", imageString: "github", sectionState: .github), + Section(sectionTitle: "Chat with the team on Matrix", imageString: "matrix", sectionState: .matrix), + Section(sectionTitle: "Wikimedia and Scribe", imageString: "wikimedia", sectionState: .wikimedia), + Section(sectionTitle: "Share Scribe", imageString: "square.and.arrow.up", sectionState: .shareScribe), ], hasDynamicData: nil ), ParentTableCellModel( headingTitle: "Feedback and support", section: [ - Section(sectionTitle: "Rate Scribe", imageString: "star", hasToggle: false, sectionState: .rateScribe), - Section(sectionTitle: "Report a bug", imageString: "ant", hasToggle: false, sectionState: .bugReport), - Section(sectionTitle: "Send us an email", imageString: "envelope", hasToggle: false, sectionState: .email), -// Section(sectionTitle: "Reset app hints", imageString: "lightbulb", hasToggle: false, sectionState: .appHints) + Section(sectionTitle: "Rate Scribe", imageString: "star", sectionState: .rateScribe), + Section(sectionTitle: "Report a bug", imageString: "ant", sectionState: .bugReport), + Section(sectionTitle: "Send us an email", imageString: "envelope", sectionState: .email), +// Section(sectionTitle: "Reset app hints", imageString: "lightbulb", sectionState: .appHints) ], hasDynamicData: nil ), ParentTableCellModel( headingTitle: "Legal", section: [ - Section(sectionTitle: "Privacy policy", imageString: "lock.shield", hasToggle: false, sectionState: .privacyPolicy), - Section(sectionTitle: "Third-party licenses", imageString: "thirdPartyLicenses", hasToggle: false, sectionState: .licenses), + Section(sectionTitle: "Privacy policy", imageString: "lock.shield", hasNestedNavigation: true, sectionState: .privacyPolicy), + Section(sectionTitle: "Third-party licenses", imageString: "thirdPartyLicenses", hasNestedNavigation: true, sectionState: .licenses), ], hasDynamicData: nil ), diff --git a/Scribe/AboutTab/AboutViewController.swift b/Scribe/AboutTab/AboutViewController.swift index ea52462b..89326f72 100644 --- a/Scribe/AboutTab/AboutViewController.swift +++ b/Scribe/AboutTab/AboutViewController.swift @@ -1,51 +1,171 @@ // -// AboutViewController.swift +// Copyright (C) 2023 Scribe +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . // import UIKit +import MessageUI +import StoreKit -class AboutViewController: UIViewController { - @IBOutlet var outerTable: UITableView! +final class AboutViewController: BaseTableViewController { - let tableData = AboutTableData.aboutTableData + override var dataSet: [ParentTableCellModel] { + AboutTableData.aboutTableData + } override func viewDidLoad() { super.viewDidLoad() - title = "About" + title = NSLocalizedString("about.title", comment: "The title of the about tab") + + tableView.register(UINib(nibName: "AboutTableViewCell", bundle: nil), forCellReuseIdentifier: AboutTableViewCell.reuseIdentifier) + } +} + +// MARK: UITableViewDataSource + +extension AboutViewController { - let nib = UINib(nibName: "ParentTableViewCell", bundle: nil) - outerTable.register(nib, forCellReuseIdentifier: "ParentTableViewCell") + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: AboutTableViewCell.reuseIdentifier, for: indexPath) as! AboutTableViewCell - outerTable.dataSource = self - outerTable.delegate = self + cell.configureCell(for: dataSet[indexPath.section].section[indexPath.row]) - outerTable.separatorStyle = .none - outerTable.backgroundColor = .clear + return cell } } -// MARK: UITableViewDataSource +// MARK: UITableViewDelegate + +extension AboutViewController { + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let tableSection = dataSet[indexPath.section] + let section = tableSection.section[indexPath.row] + + switch section.sectionState { + case .github: + openURLString(urlString: "https://github.com/scribe-org/Scribe-iOS", withEncoding: false) + + case .matrix: + openURLString(urlString: "https://matrix.to/#/#scribe_community:matrix.org", withEncoding: true) + + case .wikimedia: + if let viewController = storyboard?.instantiateViewController(identifier: "InformationScreenVC") as? InformationScreenVC { + navigationController?.pushViewController(viewController, animated: true) + viewController.section = .wikimedia + } + + case .shareScribe: + showShareSheet() -/// Function implementation conforming to the UITableViewDataSource protocol. -extension AboutViewController: UITableViewDataSource { - func tableView(_: UITableView, numberOfRowsInSection _: Int) -> Int { - return tableData.count + case .rateScribe: + showRateScribeUI() + + case .bugReport: + openURLString(urlString: "https://github.com/scribe-org/Scribe-iOS/issues", withEncoding: false) + + case .email: + showEmailUI() + + // case .appHints: + // // reset functionality + // print("Resets app hints") + + case .privacyPolicy: + if let viewController = storyboard?.instantiateViewController(identifier: "InformationScreenVC") as? InformationScreenVC { + navigationController?.pushViewController(viewController, animated: true) + viewController.section = .privacyPolicy + } + + case .licenses: + if let viewController = storyboard?.instantiateViewController(identifier: "InformationScreenVC") as? InformationScreenVC { + navigationController?.pushViewController(viewController, animated: true) + viewController.section = .licenses + } + + case .appLang: break + case .none: break + case .specificLang(_): break + } + + + if let selectedIndexPath = tableView.indexPathForSelectedRow { + tableView.deselectRow(at: selectedIndexPath, animated: false) + } } - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "ParentTableViewCell", for: indexPath) as! ParentTableViewCell + private func openURLString(urlString: String, withEncoding: Bool) { + if withEncoding { + let encodedString = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) + guard let encodedURLString = encodedString, let url = URL(string: encodedURLString) else { return } + UIApplication.shared.open(url, options: [:], completionHandler: nil) + } else { + guard let url = URL(string: urlString) else { return } + UIApplication.shared.open(url, options: [:], completionHandler: nil) + } + } - cell.configureCell(for: tableData[indexPath.row]) + private func showRateScribeUI() { + if #available(iOS 14.0, *) { + guard let scene = UIApplication.shared.foregroundActiveScene else { return } + SKStoreReviewController.requestReview(in: scene) + } else { + let alert = UIAlertController(title: "Enjoying Scribe?", message: "Rate Scribe on the App Store.", preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "Continue", style: .default, handler: openScribeAppStore(alert:))) + alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) + present(alert, animated: true) + } + } - cell.backgroundColor = .clear - cell.selectionStyle = .none + private func openScribeAppStore(alert _: UIAlertAction) { + openURLString(urlString: "itms-apps: //itunes.apple.com/app/id1596613886", withEncoding: true) + } - return cell + private func showEmailUI() { + if MFMailComposeViewController.canSendMail() { + let mailComposeViewController = MFMailComposeViewController() + mailComposeViewController.mailComposeDelegate = self + mailComposeViewController.setToRecipients(["team@scri.be"]) + mailComposeViewController.setSubject("Hey Scribe!") + + present(mailComposeViewController, animated: true, completion: nil) + } else { + /// Show alert mentioning the email address + let alert = UIAlertController(title: "Send us an email?", message: "Reach out to us at team@scri.be", preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) + present(alert, animated: true) + } + } + + private func showShareSheet() { + let urlString = "itms-apps: //itunes.apple.com/app/id1596613886" + let encodedString = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) + guard let encodedURLString = encodedString, let url = URL(string: encodedURLString) else { return } + + let shareSheetVC = UIActivityViewController(activityItems: [url], applicationActivities: nil) + + present(shareSheetVC, animated: true, completion: nil) } } -// MARK: UITableViewDelegate +// MARK: - MFMailComposeViewControllerDelegate + +extension AboutViewController: MFMailComposeViewControllerDelegate { -/// Function implementation conforming to the UITableViewDelegate protocol. -extension AboutViewController: UITableViewDelegate {} + func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith _: MFMailComposeResult, error _: Error?) { + controller.dismiss(animated: true, completion: nil) + } +} diff --git a/Scribe/AboutTab/InformationScreenVC.swift b/Scribe/AboutTab/InformationScreenVC.swift index d019da10..3f40d61f 100644 --- a/Scribe/AboutTab/InformationScreenVC.swift +++ b/Scribe/AboutTab/InformationScreenVC.swift @@ -15,7 +15,6 @@ class InformationScreenVC: UIViewController { @IBOutlet var iconImageView: UIImageView! var text: String = "" - var screenTitle: String = "" var section: SectionState = .privacyPolicy override func viewDidLoad() { diff --git a/Scribe/Base.lproj/AppScreen.storyboard b/Scribe/Base.lproj/AppScreen.storyboard index 237a5aa0..15a5ec90 100644 --- a/Scribe/Base.lproj/AppScreen.storyboard +++ b/Scribe/Base.lproj/AppScreen.storyboard @@ -1,9 +1,9 @@ - + - + @@ -11,36 +11,24 @@ - + - - + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + - + @@ -51,7 +39,7 @@ - + @@ -171,11 +159,10 @@ - + - - + @@ -208,10 +195,10 @@ - + - + @@ -232,44 +219,13 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -420,7 +376,7 @@ - + @@ -448,6 +404,25 @@ + + + + + + + + + + + + + + + + + + + @@ -472,13 +447,13 @@ - + - + diff --git a/Scribe/Components/InfoChildTableViewCell/CustomChildTableView.swift b/Scribe/Components/InfoChildTableViewCell/CustomChildTableView.swift deleted file mode 100644 index a0c27a75..00000000 --- a/Scribe/Components/InfoChildTableViewCell/CustomChildTableView.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// CustomChildTableView.swift -// - -import UIKit - -/// Need this class so that rows of the child table resize the parent table cell. -/// Works similar to extending the UITableView class. -/// Without this, the child table views will not be visible until we manually specify a height for parent table cell. -class CustomChildTableView: UITableView { - override var intrinsicContentSize: CGSize { - self.layoutIfNeeded() - return self.contentSize - } - - override var contentSize: CGSize { - didSet { - self.invalidateIntrinsicContentSize() - } - } - - override func reloadData() { - super.reloadData() - invalidateIntrinsicContentSize() - } -} diff --git a/Scribe/Components/InfoChildTableViewCell/InfoChildTableViewCell.swift b/Scribe/Components/InfoChildTableViewCell/InfoChildTableViewCell.swift index 3b63bb90..350e5b9b 100644 --- a/Scribe/Components/InfoChildTableViewCell/InfoChildTableViewCell.swift +++ b/Scribe/Components/InfoChildTableViewCell/InfoChildTableViewCell.swift @@ -4,11 +4,16 @@ import UIKit -class InfoChildTableViewCell: UITableViewCell { - @IBOutlet var titleLabel: UILabel! - @IBOutlet var iconImageView: UIImageView! +final class InfoChildTableViewCell: UITableViewCell { + + // MARK: - Constants + + static let reuseIdentifier = String(describing: InfoChildTableViewCell.self) - @IBOutlet var chevronImgView: UIImageView! + // MARK: - Properties + + @IBOutlet var titleLabel: UILabel! + @IBOutlet var descriptionLabel: UILabel! @IBOutlet var toggleSwitch: UISwitch! var section: Section? @@ -29,16 +34,31 @@ class InfoChildTableViewCell: UITableViewCell { return action } + + // MARK: - Functions func configureCell(for section: Section) { self.section = section + + selectionStyle = .none + titleLabel.text = section.sectionTitle - iconImageView.image = UIImage.availableIconImage(with: section.imageString) + + if let shortDescription = section.shortDescription { + descriptionLabel.text = shortDescription + + contentView.addSubview(descriptionLabel) + } else { + descriptionLabel.text = nil + descriptionLabel.removeFromSuperview() + } if !section.hasToggle { + accessoryType = .disclosureIndicator toggleSwitch.isHidden = true } else { - chevronImgView.isHidden = true + accessoryType = .none + toggleSwitch.isHidden = false } fetchSwitchStateForCell() diff --git a/Scribe/Components/InfoChildTableViewCell/InfoChildTableViewCell.xib b/Scribe/Components/InfoChildTableViewCell/InfoChildTableViewCell.xib index d22b8dfa..a89771bf 100644 --- a/Scribe/Components/InfoChildTableViewCell/InfoChildTableViewCell.xib +++ b/Scribe/Components/InfoChildTableViewCell/InfoChildTableViewCell.xib @@ -6,82 +6,74 @@ + - - - + + + - + - - - + + - - + - - - - + + + + + - + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + - - + - + - + + + diff --git a/Scribe/Components/ParentTableViewCell/ParentTableViewCell.swift b/Scribe/Components/ParentTableViewCell/ParentTableViewCell.swift deleted file mode 100644 index e2e1c9e7..00000000 --- a/Scribe/Components/ParentTableViewCell/ParentTableViewCell.swift +++ /dev/null @@ -1,241 +0,0 @@ -// -// ParentTableViewCell.swift -// - -import MessageUI -import StoreKit -import UIKit - -class ParentTableViewCell: UITableViewCell { - @IBOutlet var titleLabel: UILabel! - @IBOutlet var innerTable: UITableView! - @IBOutlet var containerView: UIView! - - var data: ParentTableCellModel? - var parentSection: Section? - - override func awakeFromNib() { - super.awakeFromNib() - - let nib = UINib(nibName: "InfoChildTableViewCell", bundle: nil) - innerTable.register(nib, forCellReuseIdentifier: "InfoChildTableViewCell") - - innerTable.dataSource = self - innerTable.delegate = self - innerTable.rowHeight = UITableView.automaticDimension - innerTable.isScrollEnabled = false - innerTable.reloadData() - - setContainerViewUI() - } - - override func setSelected(_ selected: Bool, animated: Bool) { - super.setSelected(selected, animated: animated) - } - - func configureCell(for data: ParentTableCellModel) { - self.data = data - titleLabel.text = data.headingTitle - - if let safeData = self.data { - if let _ = safeData.hasDynamicData { - NotificationCenter.default.addObserver(self, selector: #selector(reloadChildTable), name: .keyboardsUpdatedNotification, object: nil) - } - } - } - - func setContainerViewUI() { - containerView.layer.cornerRadius = containerView.frame.width * 0.05 - innerTable.layer.cornerRadius = innerTable.frame.width * 0.05 - innerTable.clipsToBounds = true - applyShadowEffects(elem: containerView) - } - - @objc func reloadChildTable() { - guard let data = data, - let dynamicDataState = data.hasDynamicData else { return } - - switch dynamicDataState { - case .installedKeyboards: - self.data?.section = SettingsTableData.getInstalledKeyboardsSections() - } - - DispatchQueue.main.async { - self.innerTable.reloadData() - } - } - - deinit { - NotificationCenter.default.removeObserver(self) - } -} - -// MARK: UITableViewDataSource - -/// Function implementation conforming to the UITableViewDataSource protocol. -extension ParentTableViewCell: UITableViewDataSource { - func tableView(_: UITableView, numberOfRowsInSection _: Int) -> Int { - return data?.section.count ?? 0 - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "InfoChildTableViewCell", for: indexPath) as! InfoChildTableViewCell - - if let parentSection = parentSection { - cell.parentSection = parentSection - } - - if let data = data { - cell.configureCell(for: data.section[indexPath.row]) - } - - return cell - } -} - -// MARK: UITableViewDelegate - -/// Function implementation conforming to the UITableViewDelegate protocol. -extension ParentTableViewCell: UITableViewDelegate { - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - if let section = data?.section[indexPath.row] { - switch section.sectionState { - case .github: - openURLString(urlString: "https://github.com/scribe-org/Scribe-iOS", withEncoding: false) - - case .matrix: - openURLString(urlString: "https://matrix.to/#/#scribe_community:matrix.org", withEncoding: true) - - case .wikimedia: - if let viewController = parentViewController?.storyboard?.instantiateViewController(identifier: "InformationScreenVC") as? InformationScreenVC { - parentViewController?.navigationController?.pushViewController(viewController, animated: true) - viewController.section = .wikimedia - } - - case .shareScribe: - showShareSheet() - - case .rateScribe: - showRateScribeUI() - - case .bugReport: - openURLString(urlString: "https://github.com/scribe-org/Scribe-iOS/issues", withEncoding: false) - - case .email: - showEmailUI() - -// case .appHints: -// // reset functionality -// print("Resets app hints") - - case .privacyPolicy: - if let viewController = parentViewController?.storyboard?.instantiateViewController(identifier: "InformationScreenVC") as? InformationScreenVC { - parentViewController?.navigationController?.pushViewController(viewController, animated: true) - viewController.section = .privacyPolicy - } - - case .licenses: - if let viewController = parentViewController?.storyboard?.instantiateViewController(identifier: "InformationScreenVC") as? InformationScreenVC { - parentViewController?.navigationController?.pushViewController(viewController, animated: true) - viewController.section = .licenses - } - - case .appLang: break - - case .specificLang: - if let viewController = parentViewController?.storyboard?.instantiateViewController(identifier: "TableViewTemplateViewController") as? TableViewTemplateViewController { - // Languages where we can disable accent keys. - let accentKeyLanguages: [String] = ["Swedish", "German", "Spanish"] - let accentKeyOptionIndex = SettingsTableData.languageSettingsData[0].section.firstIndex(where: { - s in s.sectionTitle.elementsEqual("Disable accent characters") - }) ?? -1 - if accentKeyLanguages.firstIndex(of: section.sectionTitle) == nil && accentKeyOptionIndex != -1 - { - // As there are no accent keys we can remove the `Disable accent characters` option. - let accentKeySettings = SettingsTableData.languageSettingsData[0].section.remove(at: accentKeyOptionIndex) - print(accentKeySettings) - } else if accentKeyLanguages.firstIndex(of: section.sectionTitle) != nil && accentKeyOptionIndex == -1 - { - SettingsTableData.languageSettingsData[0].section.insert(Section( - sectionTitle: "Disable accent characters", - imageString: "info.circle", - hasToggle: true, - sectionState: .none(.toggleAccentCharacters) - ), at: 1) - } - viewController.configureTable(for: SettingsTableData.languageSettingsData, parentSection: section) - - parentViewController?.navigationController?.pushViewController(viewController, animated: true) - } - - case .none: break - } - } - - if let selectedIndexPath = tableView.indexPathForSelectedRow { - tableView.deselectRow(at: selectedIndexPath, animated: false) - } - } - - func openURLString(urlString: String, withEncoding: Bool) { - if withEncoding { - let encodedString = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) - guard let encodedURLString = encodedString, let url = URL(string: encodedURLString) else { return } - UIApplication.shared.open(url, options: [:], completionHandler: nil) - } else { - guard let url = URL(string: urlString) else { return } - UIApplication.shared.open(url, options: [:], completionHandler: nil) - } - } - - func showRateScribeUI() { - if #available(iOS 14.0, *) { - guard let scene = UIApplication.shared.foregroundActiveScene else { return } - SKStoreReviewController.requestReview(in: scene) - } else { - let alert = UIAlertController(title: "Enjoying Scribe?", message: "Rate Scribe on the App Store.", preferredStyle: .alert) - alert.addAction(UIAlertAction(title: "Continue", style: .default, handler: openScribeAppStore(alert:))) - alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) - parentViewController?.present(alert, animated: true) - } - } - - func openScribeAppStore(alert _: UIAlertAction) { - openURLString(urlString: "itms-apps: //itunes.apple.com/app/id1596613886", withEncoding: true) - } - - func showEmailUI() { - if MFMailComposeViewController.canSendMail() { - let mailComposeViewController = MFMailComposeViewController() - mailComposeViewController.mailComposeDelegate = self - mailComposeViewController.setToRecipients(["team@scri.be"]) - mailComposeViewController.setSubject("Hey Scribe!") - - parentViewController?.present(mailComposeViewController, animated: true, completion: nil) - } else { - /// Show alert mentioning the email address - let alert = UIAlertController(title: "Send us an email?", message: "Reach out to us at team@scri.be", preferredStyle: .alert) - alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) - parentViewController?.present(alert, animated: true) - } - } - - func showShareSheet() { - let urlString = "itms-apps: //itunes.apple.com/app/id1596613886" - let encodedString = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) - guard let encodedURLString = encodedString, let url = URL(string: encodedURLString) else { return } - - let shareSheetVC = UIActivityViewController(activityItems: [url], applicationActivities: nil) - - parentViewController?.present(shareSheetVC, animated: true, completion: nil) - } -} - -// MARK: MFMailComposeViewControllerDelegate - -/// Function implementation conforming to the MFMailComposeViewControllerDelegate protocol. -extension ParentTableViewCell: MFMailComposeViewControllerDelegate { - func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith _: MFMailComposeResult, error _: Error?) { - controller.dismiss(animated: true, completion: nil) - } -} diff --git a/Scribe/Components/ParentTableViewCell/ParentTableViewCell.xib b/Scribe/Components/ParentTableViewCell/ParentTableViewCell.xib deleted file mode 100644 index 242b0aa9..00000000 --- a/Scribe/Components/ParentTableViewCell/ParentTableViewCell.xib +++ /dev/null @@ -1,70 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Scribe/Components/TableViewTemplateViewController.swift b/Scribe/Components/TableViewTemplateViewController.swift index 587ee0dd..b2b9d5c6 100644 --- a/Scribe/Components/TableViewTemplateViewController.swift +++ b/Scribe/Components/TableViewTemplateViewController.swift @@ -4,65 +4,57 @@ import UIKit -class TableViewTemplateViewController: UIViewController { - @IBOutlet var mainTable: UITableView! +final class TableViewTemplateViewController: BaseTableViewController { - var screenTitle: String = "" - var tableData: [ParentTableCellModel] = [] - var parentSection: Section? + // MARK: - Properties - var langCode: String { - if let section = parentSection { - guard case let .specificLang(lang) = section.sectionState else { return "de" } + override var dataSet: [ParentTableCellModel] { + tableData + } - return lang - } + private var tableData: [ParentTableCellModel] = [] + private var parentSection: Section? - return "" - } + private var langCode: String { + guard let parentSection else { + return "" + } - func configureTable(for tableData: [ParentTableCellModel], parentSection: Section) { - self.tableData = tableData - self.parentSection = parentSection + guard case let .specificLang(lang) = parentSection.sectionState else { + return "de" + } - print("break") + return lang } + // MARK: - Functions + override func viewDidLoad() { super.viewDidLoad() - title = parentSection?.sectionTitle ?? "Unknown" - - let nib = UINib(nibName: "ParentTableViewCell", bundle: nil) - mainTable.register(nib, forCellReuseIdentifier: "ParentTableViewCell") + tableView.rowHeight = UITableView.automaticDimension + tableView.estimatedRowHeight = 250 + tableView.separatorStyle = .none + } - mainTable.dataSource = self - mainTable.delegate = self + func configureTable(for tableData: [ParentTableCellModel], parentSection: Section) { + self.tableData = tableData + self.parentSection = parentSection - mainTable.separatorStyle = .none - mainTable.backgroundColor = .clear + title = parentSection.sectionTitle } } -extension TableViewTemplateViewController: UITableViewDataSource { - func tableView(_: UITableView, numberOfRowsInSection _: Int) -> Int { - return tableData.count - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "ParentTableViewCell", for: indexPath) as! ParentTableViewCell +// MARK: - UITableViewDataSource - if let parentSection = parentSection { - cell.parentSection = parentSection - } +extension TableViewTemplateViewController { - cell.configureCell(for: tableData[indexPath.row]) + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: InfoChildTableViewCell.reuseIdentifier, for: indexPath) as! InfoChildTableViewCell - cell.backgroundColor = .clear - cell.selectionStyle = .none + cell.parentSection = parentSection + cell.configureCell(for: tableData[indexPath.section].section[indexPath.row]) return cell } } - -extension TableViewTemplateViewController: UITableViewDelegate {} diff --git a/Scribe/Components/UITableViewCells/AboutTableViewCell.swift b/Scribe/Components/UITableViewCells/AboutTableViewCell.swift new file mode 100644 index 00000000..7f84a81f --- /dev/null +++ b/Scribe/Components/UITableViewCells/AboutTableViewCell.swift @@ -0,0 +1,49 @@ +// +// Copyright (C) 2023 Scribe +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +import UIKit + +final class AboutTableViewCell: UITableViewCell { + + // MARK: - Constants + + static let reuseIdentifier = String(describing: InfoChildTableViewCell.self) + + // MARK: - Properties + + @IBOutlet var titleLabel: UILabel! + @IBOutlet var iconImageView: UIImageView! + + private var section: Section? + private var parentSection: Section? + + // MARK: - Functions + + func configureCell(for section: Section) { + selectionStyle = .none + + titleLabel.text = section.sectionTitle + + if let icon = section.imageString { + iconImageView.image = UIImage.availableIconImage(with: icon) + } else { + iconImageView.image = nil + } + + accessoryType = section.hasNestedNavigation ? .disclosureIndicator : .none + } +} diff --git a/Scribe/Components/UITableViewCells/AboutTableViewCell.xib b/Scribe/Components/UITableViewCells/AboutTableViewCell.xib new file mode 100644 index 00000000..011920ea --- /dev/null +++ b/Scribe/Components/UITableViewCells/AboutTableViewCell.xib @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Scribe/Extensions/UIEdgeInsetsExtensions.swift b/Scribe/Extensions/UIEdgeInsetsExtensions.swift new file mode 100644 index 00000000..f3dc76e3 --- /dev/null +++ b/Scribe/Extensions/UIEdgeInsetsExtensions.swift @@ -0,0 +1,27 @@ +// +// Copyright (C) 2023 Scribe +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +import UIKit + +extension UIEdgeInsets { + + // MARK: - Initialisation + + init(vertical: CGFloat, horizontal: CGFloat) { + self.init(top: vertical, left: horizontal, bottom: vertical, right: horizontal) + } +} diff --git a/Scribe/ParentTableCellModel.swift b/Scribe/ParentTableCellModel.swift index 99867cc9..036f6c87 100644 --- a/Scribe/ParentTableCellModel.swift +++ b/Scribe/ParentTableCellModel.swift @@ -12,10 +12,22 @@ struct ParentTableCellModel { } struct Section { + let sectionTitle: String - let imageString: String + let imageString: String? let hasToggle: Bool + let hasNestedNavigation: Bool let sectionState: SectionState + let shortDescription: String? + + init(sectionTitle: String, imageString: String? = nil, hasToggle: Bool = false, hasNestedNavigation: Bool = false, sectionState: SectionState, shortDescription: String? = nil) { + self.sectionTitle = sectionTitle + self.imageString = imageString + self.hasToggle = hasToggle + self.hasNestedNavigation = hasNestedNavigation + self.sectionState = sectionState + self.shortDescription = shortDescription + } } enum SectionState: Equatable { diff --git a/Scribe/Resources/Localizable.xcstrings b/Scribe/Resources/Localizable.xcstrings new file mode 100644 index 00000000..bd30f59d --- /dev/null +++ b/Scribe/Resources/Localizable.xcstrings @@ -0,0 +1,157 @@ +{ + "sourceLanguage" : "en", + "strings" : { + "about.title" : { + "comment" : "The title of the about tab", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "About" + } + } + } + }, + "settings.appSettings" : { + "comment" : "The title of the app settings section", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "App settings" + } + } + } + }, + "settings.appSettings.appLanguage" : { + "comment" : "Change the language of the Scribe App", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "App language" + } + } + } + }, + "settings.functionality" : { + "comment" : "The title of the functionality section", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Functionality" + } + } + } + }, + "settings.functionality.autoSuggestEmoji" : { + "comment" : "Toggles the suggestion of Emoji", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Autosuggest emojis" + } + } + } + }, + "settings.installedKeyboards" : { + "comment" : "The title of the installed keyboards section", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Select installed keyboard" + } + } + } + }, + "settings.layout" : { + "comment" : "The title of the layout section", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Layout" + } + } + } + }, + "settings.layout.autoSuggestEmoji.description" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Turn on emoji suggestions and completions for more expressive typing." + } + } + } + }, + "settings.layout.disableAccentCharacters" : { + "comment" : "Toggles accented characters for the selected keyboard", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Disable accent characters" + } + } + } + }, + "settings.layout.disableAccentCharacters.description" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Include accented letter keys on the primary keyboard layout." + } + } + } + }, + "settings.layout.periodAndComma" : { + "comment" : "Toggles period and commas for the selected keyboard", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Period and comma on ABC" + } + } + } + }, + "settings.layout.periodAndComma.description" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Include comma and period keys on the main keyboard for convenient typing." + } + } + } + }, + "settings.title" : { + "comment" : "The title for the settings screen", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Scribe Settings" + } + } + } + }, + "settings.title.backButton" : { + "comment" : "The back button's title for the settings screen", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Settings" + } + } + } + } + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/Scribe/SettingsTab/SettingsTableData.swift b/Scribe/SettingsTab/SettingsTableData.swift index 64e2fa27..e702bfeb 100644 --- a/Scribe/SettingsTab/SettingsTableData.swift +++ b/Scribe/SettingsTab/SettingsTableData.swift @@ -5,54 +5,56 @@ import Foundation struct SettingsTableData { - static var settingsTableData: [ParentTableCellModel] = [ + + static let settingsTableData: [ParentTableCellModel] = [ ParentTableCellModel( - headingTitle: "App language", + headingTitle: NSLocalizedString("settings.appSettings", comment: "The title of the app settings section"), section: [ - Section(sectionTitle: "System language", imageString: "globe", hasToggle: false, sectionState: .appLang), + Section(sectionTitle: NSLocalizedString("settings.appSettings.appLanguage", comment: "Change the language of the Scribe App"), imageString: "globe", hasToggle: false, sectionState: .appLang), ], hasDynamicData: nil ), ParentTableCellModel( - headingTitle: "Select installed keyboard", + headingTitle: NSLocalizedString("settings.installedKeyboards", comment: "The title of the installed keyboards section"), section: [ - // Section(sectionTitle: "All keyboards", imageString: "globe", hasToggle: false, sectionState: .specificLang("all")), + // Section(sectionTitle: "All keyboards", imageString: "globe", sectionState: .specificLang("all")), ], hasDynamicData: .installedKeyboards ), ] - static var languageSettingsData: [ParentTableCellModel] = [ + static let languageSettingsData: [ParentTableCellModel] = [ ParentTableCellModel( - headingTitle: "Layout", + headingTitle: NSLocalizedString("settings.layout", comment: "The title of the layout section"), section: [ Section( - sectionTitle: "Period and comma on ABC", - imageString: "info.circle", + sectionTitle: NSLocalizedString("settings.layout.periodAndComma", comment: "Toggles period and commas for the selected keyboard"), hasToggle: true, - sectionState: .none(.toggleCommaAndPeriod) + sectionState: .none(.toggleCommaAndPeriod), + shortDescription: NSLocalizedString("settings.layout.periodAndComma.description", comment: "") ), Section( - sectionTitle: "Disable accent characters", + sectionTitle: NSLocalizedString("settings.layout.disableAccentCharacters", comment: "Toggles accented characters for the selected keyboard"), imageString: "info.circle", hasToggle: true, - sectionState: .none(.toggleAccentCharacters) + sectionState: .none(.toggleAccentCharacters), + shortDescription: NSLocalizedString("settings.layout.disableAccentCharacters.description", comment: "") ), ], hasDynamicData: nil ), ParentTableCellModel( - headingTitle: "Functionality", + headingTitle: NSLocalizedString("settings.functionality", comment: "The title of the functionality section"), section: [ Section( - sectionTitle: "Autosuggest emojis", - imageString: "info.circle", + sectionTitle: NSLocalizedString("settings.functionality.autoSuggestEmoji", comment: "Toggles the suggestion of Emoji"), hasToggle: true, - sectionState: .none(.autosuggestEmojis) + sectionState: .none(.autosuggestEmojis), + shortDescription: NSLocalizedString("settings.layout.autoSuggestEmoji.description", comment: "") ), ], hasDynamicData: nil - ), + ) ] static func getInstalledKeyboardsSections() -> [Section] { @@ -75,8 +77,6 @@ struct SettingsTableData { for language in installedKeyboards { let newSection = Section( sectionTitle: language, - imageString: "globe", - hasToggle: false, sectionState: .specificLang(languagesAbbrDict[language]!) ) diff --git a/Scribe/SettingsTab/SettingsViewController.swift b/Scribe/SettingsTab/SettingsViewController.swift index d32686cf..956e525e 100644 --- a/Scribe/SettingsTab/SettingsViewController.swift +++ b/Scribe/SettingsTab/SettingsViewController.swift @@ -1,31 +1,51 @@ // -// SettingsViewController.swift +// Copyright (C) 2023 Scribe +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . // import UIKit -class SettingsViewController: UIViewController { +final class SettingsViewController: UIViewController { + + // MARK: - Constants + + private let sectionHeaderHeight: CGFloat = 32 + private let separatorInset = UIEdgeInsets(vertical: 16, horizontal: 16) + + // MARK: - Properties + @IBOutlet var footerFrame: UIView! @IBOutlet var footerButton: UIButton! - @IBOutlet var parentTable: UITableView! var tableData = SettingsTableData.settingsTableData + // MARK: - Functions + override func viewDidLoad() { super.viewDidLoad() - title = "Settings" - navigationItem.backButtonTitle = "Settings" - - let nib = UINib(nibName: "ParentTableViewCell", bundle: nil) - parentTable.register(nib, forCellReuseIdentifier: "ParentTableViewCell") + title = NSLocalizedString("settings.title", comment: "The title for the settings screen") + navigationItem.backButtonTitle = NSLocalizedString("settings.title.backButton", comment: "The back button's title for the settings screen") + parentTable.register(UINib(nibName: "InfoChildTableViewCell", bundle: nil), forCellReuseIdentifier: InfoChildTableViewCell.reuseIdentifier) parentTable.dataSource = self parentTable.delegate = self - - parentTable.separatorStyle = .none parentTable.backgroundColor = .clear + parentTable.sectionHeaderHeight = sectionHeaderHeight + parentTable.separatorInset = separatorInset setFooterButtonView() @@ -70,21 +90,91 @@ class SettingsViewController: UIViewController { } } +// MARK: - UITableViewDataSource + extension SettingsViewController: UITableViewDataSource { - func tableView(_: UITableView, numberOfRowsInSection _: Int) -> Int { - return tableData.count + + func numberOfSections(in tableView: UITableView) -> Int { + tableData.count + } + + func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int { + tableData[section].section.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "ParentTableViewCell", for: indexPath) as! ParentTableViewCell + let cell = tableView.dequeueReusableCell(withIdentifier: InfoChildTableViewCell.reuseIdentifier, for: indexPath) as! InfoChildTableViewCell - cell.configureCell(for: tableData[indexPath.row]) + let section = tableData[indexPath.section] + let setting = section.section[indexPath.row] - cell.backgroundColor = .clear - cell.selectionStyle = .none + cell.configureCell(for: setting) return cell } } -extension SettingsViewController: UITableViewDelegate {} +// MARK: - UITableViewDelegate + +extension SettingsViewController: UITableViewDelegate { + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let tableSection = tableData[indexPath.section] + let section = tableSection.section[indexPath.row] + + switch section.sectionState { + case .specificLang: + if let viewController = storyboard?.instantiateViewController(identifier: "TableViewTemplateViewController") as? TableViewTemplateViewController { + // Copy base settings + var data = SettingsTableData.languageSettingsData + + // Languages where we can disable accent keys. + let accentKeyLanguages: [String] = ["Swedish", "German", "Spanish"] + let accentKeyOptionIndex = SettingsTableData.languageSettingsData[0].section.firstIndex(where: { + s in s.sectionTitle.elementsEqual("Disable accent characters") + }) ?? -1 + + if accentKeyLanguages.firstIndex(of: section.sectionTitle) == nil && accentKeyOptionIndex != -1 { + // As there are no accent keys we can remove the `Disable accent characters` option. + let accentKeySettings = data[0].section.remove(at: accentKeyOptionIndex) + print(accentKeySettings) + } else if accentKeyLanguages.firstIndex(of: section.sectionTitle) != nil && accentKeyOptionIndex == -1 { + data[0].section.insert(Section( + sectionTitle: "Disable accent characters", + imageString: "info.circle", + hasToggle: true, + sectionState: .none(.toggleAccentCharacters) + ), at: 1) + } + + viewController.configureTable(for: data, parentSection: section) + + navigationController?.pushViewController(viewController, animated: true) + } + + default: break + } + + if let selectedIndexPath = tableView.indexPathForSelectedRow { + tableView.deselectRow(at: selectedIndexPath, animated: false) + } + } + + func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + let headerView: UIView + + if let reusableHeaderView = tableView.headerView(forSection: section) { + headerView = reusableHeaderView + } else { + headerView = UIView(frame: CGRect(x: 0, y: 0, width: parentTable.bounds.width, height: 32)) + } + + let label = UILabel(frame: CGRect(x: 0, y: 0, width: headerView.bounds.width, height: 32)) + label.text = tableData[section].headingTitle + label.font = UIFont.preferredFont(forTextStyle: .headline) + label.textColor = .black + headerView.addSubview(label) + + return headerView + } +} diff --git a/Scribe/Views/BaseTableViewController.swift b/Scribe/Views/BaseTableViewController.swift new file mode 100644 index 00000000..f5e28340 --- /dev/null +++ b/Scribe/Views/BaseTableViewController.swift @@ -0,0 +1,82 @@ +// +// Copyright (C) 2023 Scribe +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +import UIKit + +class BaseTableViewController: UITableViewController { + + // MARK: - Constants + + private let sectionHeaderHeight: CGFloat = 32 + private let separatorInset = UIEdgeInsets(vertical: 16, horizontal: 16) + + // MARK: - Properties + + var dataSet: [ParentTableCellModel] { + [] + } + + // MARK: - Functions + + override func viewDidLoad() { + super.viewDidLoad() + + tableView.sectionHeaderHeight = sectionHeaderHeight + tableView.register(UINib(nibName: "InfoChildTableViewCell", bundle: nil), forCellReuseIdentifier: "InfoChildTableViewCell") + tableView.separatorInset = separatorInset + } +} + +// MARK: - UITableViewDataSource + +extension BaseTableViewController { + + override func numberOfSections(in tableView: UITableView) -> Int { + dataSet.count + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + dataSet[section].section.count + } +} + +// MARK: - UITableViewDelegate + +extension BaseTableViewController { + + override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + headerView(for: section) + } + + private func headerView(for section: Int) -> UIView? { + let headerView: UIView + + if let reusableHeaderView = tableView.headerView(forSection: section) { + headerView = reusableHeaderView + } else { + headerView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.bounds.width, height: sectionHeaderHeight)) + } + + let label = UILabel(frame: CGRect(x: 0, y: 0, width: headerView.bounds.width, height: sectionHeaderHeight)) + label.text = dataSet[section].headingTitle + label.font = UIFont.preferredFont(forTextStyle: .headline) + label.textColor = .black + headerView.addSubview(label) + + return headerView + } +} diff --git a/Scribe/fr.lproj/AppScreen.strings b/Scribe/fr.lproj/AppScreen.strings new file mode 100644 index 00000000..d6ac91ee --- /dev/null +++ b/Scribe/fr.lproj/AppScreen.strings @@ -0,0 +1,27 @@ + +/* Class = "UIButton"; normalTitle = "Button"; ObjectID = "3gB-ix-8Gq"; */ +"3gB-ix-8Gq.normalTitle" = "Button"; + +/* Class = "UITabBarItem"; title = "Installation"; ObjectID = "HNz-5D-1T0"; */ +"HNz-5D-1T0.title" = "Installation"; + +/* Class = "UITabBarItem"; title = "Settings"; ObjectID = "JtD-kc-GDx"; */ +"JtD-kc-GDx.title" = "Settings"; + +/* Class = "UITextView"; text = "Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda."; ObjectID = "Q8I-Gg-Baj"; */ +"Q8I-Gg-Baj.text" = "Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda."; + +/* Class = "UILabel"; text = "Label"; ObjectID = "QQb-Sb-zfQ"; */ +"QQb-Sb-zfQ.text" = "Label"; + +/* Class = "UITabBarItem"; title = "About"; ObjectID = "U3y-iM-H0G"; */ +"U3y-iM-H0G.title" = "About"; + +/* Class = "UITextView"; text = "Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda."; ObjectID = "ZcW-Aa-aDz"; */ +"ZcW-Aa-aDz.text" = "Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda."; + +/* Class = "UIButton"; configuration.title = "Button"; ObjectID = "cWe-iO-9F6"; */ +"cWe-iO-9F6.configuration.title" = "Button"; + +/* Class = "UIButton"; normalTitle = "Button"; ObjectID = "cWe-iO-9F6"; */ +"cWe-iO-9F6.normalTitle" = "Button"; diff --git a/Scribe/fr.lproj/LaunchScreen.strings b/Scribe/fr.lproj/LaunchScreen.strings new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/Scribe/fr.lproj/LaunchScreen.strings @@ -0,0 +1 @@ +