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.