diff --git a/CHANGELOG.md b/CHANGELOG.md index 82b32c78..b828ffc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 1.0 RC4 1271 (2018-12-04) + +### Changed + +- Reorganized credits page. + ## 1.0 RC3 1258 (2018-11-15) ### Changed diff --git a/Passepartout-iOS/Global/Theme.swift b/Passepartout-iOS/Global/Theme.swift index fc046b0f..25d4113a 100644 --- a/Passepartout-iOS/Global/Theme.swift +++ b/Passepartout-iOS/Global/Theme.swift @@ -136,6 +136,12 @@ extension UITextField { } } +extension UIActivityIndicatorView { + func applyAccent(_ theme: Theme) { + color = theme.palette.colorAccent1 + } +} + // XXX: status bar is broken extension MFMailComposeViewController { func apply(_ theme: Theme) { diff --git a/Passepartout-iOS/Scenes/Organizer/CreditsViewController.swift b/Passepartout-iOS/Scenes/Organizer/CreditsViewController.swift new file mode 100644 index 00000000..6186fe41 --- /dev/null +++ b/Passepartout-iOS/Scenes/Organizer/CreditsViewController.swift @@ -0,0 +1,115 @@ +// +// CreditsViewController.swift +// Passepartout-iOS +// +// Created by Davide De Rosa on 11/26/18. +// Copyright (c) 2018 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout 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. +// +// Passepartout 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 Passepartout. If not, see . +// + +import UIKit + +class CreditsViewController: UITableViewController, TableModelHost { + private let licenses = AppConstants.License.all + + private let notices = AppConstants.Notice.all + + // MARK: TableModelHost + + var model: TableModel = TableModel() + + func reloadModel() { + model.add(.licenses) + model.add(.notices) + + model.setHeader(L10n.Credits.Sections.Licenses.header, for: .licenses) + model.setHeader(L10n.Credits.Sections.Notices.header, for: .notices) + + model.set(.license, count: licenses.count, in: .licenses) + model.set(.notice, count: notices.count, in: .notices) + } + + // MARK: UIViewController + + override func viewDidLoad() { + super.viewDidLoad() + + title = L10n.Credits.title + reloadModel() + } + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + guard let vc = segue.destination as? LabelViewController else { + return + } + guard let cell = sender as? SettingTableViewCell, let indexPath = tableView.indexPath(for: cell) else { + return + } + vc.title = cell.leftText + switch model.row(at: indexPath) { + case .license: + vc.license = licenses[indexPath.row] + + case .notice: + vc.text = notices[indexPath.row].statement + } + } +} + +extension CreditsViewController { + enum SectionType: Int { + case licenses + + case notices + } + + enum RowType: Int { + case license + + case notice + } + + override func numberOfSections(in tableView: UITableView) -> Int { + return model.count + } + + override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + return model.header(for: section) + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return model.count(for: section) + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = Cells.setting.dequeue(from: tableView, for: indexPath) + switch model.row(at: indexPath) { + case .license: + let obj = licenses[indexPath.row] + cell.leftText = obj.name + cell.rightText = obj.type + + case .notice: + let obj = notices[indexPath.row] + cell.leftText = obj.name + cell.rightText = nil + } + return cell + } +} diff --git a/Passepartout-iOS/Scenes/Organizer/LabelViewController.swift b/Passepartout-iOS/Scenes/Organizer/LabelViewController.swift index e5344dea..31950336 100644 --- a/Passepartout-iOS/Scenes/Organizer/LabelViewController.swift +++ b/Passepartout-iOS/Scenes/Organizer/LabelViewController.swift @@ -28,10 +28,14 @@ import UIKit class LabelViewController: UIViewController { @IBOutlet private weak var scrollView: UIScrollView? + @IBOutlet private weak var activity: UIActivityIndicatorView? + @IBOutlet private weak var label: UILabel? var text: String? + var license: AppConstants.License? + override func awakeFromNib() { super.awakeFromNib() @@ -41,10 +45,43 @@ class LabelViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - title = L10n.Credits.title - label?.text = text - + activity?.hidesWhenStopped = true + activity?.applyAccent(Theme.current) scrollView?.applyPrimaryBackground(Theme.current) label?.applyLight(Theme.current) + + if let license = license { + + // try cache first + if let cachedContent = AppConstants.License.cachedContent[license.name] { + label?.text = cachedContent + return + } + + label?.text = nil + activity?.startAnimating() + + DispatchQueue(label: LabelViewController.description(), qos: .background).async { [weak self] in + let content: String + let couldFetch: Bool + do { + content = try String(contentsOf: license.url) + couldFetch = true + } catch { + content = L10n.Label.License.error + couldFetch = false + } + DispatchQueue.main.async { + self?.label?.text = content + self?.activity?.stopAnimating() + + if couldFetch { + AppConstants.License.cachedContent[license.name] = content + } + } + } + } else { + label?.text = text + } } } diff --git a/Passepartout-iOS/Scenes/Organizer/VersionViewController.swift b/Passepartout-iOS/Scenes/Organizer/VersionViewController.swift index 0f0993fe..d412bbe7 100644 --- a/Passepartout-iOS/Scenes/Organizer/VersionViewController.swift +++ b/Passepartout-iOS/Scenes/Organizer/VersionViewController.swift @@ -65,14 +65,4 @@ class VersionViewController: UIViewController { @IBAction private func visitChangelog() { UIApplication.shared.open(AppConstants.URLs.changelog, options: [:], completionHandler: nil) } - - override func prepare(for segue: UIStoryboardSegue, sender: Any?) { - guard let vc = segue.destination as? LabelViewController else { - return - } - vc.title = L10n.Credits.title - var notices = AppConstants.Notices.all - notices.insert(L10n.Credits.Labels.thirdParties, at: 0) - vc.text = notices.joined(separator: "\n\n") - } } diff --git a/Passepartout-iOS/en.lproj/Organizer.storyboard b/Passepartout-iOS/en.lproj/Organizer.storyboard index a2ef1afc..94f21074 100644 --- a/Passepartout-iOS/en.lproj/Organizer.storyboard +++ b/Passepartout-iOS/en.lproj/Organizer.storyboard @@ -1,11 +1,11 @@ - + - + @@ -13,13 +13,13 @@ - + - + @@ -80,13 +80,13 @@ - + - + @@ -103,7 +103,7 @@ - + @@ -164,13 +164,13 @@ - + - + @@ -250,13 +250,13 @@ - + - + @@ -319,7 +319,7 @@ - + @@ -367,7 +367,7 @@ - + @@ -433,6 +433,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -452,13 +499,13 @@ - + - + @@ -506,7 +553,7 @@ - + @@ -552,10 +599,15 @@ + + + + + @@ -563,13 +615,14 @@ + - + diff --git a/Passepartout.xcodeproj/project.pbxproj b/Passepartout.xcodeproj/project.pbxproj index c58abb03..75cd62e6 100644 --- a/Passepartout.xcodeproj/project.pbxproj +++ b/Passepartout.xcodeproj/project.pbxproj @@ -86,6 +86,7 @@ 0EF56BBB2185AC8500B0C8AB /* SwiftGen+Segues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EF56BBA2185AC8500B0C8AB /* SwiftGen+Segues.swift */; }; 0EF5CF252141CE58004FF1BD /* HUD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EF5CF242141CE58004FF1BD /* HUD.swift */; }; 0EF5CF292141F31F004FF1BD /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E4FD7ED20D539A0002221FF /* Utils.swift */; }; + 0EFBFAC121AC464800887A8C /* CreditsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFBFAC021AC464800887A8C /* CreditsViewController.swift */; }; 0EFD943E215BE10800529B64 /* IssueReporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFD943D215BE10800529B64 /* IssueReporter.swift */; }; 0EFD9440215BED8E00529B64 /* LabelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFD943F215BED8E00529B64 /* LabelViewController.swift */; }; 390EEC911382C4814FB97475 /* Pods_Passepartout_iOS_Tunnel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D0C619B826C7140001C0F32 /* Pods_Passepartout_iOS_Tunnel.framework */; }; @@ -211,6 +212,7 @@ 0EE3BBB1215ED3A900F30952 /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = ""; }; 0EF56BBA2185AC8500B0C8AB /* SwiftGen+Segues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SwiftGen+Segues.swift"; sourceTree = ""; }; 0EF5CF242141CE58004FF1BD /* HUD.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HUD.swift; sourceTree = ""; }; + 0EFBFAC021AC464800887A8C /* CreditsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreditsViewController.swift; sourceTree = ""; }; 0EFD943D215BE10800529B64 /* IssueReporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssueReporter.swift; sourceTree = ""; }; 0EFD943F215BED8E00529B64 /* LabelViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelViewController.swift; sourceTree = ""; }; 28B2E6590DE79C3B403348DC /* Pods-Passepartout-iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Passepartout-iOS.debug.xcconfig"; path = "Target Support Files/Pods-Passepartout-iOS/Pods-Passepartout-iOS.debug.xcconfig"; sourceTree = ""; }; @@ -341,6 +343,7 @@ isa = PBXGroup; children = ( 0EE3BBB1215ED3A900F30952 /* AboutViewController.swift */, + 0EFBFAC021AC464800887A8C /* CreditsViewController.swift */, 0EB67D6A2184581E00BA6200 /* ImportedHostsViewController.swift */, 0EFD943F215BED8E00529B64 /* LabelViewController.swift */, 0EBE3A78213C4E5400BFA2F5 /* OrganizerViewController.swift */, @@ -849,6 +852,7 @@ 0E6BE13F20CFBAB300A6DD36 /* DebugLogViewController.swift in Sources */, 0E89DFC8213E8FC500741BA1 /* SessionProxy+Communication.swift in Sources */, 0ED38AEA214054A50004D387 /* OptionViewController.swift in Sources */, + 0EFBFAC121AC464800887A8C /* CreditsViewController.swift in Sources */, 0EFD943E215BE10800529B64 /* IssueReporter.swift in Sources */, 0EB60FDA2111136E00AD27F3 /* UITextView+Search.swift in Sources */, 0EB67D6B2184581E00BA6200 /* ImportedHostsViewController.swift in Sources */, diff --git a/Passepartout/Resources/en.lproj/Localizable.strings b/Passepartout/Resources/en.lproj/Localizable.strings index 583757bc..c676bbc5 100644 --- a/Passepartout/Resources/en.lproj/Localizable.strings +++ b/Passepartout/Resources/en.lproj/Localizable.strings @@ -206,4 +206,7 @@ "version.buttons.credits" = "CREDITS"; "credits.title" = "Credits"; -"credits.labels.third_parties" = "The logo is taken from the awesome Circle Icons set by Nick Roach."; +"credits.sections.licenses.header" = "Licenses"; +"credits.sections.notices.header" = "Notices"; + +"label.license.error" = "Unable to download full license content."; diff --git a/Passepartout/Sources/AppConstants.swift b/Passepartout/Sources/AppConstants.swift index a3002ba3..a226d3fb 100644 --- a/Passepartout/Sources/AppConstants.swift +++ b/Passepartout/Sources/AppConstants.swift @@ -169,23 +169,69 @@ class AppConstants { static let api = githubRaw(repo: "passepartout-api") } - class Notices { - private static let pia = "PIATunnel - Copyright (c) 2018-Present Private Internet Access" + struct License { + let name: String - private static let swiftyBeaver = "SwiftyBeaver - Copyright (c) 2015 Sebastian Kreutzberger" + let type: String - private static let progressHUD = "MBProgressHUD - Copyright (c) 2009-2016 Matej Bukovinski" + let url: URL + + init(_ name: String, _ type: String, _ urlString: String) { + self.name = name + self.type = type + url = URL(string: urlString)! + } - private static let openVPN = "© 2002-2018 OpenVPN Inc. - OpenVPN is a registered trademark of OpenVPN Inc." + static let all: [License] = [ + License( + "MBProgressHUD", + "MIT", + "https://raw.githubusercontent.com/jdg/MBProgressHUD/master/LICENSE" + ), + License( + "OpenSSL", + "OpenSSL", + "https://www.openssl.org/source/license.txt" + ), + License( + "PIATunnel", + "MIT", + "https://raw.githubusercontent.com/pia-foss/tunnel-apple/master/LICENSE" + ), + License( + "SwiftGen", + "MIT", + "https://raw.githubusercontent.com/SwiftGen/SwiftGen/master/LICENSE" + ), + License( + "SwiftyBeaver", + "MIT", + "https://raw.githubusercontent.com/SwiftyBeaver/SwiftyBeaver/master/LICENSE" + ) + ] - private static let openSSL = "This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. https://www.openssl.org/" + static var cachedContent: [String: String] = [:] + } + + struct Notice { + let name: String - static let all: [String] = [ - pia, - swiftyBeaver, - progressHUD, - openVPN, - openSSL + let statement: String + + init(_ name: String, _ statement: String) { + self.name = name + self.statement = statement + } + + static let all: [Notice] = [ + Notice( + "Circle Icons", + "The logo is taken from the awesome Circle Icons set by Nick Roach." + ), + Notice( + "OpenVPN", + "© 2002-2018 OpenVPN Inc. - OpenVPN is a registered trademark of OpenVPN Inc." + ) ] } } diff --git a/Passepartout/Sources/SwiftGen+Strings.swift b/Passepartout/Sources/SwiftGen+Strings.swift index 01bb24dd..cf2b1ef9 100644 --- a/Passepartout/Sources/SwiftGen+Strings.swift +++ b/Passepartout/Sources/SwiftGen+Strings.swift @@ -192,9 +192,15 @@ internal enum L10n { internal enum Credits { /// Credits internal static let title = L10n.tr("Localizable", "credits.title") - internal enum Labels { - /// The logo is taken from the awesome Circle Icons set by Nick Roach. - internal static let thirdParties = L10n.tr("Localizable", "credits.labels.third_parties") + internal enum Sections { + internal enum Licenses { + /// Licenses + internal static let header = L10n.tr("Localizable", "credits.sections.licenses.header") + } + internal enum Notices { + /// Notices + internal static let header = L10n.tr("Localizable", "credits.sections.notices.header") + } } } @@ -287,6 +293,13 @@ internal enum L10n { } } + internal enum Label { + internal enum License { + /// Unable to download full license content. + internal static let error = L10n.tr("Localizable", "label.license.error") + } + } + internal enum Organizer { internal enum Alerts { internal enum AddHost { diff --git a/README.md b/README.md index 1cf38215..c3b590df 100644 --- a/README.md +++ b/README.md @@ -110,9 +110,12 @@ By contributing to this project you are agreeing to the terms stated in the [Con The logo is taken from the awesome Circle Icons set by Nick Roach. -- PIATunnel - © 2018-Present Private Internet Access -- SwiftyBeaver - © 2015 Sebastian Kreutzberger - MBProgressHUD - © 2009-2016 Matej Bukovinski +- PIATunnel - © 2018-Present Private Internet Access +- SwiftGen - © 2018 SwiftGen +- SwiftyBeaver - © 2015 Sebastian Kreutzberger + +This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. ([https://www.openssl.org/][dep-openssl]) © 2002-2018 OpenVPN Inc. - OpenVPN is a registered trademark of OpenVPN Inc.