Merge branch 'upstream/master'

This commit is contained in:
Jeremy Beker 2019-06-16 13:15:50 -04:00
commit 79c40ddd2d
No known key found for this signature in database
GPG Key ID: CD5EE767A4A34FD0
9 changed files with 265 additions and 53 deletions

View File

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>5.0a3</string>
<string>5.0a4</string>
<key>CFBundleURLTypes</key>
<array>
<dict>

View File

@ -49,6 +49,8 @@
5183CCE9226F68D90010922C /* AccountRefreshTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE7226F68D90010922C /* AccountRefreshTimer.swift */; };
5183CCED22711DCE0010922C /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5183CCEC22711DCE0010922C /* Settings.storyboard */; };
5183CCEF227125970010922C /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCEE227125970010922C /* SettingsViewController.swift */; };
5194B5EE22B6965300144881 /* SettingsSubscriptionsImportDocumentPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5194B5ED22B6965300144881 /* SettingsSubscriptionsImportDocumentPickerView.swift */; };
5194B5F222B69FCC00144881 /* SettingsSubscriptionsExportDocumentPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5194B5F122B69FCC00144881 /* SettingsSubscriptionsExportDocumentPickerView.swift */; };
519B8D332143397200FA689C /* SharingServiceDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519B8D322143397200FA689C /* SharingServiceDelegate.swift */; };
51C451A9226377C200C03939 /* ArticlesDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8407167F2262A61100344432 /* ArticlesDatabase.framework */; };
51C451AA226377C200C03939 /* ArticlesDatabase.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8407167F2262A61100344432 /* ArticlesDatabase.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@ -318,6 +320,7 @@
D5F4EDB5200744A700B9E363 /* ScriptingObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F4EDB4200744A700B9E363 /* ScriptingObject.swift */; };
D5F4EDB720074D6500B9E363 /* Feed+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F4EDB620074D6500B9E363 /* Feed+Scriptability.swift */; };
D5F4EDB920074D7C00B9E363 /* Folder+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F4EDB820074D7C00B9E363 /* Folder+Scriptability.swift */; };
DF999FF722B5AEFA0064B687 /* SafariView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF999FF622B5AEFA0064B687 /* SafariView.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -693,6 +696,8 @@
5183CCE7226F68D90010922C /* AccountRefreshTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountRefreshTimer.swift; sourceTree = "<group>"; };
5183CCEC22711DCE0010922C /* Settings.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = "<group>"; };
5183CCEE227125970010922C /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = "<group>"; };
5194B5ED22B6965300144881 /* SettingsSubscriptionsImportDocumentPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSubscriptionsImportDocumentPickerView.swift; sourceTree = "<group>"; };
5194B5F122B69FCC00144881 /* SettingsSubscriptionsExportDocumentPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSubscriptionsExportDocumentPickerView.swift; sourceTree = "<group>"; };
519B8D322143397200FA689C /* SharingServiceDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingServiceDelegate.swift; sourceTree = "<group>"; };
51C4524E226506F400C03939 /* UIStoryboard-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStoryboard-Extensions.swift"; sourceTree = "<group>"; };
51C45250226506F400C03939 /* String-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String-Extensions.swift"; sourceTree = "<group>"; };
@ -926,6 +931,7 @@
D5F4EDB4200744A700B9E363 /* ScriptingObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptingObject.swift; sourceTree = "<group>"; };
D5F4EDB620074D6500B9E363 /* Feed+Scriptability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Feed+Scriptability.swift"; sourceTree = "<group>"; };
D5F4EDB820074D7C00B9E363 /* Folder+Scriptability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Folder+Scriptability.swift"; sourceTree = "<group>"; };
DF999FF622B5AEFA0064B687 /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -1045,12 +1051,22 @@
51F772EC22B2789B0087D9D1 /* SettingsDetailAccountView.swift */,
510D707F22B02A5F004E8F65 /* SettingsFeedbinAccountView.swift */,
510D707D22B02A4B004E8F65 /* SettingsLocalAccountView.swift */,
5194B5F122B69FCC00144881 /* SettingsSubscriptionsExportDocumentPickerView.swift */,
5194B5ED22B6965300144881 /* SettingsSubscriptionsImportDocumentPickerView.swift */,
51F35D0822AFD4760003CE1B /* SettingsView.swift */,
51F35CFD22AFD0350003CE1B /* UIKit */,
);
path = Settings;
sourceTree = "<group>";
};
5194B5E222B693EC00144881 /* Wrappers */ = {
isa = PBXGroup;
children = (
DF999FF622B5AEFA0064B687 /* SafariView.swift */,
);
path = Wrappers;
sourceTree = "<group>";
};
51C45245226506C800C03939 /* Extensions */ = {
isa = PBXGroup;
children = (
@ -1690,6 +1706,7 @@
5183CCEB227117C70010922C /* Settings */,
5183CCDB226F1EEB0010922C /* Progress */,
51C45245226506C800C03939 /* Extensions */,
5194B5E222B693EC00144881 /* Wrappers */,
84C9FC9A2262A1A900D921D6 /* Resources */,
);
path = iOS;
@ -2365,10 +2382,13 @@
51C452762265091600C03939 /* MasterTimelineViewController.swift in Sources */,
5183CCE9226F68D90010922C /* AccountRefreshTimer.swift in Sources */,
51C452882265093600C03939 /* AddFeedViewController.swift in Sources */,
DF999FF722B5AEFA0064B687 /* SafariView.swift in Sources */,
51C4529B22650A1000C03939 /* FaviconDownloader.swift in Sources */,
5183CCE3226F314C0010922C /* ProgressTableViewController.swift in Sources */,
512E09012268907400BDCFDD /* MasterFeedTableViewSectionHeader.swift in Sources */,
5194B5F222B69FCC00144881 /* SettingsSubscriptionsExportDocumentPickerView.swift in Sources */,
51C45268226508F600C03939 /* MasterFeedUnreadCountView.swift in Sources */,
5194B5EE22B6965300144881 /* SettingsSubscriptionsImportDocumentPickerView.swift in Sources */,
5183CCD0226E1E880010922C /* NonIntrinsicLabel.swift in Sources */,
51C4529F22650A1900C03939 /* AuthorAvatarDownloader.swift in Sources */,
51E595AD228E1C2100FCC42B /* AddAccountViewController.swift in Sources */,

View File

@ -9,10 +9,12 @@
import SwiftUI
import Combine
import Account
import RSWeb
struct SettingsDetailAccountView : View {
@ObjectBinding var viewModel: ViewModel
@State private var verifyDelete = false
@State private var showFeedbinCredentials = false
var body: some View {
List {
@ -26,15 +28,19 @@ struct SettingsDetailAccountView : View {
Text("Active")
}
}
Section {
HStack {
Spacer()
Button(action: {
}) {
Text("Credentials")
if viewModel.isCreditialsAvailable {
Section {
HStack {
Spacer()
Button(action: {
self.showFeedbinCredentials = true
}) {
Text("Credentials")
}
.presentation(showFeedbinCredentials ? feedbinCredentialsModal : nil)
.onDisappear() { self.showFeedbinCredentials = false }
Spacer()
}
Spacer()
}
}
if viewModel.isDeletable {
@ -62,6 +68,11 @@ struct SettingsDetailAccountView : View {
}
var feedbinCredentialsModal: Modal {
let feedbinViewModel = SettingsFeedbinAccountView.ViewModel(account: viewModel.account)
return Modal(SettingsFeedbinAccountView(viewModel: feedbinViewModel))
}
class ViewModel: BindableObject {
let didChange = PassthroughSubject<ViewModel, Never>()
let account: Account
@ -94,6 +105,10 @@ struct SettingsDetailAccountView : View {
}
}
var isCreditialsAvailable: Bool {
return account.type != .onMyMac
}
var isDeletable: Bool {
return AccountManager.shared.defaultAccount != account
}

View File

@ -16,8 +16,7 @@ struct SettingsFeedbinAccountView : View {
@ObjectBinding var viewModel: ViewModel
@State var busy: Bool = false
@State var error: Text = Text("")
var account: Account? = nil
var body: some View {
NavigationView {
List {
@ -25,15 +24,15 @@ struct SettingsFeedbinAccountView : View {
SettingsAccountLabelView(accountImage: "accountFeedbin", accountLabel: "Feedbin").padding()
) {
HStack {
Spacer()
TextField($viewModel.email, placeholder: Text("Email"))
.textContentType(.username)
Spacer()
Text("Email:")
Divider()
TextField($viewModel.email)
.textContentType(.username)
}
HStack {
Spacer()
SecureField($viewModel.password, placeholder: Text("Password"))
Spacer()
Text("Password:")
Divider()
SecureField($viewModel.password)
}
}
Section(footer:
@ -46,7 +45,11 @@ struct SettingsFeedbinAccountView : View {
HStack {
Spacer()
Button(action: { self.addAccount() }) {
Text("Add Account")
if viewModel.isUpdate {
Text("Update Account")
} else {
Text("Add Account")
}
}
.disabled(!viewModel.isValid)
Spacer()
@ -65,7 +68,8 @@ struct SettingsFeedbinAccountView : View {
private func addAccount() {
busy = true
error = Text("")
let emailAddress = viewModel.email.trimmingCharacters(in: .whitespaces)
let credentials = Credentials.basic(username: emailAddress, password: viewModel.password)
@ -80,11 +84,11 @@ struct SettingsFeedbinAccountView : View {
var newAccount = false
let workAccount: Account
if self.account == nil {
if self.viewModel.account == nil {
workAccount = AccountManager.shared.createAccount(type: .feedbin)
newAccount = true
} else {
workAccount = self.account!
workAccount = self.viewModel.account!
}
do {
@ -122,6 +126,18 @@ struct SettingsFeedbinAccountView : View {
class ViewModel: BindableObject {
let didChange = PassthroughSubject<ViewModel, Never>()
var account: Account? = nil
init() {
}
init(account: Account) {
self.account = account
if case .basic(let username, let password) = try? account.retrieveBasicCredentials() {
self.email = username
self.password = password
}
}
var email: String = "" {
didSet {
@ -134,6 +150,10 @@ struct SettingsFeedbinAccountView : View {
}
}
var isUpdate: Bool {
return account != nil
}
var isValid: Bool {
return !email.isEmpty && !password.isEmpty
}
@ -144,7 +164,7 @@ struct SettingsFeedbinAccountView : View {
#if DEBUG
struct SettingsFeedbinAccountView_Previews : PreviewProvider {
static var previews: some View {
SettingsFeedbinAccountView(viewModel: SettingsFeedbinAccountView.ViewModel())
SettingsFeedbinAccountView(viewModel: SettingsFeedbinAccountView.ViewModel())
}
}
#endif

View File

@ -20,9 +20,9 @@ struct SettingsLocalAccountView : View {
SettingsAccountLabelView(accountImage: "accountLocal", accountLabel: Account.defaultLocalAccountName).padding()
) {
HStack {
Spacer()
TextField($name, placeholder: Text("Name (Optional)"))
Spacer()
Text("Name")
Divider()
TextField($name, placeholder: Text("(Optional)"))
}
}
Section {

View File

@ -0,0 +1,31 @@
//
// SettingsSubscriptionsExportDocumentPickerView.swift
// NetNewsWire-iOS
//
// Created by Maurice Parker on 6/16/19.
// Copyright © 2019 Ranchero Software. All rights reserved.
//
import SwiftUI
import Account
struct SettingsSubscriptionsExportDocumentPickerView : UIViewControllerRepresentable {
var account: Account
func makeUIViewController(context: UIViewControllerRepresentableContext<SettingsSubscriptionsExportDocumentPickerView>) -> UIDocumentPickerViewController {
let accountName = account.nameForDisplay.replacingOccurrences(of: " ", with: "").trimmingCharacters(in: .whitespaces)
let filename = "Subscriptions-\(accountName).opml"
let tempFile = FileManager.default.temporaryDirectory.appendingPathComponent(filename)
let opmlString = OPMLExporter.OPMLString(with: account, title: filename)
try? opmlString.write(to: tempFile, atomically: true, encoding: String.Encoding.utf8)
return UIDocumentPickerViewController(url: tempFile, in: .exportToService)
}
func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: UIViewControllerRepresentableContext<SettingsSubscriptionsExportDocumentPickerView>) {
//
}
}

View File

@ -0,0 +1,43 @@
//
// SettingsSubscriptionsImportDocumentPickerView.swift
// NetNewsWire-iOS
//
// Created by Maurice Parker on 6/16/19.
// Copyright © 2019 Ranchero Software. All rights reserved.
//
import SwiftUI
import Account
struct SettingsSubscriptionsImportDocumentPickerView : UIViewControllerRepresentable {
var account: Account
func makeUIViewController(context: UIViewControllerRepresentableContext<SettingsSubscriptionsImportDocumentPickerView>) -> UIDocumentPickerViewController {
let docPicker = UIDocumentPickerViewController(documentTypes: ["public.xml", "org.opml.opml"], in: .import)
docPicker.delegate = context.coordinator
return docPicker
}
func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: UIViewControllerRepresentableContext<SettingsSubscriptionsImportDocumentPickerView>) {
//
}
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
class Coordinator : NSObject, UIDocumentPickerDelegate {
var parent: SettingsSubscriptionsImportDocumentPickerView
init(_ view: SettingsSubscriptionsImportDocumentPickerView) {
self.parent = view
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
for url in urls {
parent.account.importOPML(url) { result in}
}
}
}
}

View File

@ -10,9 +10,15 @@ import SwiftUI
import Combine
import Account
struct SettingsView : View {
@ObjectBinding var viewModel: ViewModel
@State var subscriptionsImportAccounts: ActionSheet? = nil
@State var subscriptionsImportDocumentPicker: Modal? = nil
@State var subscriptionsExportAccounts: ActionSheet? = nil
@State var subscriptionsExportDocumentPicker: Modal? = nil
var body: some View {
NavigationView {
List {
@ -31,30 +37,16 @@ struct SettingsView : View {
Section(header: Text("ABOUT")) {
Text("About NetNewsWire")
Button(action: {
UIApplication.shared.open(URL(string: "https://ranchero.com/netnewswire/")!, options: [:])
}) {
Text("Website")
}
Button(action: {
UIApplication.shared.open(URL(string: "https://github.com/brentsimmons/NetNewsWire")!, options: [:])
}) {
Text("Github Repository")
}
Button(action: {
UIApplication.shared.open(URL(string: "https://github.com/brentsimmons/NetNewsWire/issues")!, options: [:])
}) {
Text("Bug Tracker")
}
Button(action: {
UIApplication.shared.open(URL(string: "https://github.com/brentsimmons/NetNewsWire/tree/master/Technotes")!, options: [:])
}) {
Text("Technotes")
}
PresentationButton(Text("Website"), destination: SafariView(url: URL(string: "https://ranchero.com/netnewswire/")!))
PresentationButton(Text("Github Repository"), destination: SafariView(url: URL(string: "https://github.com/brentsimmons/NetNewsWire")!))
PresentationButton(Text("Bug Tracker"), destination: SafariView(url: URL(string: "https://github.com/brentsimmons/NetNewsWire/issues")!))
PresentationButton(Text("Technotes"), destination: SafariView(url: URL(string: "https://github.com/brentsimmons/NetNewsWire/tree/master/Technotes")!))
PresentationButton(Text("How to Support NetNewsWire"), destination: SafariView(url: URL(string: "https://github.com/brentsimmons/NetNewsWire/blob/master/Technotes/HowToSupportNetNewsWire.markdown")!))
Text("Add NetNewsWire News Feed")
@ -76,10 +68,23 @@ struct SettingsView : View {
Text(interval.description()).tag(interval)
}
}
Text("Import Subscriptions...")
Text("Export Subscriptions...")
Button(action: {
self.subscriptionsImportAccounts = self.createSubscriptionsImportAccounts
}) {
Text("Import Subscriptions...")
}
.presentation(subscriptionsImportAccounts)
.presentation(subscriptionsImportDocumentPicker)
Button(action: {
self.subscriptionsExportAccounts = self.createSubscriptionsExportAccounts
}) {
Text("Export Subscriptions...")
}
.presentation(subscriptionsExportAccounts)
.presentation(subscriptionsExportDocumentPicker)
}
.foregroundColor(.primary)
}
.listStyle(.grouped)
.navigationBarTitle(Text("Settings"), displayMode: .inline)
@ -87,6 +92,32 @@ struct SettingsView : View {
}
}
var createSubscriptionsImportAccounts: ActionSheet {
var buttons = [ActionSheet.Button]()
for account in viewModel.accounts {
let button = ActionSheet.Button.default(Text(verbatim: account.nameForDisplay)) {
self.subscriptionsImportAccounts = nil
self.subscriptionsImportDocumentPicker = Modal(SettingsSubscriptionsImportDocumentPickerView(account: account))
}
buttons.append(button)
}
buttons.append(.cancel { self.subscriptionsImportAccounts = nil })
return ActionSheet(title: Text("Import Subscriptions..."), message: Text("Select the account to import your OPML file into."), buttons: buttons)
}
var createSubscriptionsExportAccounts: ActionSheet {
var buttons = [ActionSheet.Button]()
for account in viewModel.accounts {
let button = ActionSheet.Button.default(Text(verbatim: account.nameForDisplay)) {
self.subscriptionsExportAccounts = nil
self.subscriptionsExportDocumentPicker = Modal(SettingsSubscriptionsExportDocumentPickerView(account: account))
}
buttons.append(button)
}
buttons.append(.cancel { self.subscriptionsExportAccounts = nil })
return ActionSheet(title: Text("Export Subscriptions..."), message: Text("Select the account to export out of."), buttons: buttons)
}
class ViewModel: BindableObject {
let didChange = PassthroughSubject<ViewModel, Never>()

View File

@ -0,0 +1,52 @@
//
// SafariView.swift
// NetNewsWire-iOS
//
// Created by Stuart Breckenridge on 16/6/19.
// Copyright © 2019 Ranchero Software. All rights reserved.
//
import SwiftUI
import SafariServices
struct SafariView : UIViewControllerRepresentable {
let url: URL
func makeUIViewController(context: UIViewControllerRepresentableContext<SafariView>) -> SFSafariViewController {
let safari = SFSafariViewController(url: url)
safari.delegate = context.coordinator
return safari
}
func updateUIViewController(_ uiViewController: SFSafariViewController, context: UIViewControllerRepresentableContext<SafariView>) {
//
}
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
class Coordinator : NSObject, SFSafariViewControllerDelegate {
var parent: SafariView
init(_ safariView: SafariView) {
self.parent = safariView
}
// MARK: SFSafariViewControllerDelegate
func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
}
func safariViewController(_ controller: SFSafariViewController, initialLoadDidRedirectTo URL: URL) {
}
func safariViewController(_ controller: SFSafariViewController, didCompleteInitialLoad didLoadSuccessfully: Bool) {
}
}
}