From 8dcf4966fbbb6f470bcda1f8cd4ce7329047ecf2 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Sat, 15 Jun 2019 16:03:41 -0500 Subject: [PATCH 1/7] Port Feedbin credential update too SwfitUI. --- iOS/Settings/SettingsDetailAccountView.swift | 31 ++++++++---- iOS/Settings/SettingsFeedbinAccountView.swift | 48 +++++++++++++------ 2 files changed, 57 insertions(+), 22 deletions(-) diff --git a/iOS/Settings/SettingsDetailAccountView.swift b/iOS/Settings/SettingsDetailAccountView.swift index 75f4fe1fe..dab8f30ce 100644 --- a/iOS/Settings/SettingsDetailAccountView.swift +++ b/iOS/Settings/SettingsDetailAccountView.swift @@ -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() 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 } diff --git a/iOS/Settings/SettingsFeedbinAccountView.swift b/iOS/Settings/SettingsFeedbinAccountView.swift index 50d425bc7..c867c94d2 100644 --- a/iOS/Settings/SettingsFeedbinAccountView.swift +++ b/iOS/Settings/SettingsFeedbinAccountView.swift @@ -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() + 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 From dc75737c979801cd2403a8c242a756e1adb4b1fe Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Sat, 15 Jun 2019 18:19:20 -0500 Subject: [PATCH 2/7] Add Import/Export OPML action sheet. --- iOS/Settings/SettingsLocalAccountView.swift | 6 +-- iOS/Settings/SettingsView.swift | 47 +++++++++++++++++++-- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/iOS/Settings/SettingsLocalAccountView.swift b/iOS/Settings/SettingsLocalAccountView.swift index 40f671661..6247aea77 100644 --- a/iOS/Settings/SettingsLocalAccountView.swift +++ b/iOS/Settings/SettingsLocalAccountView.swift @@ -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 { diff --git a/iOS/Settings/SettingsView.swift b/iOS/Settings/SettingsView.swift index d0ddae653..7e8feec9f 100644 --- a/iOS/Settings/SettingsView.swift +++ b/iOS/Settings/SettingsView.swift @@ -12,7 +12,9 @@ import Account struct SettingsView : View { @ObjectBinding var viewModel: ViewModel - + @State var showImportSubscriptions = false + @State var showExportSubscriptions = false + var body: some View { NavigationView { List { @@ -76,10 +78,21 @@ struct SettingsView : View { Text(interval.description()).tag(interval) } } - Text("Import Subscriptions...") - Text("Export Subscriptions...") + Button(action: { + self.showImportSubscriptions = true + }) { + Text("Import Subscriptions...") + } + .presentation(showImportSubscriptions ? importSubscriptionsActionSheet : nil) + Button(action: { + self.showExportSubscriptions = true + }) { + Text("Export Subscriptions...") + } + .presentation(showExportSubscriptions ? exportSubscriptionsActionSheet : nil) } - + .foregroundColor(.primary) + } .listStyle(.grouped) .navigationBarTitle(Text("Settings"), displayMode: .inline) @@ -87,6 +100,32 @@ struct SettingsView : View { } } + var importSubscriptionsActionSheet: ActionSheet { + var buttons = [ActionSheet.Button]() + for account in viewModel.accounts { + let button = ActionSheet.Button.default(Text(verbatim: account.nameForDisplay)) { + self.showImportSubscriptions = false + // Call doc picker here... + } + buttons.append(button) + } + buttons.append(.cancel { self.showImportSubscriptions = false }) + return ActionSheet(title: Text("Import Subscriptions..."), message: Text("Select the account to import your OPML file into."), buttons: buttons) + } + + var exportSubscriptionsActionSheet: ActionSheet { + var buttons = [ActionSheet.Button]() + for account in viewModel.accounts { + let button = ActionSheet.Button.default(Text(verbatim: account.nameForDisplay)) { + self.showExportSubscriptions = false + // Call doc picker here... + } + buttons.append(button) + } + buttons.append(.cancel { self.showImportSubscriptions = false }) + return ActionSheet(title: Text("Export Subscriptions..."), message: Text("Select the account to export out of."), buttons: buttons) + } + class ViewModel: BindableObject { let didChange = PassthroughSubject() From 2f4d1145f4efb94d8412cd341e9bd7a0c72ac474 Mon Sep 17 00:00:00 2001 From: Stuart Breckenridge Date: Sun, 16 Jun 2019 07:23:32 +0800 Subject: [PATCH 3/7] Opens Settings option in SFSafariViewController Fixes #754 Fixes #756 --- NetNewsWire.xcodeproj/project.pbxproj | 12 ++++--- iOS/Settings/SafariView.swift | 52 +++++++++++++++++++++++++++ iOS/Settings/SettingsView.swift | 35 +++++++----------- 3 files changed, 72 insertions(+), 27 deletions(-) create mode 100644 iOS/Settings/SafariView.swift diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index a6cd26935..5b7f782c1 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -318,6 +318,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 */ @@ -926,6 +927,7 @@ D5F4EDB4200744A700B9E363 /* ScriptingObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptingObject.swift; sourceTree = ""; }; D5F4EDB620074D6500B9E363 /* Feed+Scriptability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Feed+Scriptability.swift"; sourceTree = ""; }; D5F4EDB820074D7C00B9E363 /* Folder+Scriptability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Folder+Scriptability.swift"; sourceTree = ""; }; + DF999FF622B5AEFA0064B687 /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1040,6 +1042,7 @@ 5183CCEB227117C70010922C /* Settings */ = { isa = PBXGroup; children = ( + DF999FF622B5AEFA0064B687 /* SafariView.swift */, 510D708122B041CC004E8F65 /* SettingsAccountLabelView.swift */, 510D707322B028E1004E8F65 /* SettingsAddAccountView.swift */, 51F772EC22B2789B0087D9D1 /* SettingsDetailAccountView.swift */, @@ -1935,12 +1938,12 @@ ORGANIZATIONNAME = "Ranchero Software"; TargetAttributes = { 6581C73220CED60000F4AD34 = { - DevelopmentTeam = SHJK2V3AJG; + DevelopmentTeam = M8L2WTLA8W; ProvisioningStyle = Manual; }; 840D617B2029031C009BC708 = { CreatedOnToolsVersion = 9.3; - DevelopmentTeam = SHJK2V3AJG; + DevelopmentTeam = M8L2WTLA8W; ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.BackgroundModes = { @@ -1950,7 +1953,7 @@ }; 849C645F1ED37A5D003D8FC0 = { CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = SHJK2V3AJG; + DevelopmentTeam = M8L2WTLA8W; ProvisioningStyle = Manual; SystemCapabilities = { com.apple.HardenedRuntime = { @@ -1960,7 +1963,7 @@ }; 849C64701ED37A5D003D8FC0 = { CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = SHJK2V3AJG; + DevelopmentTeam = 9C84TZ7Q6Z; ProvisioningStyle = Automatic; TestTargetID = 849C645F1ED37A5D003D8FC0; }; @@ -2365,6 +2368,7 @@ 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 */, diff --git a/iOS/Settings/SafariView.swift b/iOS/Settings/SafariView.swift new file mode 100644 index 000000000..6b5d78334 --- /dev/null +++ b/iOS/Settings/SafariView.swift @@ -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) -> SFSafariViewController { + let safari = SFSafariViewController(url: url) + safari.delegate = context.coordinator + return safari + } + + func updateUIViewController(_ uiViewController: SFSafariViewController, context: UIViewControllerRepresentableContext) { + // + } + + 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) { + + } + } +} + + diff --git a/iOS/Settings/SettingsView.swift b/iOS/Settings/SettingsView.swift index d0ddae653..82b1cbe07 100644 --- a/iOS/Settings/SettingsView.swift +++ b/iOS/Settings/SettingsView.swift @@ -10,9 +10,12 @@ import SwiftUI import Combine import Account + + struct SettingsView : View { @ObjectBinding var viewModel: ViewModel + var body: some View { NavigationView { List { @@ -31,30 +34,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") From 991c14849467d79b2e1a152d0f2bcee1698ad5c5 Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Sat, 15 Jun 2019 17:02:42 -0700 Subject: [PATCH 4/7] Update version to 5.0a4. --- Mac/Resources/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mac/Resources/Info.plist b/Mac/Resources/Info.plist index 579e08015..c6dc652a0 100644 --- a/Mac/Resources/Info.plist +++ b/Mac/Resources/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 5.0a3 + 5.0a4 CFBundleURLTypes From b8b2f2333364d1acd6d9691ac6f7778ecca9f11d Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Sun, 16 Jun 2019 10:21:03 -0500 Subject: [PATCH 5/7] Move SafariView to Wrappers group since it is a reusable SwiftUI View --- NetNewsWire.xcodeproj/project.pbxproj | 18 +++++++++++++----- iOS/{Settings => Wrappers}/SafariView.swift | 0 2 files changed, 13 insertions(+), 5 deletions(-) rename iOS/{Settings => Wrappers}/SafariView.swift (100%) diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 5b7f782c1..063a67af6 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -1042,7 +1042,6 @@ 5183CCEB227117C70010922C /* Settings */ = { isa = PBXGroup; children = ( - DF999FF622B5AEFA0064B687 /* SafariView.swift */, 510D708122B041CC004E8F65 /* SettingsAccountLabelView.swift */, 510D707322B028E1004E8F65 /* SettingsAddAccountView.swift */, 51F772EC22B2789B0087D9D1 /* SettingsDetailAccountView.swift */, @@ -1054,6 +1053,14 @@ path = Settings; sourceTree = ""; }; + 5194B5E222B693EC00144881 /* Wrappers */ = { + isa = PBXGroup; + children = ( + DF999FF622B5AEFA0064B687 /* SafariView.swift */, + ); + path = Wrappers; + sourceTree = ""; + }; 51C45245226506C800C03939 /* Extensions */ = { isa = PBXGroup; children = ( @@ -1693,6 +1700,7 @@ 5183CCEB227117C70010922C /* Settings */, 5183CCDB226F1EEB0010922C /* Progress */, 51C45245226506C800C03939 /* Extensions */, + 5194B5E222B693EC00144881 /* Wrappers */, 84C9FC9A2262A1A900D921D6 /* Resources */, ); path = iOS; @@ -1938,12 +1946,12 @@ ORGANIZATIONNAME = "Ranchero Software"; TargetAttributes = { 6581C73220CED60000F4AD34 = { - DevelopmentTeam = M8L2WTLA8W; + DevelopmentTeam = SHJK2V3AJG; ProvisioningStyle = Manual; }; 840D617B2029031C009BC708 = { CreatedOnToolsVersion = 9.3; - DevelopmentTeam = M8L2WTLA8W; + DevelopmentTeam = SHJK2V3AJG; ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.BackgroundModes = { @@ -1953,7 +1961,7 @@ }; 849C645F1ED37A5D003D8FC0 = { CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = M8L2WTLA8W; + DevelopmentTeam = SHJK2V3AJG; ProvisioningStyle = Manual; SystemCapabilities = { com.apple.HardenedRuntime = { @@ -1963,7 +1971,7 @@ }; 849C64701ED37A5D003D8FC0 = { CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = 9C84TZ7Q6Z; + DevelopmentTeam = SHJK2V3AJG; ProvisioningStyle = Automatic; TestTargetID = 849C645F1ED37A5D003D8FC0; }; diff --git a/iOS/Settings/SafariView.swift b/iOS/Wrappers/SafariView.swift similarity index 100% rename from iOS/Settings/SafariView.swift rename to iOS/Wrappers/SafariView.swift From 3af60f0edc295ed1fd9b542e6e90be3307f8f6bf Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Sun, 16 Jun 2019 10:54:18 -0500 Subject: [PATCH 6/7] Add Import OPML functionality --- NetNewsWire.xcodeproj/project.pbxproj | 4 ++ ...mportSubscriptionsDocumentPickerView.swift | 52 +++++++++++++++++++ iOS/Settings/SettingsView.swift | 22 ++++---- 3 files changed, 68 insertions(+), 10 deletions(-) create mode 100644 iOS/Settings/SettingsImportSubscriptionsDocumentPickerView.swift diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 063a67af6..290cbee3f 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -49,6 +49,7 @@ 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 /* SettingsImportSubscriptionsDocumentPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5194B5ED22B6965300144881 /* SettingsImportSubscriptionsDocumentPickerView.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, ); }; }; @@ -694,6 +695,7 @@ 5183CCE7226F68D90010922C /* AccountRefreshTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountRefreshTimer.swift; sourceTree = ""; }; 5183CCEC22711DCE0010922C /* Settings.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = ""; }; 5183CCEE227125970010922C /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; + 5194B5ED22B6965300144881 /* SettingsImportSubscriptionsDocumentPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsImportSubscriptionsDocumentPickerView.swift; sourceTree = ""; }; 519B8D322143397200FA689C /* SharingServiceDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingServiceDelegate.swift; sourceTree = ""; }; 51C4524E226506F400C03939 /* UIStoryboard-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStoryboard-Extensions.swift"; sourceTree = ""; }; 51C45250226506F400C03939 /* String-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String-Extensions.swift"; sourceTree = ""; }; @@ -1047,6 +1049,7 @@ 51F772EC22B2789B0087D9D1 /* SettingsDetailAccountView.swift */, 510D707F22B02A5F004E8F65 /* SettingsFeedbinAccountView.swift */, 510D707D22B02A4B004E8F65 /* SettingsLocalAccountView.swift */, + 5194B5ED22B6965300144881 /* SettingsImportSubscriptionsDocumentPickerView.swift */, 51F35D0822AFD4760003CE1B /* SettingsView.swift */, 51F35CFD22AFD0350003CE1B /* UIKit */, ); @@ -2381,6 +2384,7 @@ 5183CCE3226F314C0010922C /* ProgressTableViewController.swift in Sources */, 512E09012268907400BDCFDD /* MasterFeedTableViewSectionHeader.swift in Sources */, 51C45268226508F600C03939 /* MasterFeedUnreadCountView.swift in Sources */, + 5194B5EE22B6965300144881 /* SettingsImportSubscriptionsDocumentPickerView.swift in Sources */, 5183CCD0226E1E880010922C /* NonIntrinsicLabel.swift in Sources */, 51C4529F22650A1900C03939 /* AuthorAvatarDownloader.swift in Sources */, 51E595AD228E1C2100FCC42B /* AddAccountViewController.swift in Sources */, diff --git a/iOS/Settings/SettingsImportSubscriptionsDocumentPickerView.swift b/iOS/Settings/SettingsImportSubscriptionsDocumentPickerView.swift new file mode 100644 index 000000000..3c8cbbeb9 --- /dev/null +++ b/iOS/Settings/SettingsImportSubscriptionsDocumentPickerView.swift @@ -0,0 +1,52 @@ +// +// SettingsImportSubscriptionsDocumentPickerView.swift +// NetNewsWire-iOS +// +// Created by Maurice Parker on 6/16/19. +// Copyright © 2019 Ranchero Software. All rights reserved. +// + +import SwiftUI +import Account + +struct SettingsImportSubscriptionsDocumentPickerView : UIViewControllerRepresentable { + var account: Account? + + func makeUIViewController(context: UIViewControllerRepresentableContext) -> UIDocumentPickerViewController { + let docPicker = UIDocumentPickerViewController(documentTypes: ["public.xml", "org.opml.opml"], in: .import) + docPicker.delegate = context.coordinator + docPicker.modalPresentationStyle = .formSheet + return docPicker + } + + func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: UIViewControllerRepresentableContext) { + // + } + + func makeCoordinator() -> Coordinator { + return Coordinator(self) + } + + class Coordinator : NSObject, UIDocumentPickerDelegate { + var parent: SettingsImportSubscriptionsDocumentPickerView + + init(_ view: SettingsImportSubscriptionsDocumentPickerView) { + self.parent = view + } + + func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { + for url in urls { + parent.account?.importOPML(url) { result in} + } + } + + } +} + +#if DEBUG +struct SettingsImportSubscriptionsDocumentPickerView_Previews : PreviewProvider { + static var previews: some View { + SettingsImportSubscriptionsDocumentPickerView() + } +} +#endif diff --git a/iOS/Settings/SettingsView.swift b/iOS/Settings/SettingsView.swift index 26281f8a9..761f4a268 100644 --- a/iOS/Settings/SettingsView.swift +++ b/iOS/Settings/SettingsView.swift @@ -14,7 +14,8 @@ import Account struct SettingsView : View { @ObjectBinding var viewModel: ViewModel - @State var showImportSubscriptions = false + @State var importSubscriptionsAccounts: ActionSheet? = nil + @State var importSubscriptionsDocumentPicker: Modal? = nil @State var showExportSubscriptions = false var body: some View { @@ -67,17 +68,18 @@ struct SettingsView : View { } } Button(action: { - self.showImportSubscriptions = true + self.importSubscriptionsAccounts = self.createImportSubscriptionsAccounts }) { Text("Import Subscriptions...") } - .presentation(showImportSubscriptions ? importSubscriptionsActionSheet : nil) + .presentation(importSubscriptionsAccounts) + .presentation(importSubscriptionsDocumentPicker) Button(action: { self.showExportSubscriptions = true }) { Text("Export Subscriptions...") } - .presentation(showExportSubscriptions ? exportSubscriptionsActionSheet : nil) + .presentation(showExportSubscriptions ? exportSubscriptionsAccounts : nil) } .foregroundColor(.primary) @@ -88,20 +90,20 @@ struct SettingsView : View { } } - var importSubscriptionsActionSheet: ActionSheet { + var createImportSubscriptionsAccounts: ActionSheet { var buttons = [ActionSheet.Button]() for account in viewModel.accounts { let button = ActionSheet.Button.default(Text(verbatim: account.nameForDisplay)) { - self.showImportSubscriptions = false - // Call doc picker here... + self.importSubscriptionsAccounts = nil + self.importSubscriptionsDocumentPicker = Modal(SettingsImportSubscriptionsDocumentPickerView(account: account)) } buttons.append(button) } - buttons.append(.cancel { self.showImportSubscriptions = false }) + buttons.append(.cancel { self.importSubscriptionsAccounts = nil }) return ActionSheet(title: Text("Import Subscriptions..."), message: Text("Select the account to import your OPML file into."), buttons: buttons) } - var exportSubscriptionsActionSheet: ActionSheet { + var exportSubscriptionsAccounts: ActionSheet { var buttons = [ActionSheet.Button]() for account in viewModel.accounts { let button = ActionSheet.Button.default(Text(verbatim: account.nameForDisplay)) { @@ -110,7 +112,7 @@ struct SettingsView : View { } buttons.append(button) } - buttons.append(.cancel { self.showImportSubscriptions = false }) + buttons.append(.cancel { self.showExportSubscriptions = false }) return ActionSheet(title: Text("Export Subscriptions..."), message: Text("Select the account to export out of."), buttons: buttons) } From d4e2d44d8f5ab37572a6b00d2a87cd515bf306e1 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Sun, 16 Jun 2019 11:19:15 -0500 Subject: [PATCH 7/7] Add OPML Export for SwiftUI --- NetNewsWire.xcodeproj/project.pbxproj | 12 ++++--- ...ubscriptionsExportDocumentPickerView.swift | 31 +++++++++++++++++ ...bscriptionsImportDocumentPickerView.swift} | 25 +++++--------- iOS/Settings/SettingsView.swift | 34 ++++++++++--------- 4 files changed, 65 insertions(+), 37 deletions(-) create mode 100644 iOS/Settings/SettingsSubscriptionsExportDocumentPickerView.swift rename iOS/Settings/{SettingsImportSubscriptionsDocumentPickerView.swift => SettingsSubscriptionsImportDocumentPickerView.swift} (57%) diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 290cbee3f..48cc63bf3 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -49,7 +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 /* SettingsImportSubscriptionsDocumentPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5194B5ED22B6965300144881 /* SettingsImportSubscriptionsDocumentPickerView.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, ); }; }; @@ -695,7 +696,8 @@ 5183CCE7226F68D90010922C /* AccountRefreshTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountRefreshTimer.swift; sourceTree = ""; }; 5183CCEC22711DCE0010922C /* Settings.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = ""; }; 5183CCEE227125970010922C /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; - 5194B5ED22B6965300144881 /* SettingsImportSubscriptionsDocumentPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsImportSubscriptionsDocumentPickerView.swift; sourceTree = ""; }; + 5194B5ED22B6965300144881 /* SettingsSubscriptionsImportDocumentPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSubscriptionsImportDocumentPickerView.swift; sourceTree = ""; }; + 5194B5F122B69FCC00144881 /* SettingsSubscriptionsExportDocumentPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSubscriptionsExportDocumentPickerView.swift; sourceTree = ""; }; 519B8D322143397200FA689C /* SharingServiceDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingServiceDelegate.swift; sourceTree = ""; }; 51C4524E226506F400C03939 /* UIStoryboard-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStoryboard-Extensions.swift"; sourceTree = ""; }; 51C45250226506F400C03939 /* String-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String-Extensions.swift"; sourceTree = ""; }; @@ -1049,7 +1051,8 @@ 51F772EC22B2789B0087D9D1 /* SettingsDetailAccountView.swift */, 510D707F22B02A5F004E8F65 /* SettingsFeedbinAccountView.swift */, 510D707D22B02A4B004E8F65 /* SettingsLocalAccountView.swift */, - 5194B5ED22B6965300144881 /* SettingsImportSubscriptionsDocumentPickerView.swift */, + 5194B5F122B69FCC00144881 /* SettingsSubscriptionsExportDocumentPickerView.swift */, + 5194B5ED22B6965300144881 /* SettingsSubscriptionsImportDocumentPickerView.swift */, 51F35D0822AFD4760003CE1B /* SettingsView.swift */, 51F35CFD22AFD0350003CE1B /* UIKit */, ); @@ -2383,8 +2386,9 @@ 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 /* SettingsImportSubscriptionsDocumentPickerView.swift in Sources */, + 5194B5EE22B6965300144881 /* SettingsSubscriptionsImportDocumentPickerView.swift in Sources */, 5183CCD0226E1E880010922C /* NonIntrinsicLabel.swift in Sources */, 51C4529F22650A1900C03939 /* AuthorAvatarDownloader.swift in Sources */, 51E595AD228E1C2100FCC42B /* AddAccountViewController.swift in Sources */, diff --git a/iOS/Settings/SettingsSubscriptionsExportDocumentPickerView.swift b/iOS/Settings/SettingsSubscriptionsExportDocumentPickerView.swift new file mode 100644 index 000000000..a471c7d27 --- /dev/null +++ b/iOS/Settings/SettingsSubscriptionsExportDocumentPickerView.swift @@ -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) -> 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) { + // + } + +} diff --git a/iOS/Settings/SettingsImportSubscriptionsDocumentPickerView.swift b/iOS/Settings/SettingsSubscriptionsImportDocumentPickerView.swift similarity index 57% rename from iOS/Settings/SettingsImportSubscriptionsDocumentPickerView.swift rename to iOS/Settings/SettingsSubscriptionsImportDocumentPickerView.swift index 3c8cbbeb9..4f4043a92 100644 --- a/iOS/Settings/SettingsImportSubscriptionsDocumentPickerView.swift +++ b/iOS/Settings/SettingsSubscriptionsImportDocumentPickerView.swift @@ -1,5 +1,5 @@ // -// SettingsImportSubscriptionsDocumentPickerView.swift +// SettingsSubscriptionsImportDocumentPickerView.swift // NetNewsWire-iOS // // Created by Maurice Parker on 6/16/19. @@ -9,17 +9,16 @@ import SwiftUI import Account -struct SettingsImportSubscriptionsDocumentPickerView : UIViewControllerRepresentable { - var account: Account? +struct SettingsSubscriptionsImportDocumentPickerView : UIViewControllerRepresentable { + var account: Account - func makeUIViewController(context: UIViewControllerRepresentableContext) -> UIDocumentPickerViewController { + func makeUIViewController(context: UIViewControllerRepresentableContext) -> UIDocumentPickerViewController { let docPicker = UIDocumentPickerViewController(documentTypes: ["public.xml", "org.opml.opml"], in: .import) docPicker.delegate = context.coordinator - docPicker.modalPresentationStyle = .formSheet return docPicker } - func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: UIViewControllerRepresentableContext) { + func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: UIViewControllerRepresentableContext) { // } @@ -28,25 +27,17 @@ struct SettingsImportSubscriptionsDocumentPickerView : UIViewControllerRepresent } class Coordinator : NSObject, UIDocumentPickerDelegate { - var parent: SettingsImportSubscriptionsDocumentPickerView + var parent: SettingsSubscriptionsImportDocumentPickerView - init(_ view: SettingsImportSubscriptionsDocumentPickerView) { + init(_ view: SettingsSubscriptionsImportDocumentPickerView) { self.parent = view } func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { for url in urls { - parent.account?.importOPML(url) { result in} + parent.account.importOPML(url) { result in} } } } } - -#if DEBUG -struct SettingsImportSubscriptionsDocumentPickerView_Previews : PreviewProvider { - static var previews: some View { - SettingsImportSubscriptionsDocumentPickerView() - } -} -#endif diff --git a/iOS/Settings/SettingsView.swift b/iOS/Settings/SettingsView.swift index 761f4a268..1ca917abc 100644 --- a/iOS/Settings/SettingsView.swift +++ b/iOS/Settings/SettingsView.swift @@ -14,9 +14,10 @@ import Account struct SettingsView : View { @ObjectBinding var viewModel: ViewModel - @State var importSubscriptionsAccounts: ActionSheet? = nil - @State var importSubscriptionsDocumentPicker: Modal? = nil - @State var showExportSubscriptions = false + @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 { @@ -68,18 +69,19 @@ struct SettingsView : View { } } Button(action: { - self.importSubscriptionsAccounts = self.createImportSubscriptionsAccounts + self.subscriptionsImportAccounts = self.createSubscriptionsImportAccounts }) { Text("Import Subscriptions...") } - .presentation(importSubscriptionsAccounts) - .presentation(importSubscriptionsDocumentPicker) + .presentation(subscriptionsImportAccounts) + .presentation(subscriptionsImportDocumentPicker) Button(action: { - self.showExportSubscriptions = true + self.subscriptionsExportAccounts = self.createSubscriptionsExportAccounts }) { Text("Export Subscriptions...") } - .presentation(showExportSubscriptions ? exportSubscriptionsAccounts : nil) + .presentation(subscriptionsExportAccounts) + .presentation(subscriptionsExportDocumentPicker) } .foregroundColor(.primary) @@ -90,29 +92,29 @@ struct SettingsView : View { } } - var createImportSubscriptionsAccounts: ActionSheet { + var createSubscriptionsImportAccounts: ActionSheet { var buttons = [ActionSheet.Button]() for account in viewModel.accounts { let button = ActionSheet.Button.default(Text(verbatim: account.nameForDisplay)) { - self.importSubscriptionsAccounts = nil - self.importSubscriptionsDocumentPicker = Modal(SettingsImportSubscriptionsDocumentPickerView(account: account)) + self.subscriptionsImportAccounts = nil + self.subscriptionsImportDocumentPicker = Modal(SettingsSubscriptionsImportDocumentPickerView(account: account)) } buttons.append(button) } - buttons.append(.cancel { self.importSubscriptionsAccounts = nil }) + 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 exportSubscriptionsAccounts: ActionSheet { + var createSubscriptionsExportAccounts: ActionSheet { var buttons = [ActionSheet.Button]() for account in viewModel.accounts { let button = ActionSheet.Button.default(Text(verbatim: account.nameForDisplay)) { - self.showExportSubscriptions = false - // Call doc picker here... + self.subscriptionsExportAccounts = nil + self.subscriptionsExportDocumentPicker = Modal(SettingsSubscriptionsExportDocumentPickerView(account: account)) } buttons.append(button) } - buttons.append(.cancel { self.showExportSubscriptions = false }) + buttons.append(.cancel { self.subscriptionsExportAccounts = nil }) return ActionSheet(title: Text("Export Subscriptions..."), message: Text("Select the account to export out of."), buttons: buttons) }