Merge branch 'swiftui' of https://github.com/brentsimmons/NetNewsWire into swiftui-progressview

This commit is contained in:
Phil Viso 2020-07-04 07:33:14 -05:00
commit 7be80772f6
28 changed files with 929 additions and 123 deletions

View File

@ -0,0 +1,132 @@
//
// AddWebFeedModel.swift
// NetNewsWire
//
// Created by Stuart Breckenridge on 4/7/20.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import Foundation
import Account
import RSCore
import SwiftUI
enum AddWebFeedError: LocalizedError {
case none, alreadySubscribed, initialDownload, noFeeds
var errorDescription: String? {
switch self {
case .alreadySubscribed:
return NSLocalizedString("Cant add this feed because youve already subscribed to it.", comment: "Feed finder")
case .initialDownload:
return NSLocalizedString("Cant add this feed because of a download error.", comment: "Feed finder")
case .noFeeds:
return NSLocalizedString("Cant add a feed because no feed was found.", comment: "Feed finder")
default:
return nil
}
}
}
class AddWebFeedModel: ObservableObject {
@Published var shouldDismiss: Bool = false
@Published var providedURL: String = ""
@Published var providedName: String = ""
@Published var selectedFolderIndex: Int = 0
@Published var addFeedError: AddWebFeedError? {
didSet {
addFeedError != AddWebFeedError.none ? (showError = true) : (showError = false)
}
}
@Published var showError: Bool = false
@Published var containers: [Container] = []
@Published var showProgressIndicator: Bool = false
init() {
for account in AccountManager.shared.sortedActiveAccounts {
containers.append(account)
if let sortedFolders = account.sortedFolders {
containers.append(contentsOf: sortedFolders)
}
}
}
func pasteUrlFromPasteboard() {
guard let stringFromPasteboard = urlStringFromPasteboard, stringFromPasteboard.isValidURL else {
return
}
providedURL = stringFromPasteboard
}
#if os(macOS)
var urlStringFromPasteboard: String? {
if let urlString = NSPasteboard.urlString(from: NSPasteboard.general) {
return urlString.normalizedURL
}
return nil
}
#else
var urlStringFromPasteboard: String? {
if let urlString = UIPasteboard.general.url?.absoluteString {
return urlString.normalizedURL
}
return nil
}
#endif
struct AccountAndFolderSpecifier {
let account: Account
let folder: Folder?
}
func accountAndFolderFromContainer(_ container: Container) -> AccountAndFolderSpecifier? {
if let account = container as? Account {
return AccountAndFolderSpecifier(account: account, folder: nil)
}
if let folder = container as? Folder, let account = folder.account {
return AccountAndFolderSpecifier(account: account, folder: folder)
}
return nil
}
func addWebFeed() {
if let account = accountAndFolderFromContainer(containers[selectedFolderIndex])?.account {
showProgressIndicator = true
let container = containers[selectedFolderIndex]
if account.hasWebFeed(withURL: providedURL) {
addFeedError = .alreadySubscribed
showProgressIndicator = false
return
}
account.createWebFeed(url: providedURL, name: providedName, container: container, completion: { [weak self] result in
self?.showProgressIndicator = false
switch result {
case .success(let feed):
NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.webFeed: feed])
self?.shouldDismiss = true
case .failure(let error):
switch error {
case AccountError.createErrorAlreadySubscribed:
self?.addFeedError = .alreadySubscribed
return
case AccountError.createErrorNotFound:
self?.addFeedError = .noFeeds
return
default:
print("Error")
}
}
})
}
}
}

View File

@ -0,0 +1,163 @@
//
// AddWebFeedView.swift
// NetNewsWire
//
// Created by Stuart Breckenridge on 3/7/20.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import SwiftUI
import Account
import RSCore
struct AddWebFeedView: View {
@Environment(\.presentationMode) private var presentationMode
@ObservedObject private var viewModel = AddWebFeedModel()
@ViewBuilder var body: some View {
#if os(iOS)
iosForm
.onAppear {
viewModel.pasteUrlFromPasteboard()
}
.onReceive(viewModel.$shouldDismiss, perform: { dismiss in
if dismiss == true {
presentationMode.wrappedValue.dismiss()
}
})
#else
macForm
.onAppear {
viewModel.pasteUrlFromPasteboard()
}.alert(isPresented: $viewModel.showError) {
Alert(title: Text("Oops"), message: Text(viewModel.addFeedError!.localizedDescription), dismissButton: Alert.Button.cancel({
viewModel.addFeedError = AddWebFeedError.none
}))
}.onReceive(viewModel.$shouldDismiss, perform: { dismiss in
if dismiss == true {
presentationMode.wrappedValue.dismiss()
}
})
#endif
}
#if os(macOS)
var macForm: some View {
Form {
HStack {
Spacer()
Image(systemName: "globe").foregroundColor(.accentColor).font(.title)
Text("Add a Web Feed")
.font(.title)
Spacer()
}
urlTextField
.textFieldStyle(RoundedBorderTextFieldStyle())
.help("The URL of the feed you want to add.")
providedNameTextField
.textFieldStyle(RoundedBorderTextFieldStyle())
.help("The name of the feed. (Optional.)")
folderPicker
.help("Pick the folder you want to add the feed to.")
buttonStack
}
.padding()
.frame(minWidth: 450)
}
#endif
#if os(iOS)
@ViewBuilder var iosForm: some View {
NavigationView {
Form {
urlTextField
providedNameTextField
folderPicker
}
.listStyle(InsetGroupedListStyle())
.navigationBarTitle("Add Web Feed")
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(leading:
Button("Cancel", action: {
presentationMode.wrappedValue.dismiss()
})
.help("Cancel Add Feed")
, trailing:
Button("Add", action: {
viewModel.addWebFeed()
})
.disabled(!viewModel.providedURL.isValidURL)
.help("Add Feed")
)
}
}
#endif
var urlTextField: some View {
HStack {
Text("Feed:")
#if os(iOS)
TextField("URL", text: $viewModel.providedURL)
.disableAutocorrection(true)
.autocapitalization(UITextAutocapitalizationType.none)
#else
TextField("URL", text: $viewModel.providedURL)
.disableAutocorrection(true)
#endif
}
}
var providedNameTextField: some View {
HStack(alignment: .lastTextBaseline) {
Text("Name:")
TextField("Optional", text: $viewModel.providedName)
}
}
var folderPicker: some View {
Picker("Folder:", selection: $viewModel.selectedFolderIndex, content: {
ForEach(0..<viewModel.containers.count, id: \.self, content: { index in
if let containerName = (viewModel.containers[index] as? DisplayNameProvider)?.nameForDisplay {
if viewModel.containers[index] is Folder {
Text("\(viewModel.containers[index].account?.nameForDisplay ?? "") / \(containerName)").tag(index)
} else {
Text(containerName).tag(index)
}
}
})
})
}
var buttonStack: some View {
HStack {
if viewModel.showProgressIndicator == true {
ProgressView()
.frame(width: 25, height: 25)
.help("Adding Feed")
}
Spacer()
Button("Cancel", action: {
presentationMode.wrappedValue.dismiss()
})
.help("Cancel Add Feed")
Button("Add", action: {
viewModel.addWebFeed()
})
.disabled(!viewModel.providedURL.isValidURL)
.help("Add Feed")
}
}
}
struct AddFeedView_Previews: PreviewProvider {
static var previews: some View {
AddWebFeedView()
}
}

View File

@ -13,68 +13,100 @@ import Account
struct AppAssets {
static var accountLocalMacImage: RSImage! = {
return RSImage(named: "accountLocalMac")
return RSImage(named: "AccountLocalMac")
}()
static var accountLocalPadImage: RSImage = {
return RSImage(named: "accountLocalPad")!
return RSImage(named: "AccountLocalPad")!
}()
static var accountLocalPhoneImage: RSImage = {
return RSImage(named: "accountLocalPhone")!
return RSImage(named: "AccountLocalPhone")!
}()
static var accountCloudKitImage: RSImage = {
return RSImage(named: "accountCloudKit")!
return RSImage(named: "AccountCloudKit")!
}()
static var accountFeedbinImage: RSImage = {
return RSImage(named: "accountFeedbin")!
return RSImage(named: "AccountFeedbin")!
}()
static var accountFeedlyImage: RSImage = {
return RSImage(named: "accountFeedly")!
return RSImage(named: "AccountFeedly")!
}()
static var accountFeedWranglerImage: RSImage = {
return RSImage(named: "accountFeedWrangler")!
return RSImage(named: "AccountFeedWrangler")!
}()
static var accountFreshRSSImage: RSImage = {
return RSImage(named: "accountFreshRSS")!
return RSImage(named: "AccountFreshRSS")!
}()
static var accountNewsBlurImage: RSImage = {
return RSImage(named: "accountNewsBlur")!
return RSImage(named: "AccountNewsBlur")!
}()
static var addMenuImage: Image = {
return Image(systemName: "plus")
}()
static var extensionPointMarsEdit: RSImage = {
return RSImage(named: "extensionPointMarsEdit")!
return RSImage(named: "ExtensionPointMarsEdit")!
}()
static var extensionPointMicroblog: RSImage = {
return RSImage(named: "extensionPointMicroblog")!
return RSImage(named: "ExtensionPointMicroblog")!
}()
static var extensionPointReddit: RSImage = {
return RSImage(named: "extensionPointReddit")!
return RSImage(named: "ExtensionPointReddit")!
}()
static var extensionPointTwitter: RSImage = {
return RSImage(named: "extensionPointTwitter")!
return RSImage(named: "ExtensionPointTwitter")!
}()
static var faviconTemplateImage: RSImage = {
return RSImage(named: "faviconTemplateImage")!
return RSImage(named: "FaviconTemplateImage")!
}()
static var masterFolderImage: IconImage = {
static var settingsImage: Image = {
return Image(systemName: "gear")
}()
static var masterFolderImage: IconImage {
#if os(macOS)
return IconImage(NSImage(systemSymbolName: "folder.fill", accessibilityDescription: nil)!)
let image = NSImage(systemSymbolName: "folder.fill", accessibilityDescription: nil)!
let coloredImage = image.tinted(with: NSColor(named: "AccentColor")!)
return IconImage(coloredImage)
#endif
#if os(iOS)
return IconImage(UIImage(systemName: "folder.fill")!)
let image = UIImage(systemName: "folder.fill")!
let coloredImage = image.tinted(color: UIColor(named: "AccentColor")!)!
return IconImage(coloredImage)
#endif
}
static var markAllAsReadImage: Image = {
return Image("MarkAllAsRead")
}()
static var markAllAsReadImagePDF: Image = {
return Image("MarkAllAsReadPDF")
}()
static var nextUnreadArticleImage: Image = {
return Image(systemName: "chevron.down.circle")
}()
static var openInBrowserImage: Image = {
return Image(systemName: "safari")
}()
static var refreshImage: Image = {
return Image(systemName: "arrow.clockwise")
}()
static var searchFeedImage: IconImage = {
@ -86,6 +118,18 @@ struct AppAssets {
#endif
}()
static var sidebarUnreadCountBackground: Color = {
return Color("SidebarUnreadCountBackground")
}()
static var sidebarUnreadCountForeground: Color = {
return Color("SidebarUnreadCountForeground")
}()
static var shareImage: Image = {
Image(systemName: "square.and.arrow.up")
}()
static var smartFeedImage: RSImage = {
#if os(macOS)
return NSImage(systemSymbolName: "gear", accessibilityDescription: nil)!
@ -97,40 +141,76 @@ struct AppAssets {
static var starredFeedImage: IconImage = {
#if os(macOS)
return IconImage(NSImage(systemSymbolName: "star.fill", accessibilityDescription: nil)!)
let image = NSImage(systemSymbolName: "star.fill", accessibilityDescription: nil)!
let coloredImage = image.tinted(with: NSColor(named: "StarColor")!)
return IconImage(coloredImage)
#endif
#if os(iOS)
return IconImage(UIImage(systemName: "star.fill")!)
let image = UIImage(systemName: "star.fill")!
let coloredImage = image.tinted(color: UIColor(named: "StarColor")!)!
return IconImage(coloredImage)
#endif
}()
static var timelineStarred: Image = {
return Image(systemName: "star.fill")
#if os(macOS)
let image = NSImage(systemSymbolName: "star.fill", accessibilityDescription: nil)!
let coloredImage = image.tinted(with: NSColor(named: "StarColor")!)
return Image(nsImage: coloredImage)
#endif
#if os(iOS)
let image = UIImage(systemName: "star.fill")!
let coloredImage = image.tinted(color: UIColor(named: "StarColor")!)!
return Image(uiImage: coloredImage)
#endif
}()
static var timelineUnread: Image = {
return Image(systemName: "circle.fill")
}()
static var timelineUnread: Image {
#if os(macOS)
let image = NSImage(systemSymbolName: "circle.fill", accessibilityDescription: nil)!
let coloredImage = image.tinted(with: NSColor(named: "AccentColor")!)
return Image(nsImage: coloredImage)
#endif
#if os(iOS)
let image = UIImage(systemName: "circle.fill")!
let coloredImage = image.tinted(color: UIColor(named: "AccentColor")!)!
return Image(uiImage: coloredImage)
#endif
}
static var todayFeedImage: IconImage = {
#if os(macOS)
return IconImage(NSImage(systemSymbolName: "sun.max.fill", accessibilityDescription: nil)!)
let image = NSImage(systemSymbolName: "sun.max.fill", accessibilityDescription: nil)!
let coloredImage = image.tinted(with: .orange)
return IconImage(coloredImage)
#endif
#if os(iOS)
return IconImage(UIImage(systemName: "sun.max.fill")!)
let image = UIImage(systemName: "sun.max.fill")!
let coloredImage = image.tinted(color: .orange)!
return IconImage(coloredImage)
#endif
}()
static var unreadFeedImage: IconImage = {
static var toggleStarred: Image = {
return Image(systemName: "star.fill")
}()
static var toggleRead: Image = {
return Image(systemName: "largecircle.fill.circle")
}()
static var unreadFeedImage: IconImage {
#if os(macOS)
return IconImage(NSImage(systemSymbolName: "largecircle.fill.circle", accessibilityDescription: nil)!)
let image = NSImage(systemSymbolName: "largecircle.fill.circle", accessibilityDescription: nil)!
let coloredImage = image.tinted(with: NSColor(named: "AccentColor")!)
return IconImage(coloredImage)
#endif
#if os(iOS)
return IconImage(UIImage(systemName: "largecircle.fill.circle")!)
let image = UIImage(systemName: "largecircle.fill.circle")!
let coloredImage = image.tinted(color: UIColor(named: "AccentColor")!)!
return IconImage(coloredImage)
#endif
}()
}
static func image(for accountType: AccountType) -> RSImage? {
switch accountType {

View File

@ -13,19 +13,15 @@ struct ArticleContainerView: View {
@EnvironmentObject private var sceneModel: SceneModel
@StateObject private var articleModel = ArticleModel()
var article: Article? = nil
var article: Article
@ViewBuilder var body: some View {
if let article = article {
ArticleView()
.environmentObject(articleModel)
.onAppear {
sceneModel.articleModel = articleModel
articleModel.delegate = sceneModel
}
} else {
EmptyView()
}
}
}

View File

@ -23,9 +23,9 @@
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.945",
"green" : "0.502",
"red" : "0.176"
"blue" : "0.957",
"green" : "0.620",
"red" : "0.369"
}
},
"idiom" : "universal"

View File

@ -0,0 +1,16 @@
{
"images" : [
{
"filename" : "MarkAllAsReadPDF.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}

View File

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "0.500",
"blue" : "0.000",
"green" : "0.000",
"red" : "0.000"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "0.900",
"blue" : "1.000",
"green" : "1.000",
"red" : "1.000"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.204",
"green" : "0.776",
"red" : "0.976"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="3300px" height="2200px" viewBox="0 0 3300 2200" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 60.1 (88133) - https://sketch.com -->
<title>Untitled</title>
<!-- Generator: Sketch 61.2 (89653) - https://sketch.com -->
<title>markAllAsRead</title>
<desc>Created with Sketch.</desc>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="markAllAsRead">
@ -136,6 +136,11 @@
<path d="M50.45587,25 C77.70197,25 100.30977,47.6074 100.30977,74.8047 C100.30977,74.870144 100.30964,74.935561 100.30938,75.0009507 L99.8322178,75.0006156 C97.6995557,51.580586 76.5280991,33.3008 50.4213361,33.3008 C24.3145732,33.3008 3.24806132,51.580586 1.17616722,75.0006156 L0.701,75 L0.7,74.8047 C0.7,47.879373 22.8096545,25.452607 49.6413605,25.0067642 Z" id="Combined-Shape"></path>
<path d="M50.45587,-1.77635684e-14 C77.70197,-1.77635684e-14 100.30977,22.6074 100.30977,49.8047 C100.30977,49.870144 100.30964,49.935561 100.30938,50.0009507 L99.8322178,50.0006156 C97.6995557,26.580586 76.5280991,8.3008 50.4213361,8.3008 C24.3145732,8.3008 3.24806132,26.580586 1.17616722,50.0006156 L0.701,50 L0.7,49.8047 C0.7,22.879373 22.8096545,0.45260699 49.6413605,0.0067642025 Z" id="Combined-Shape"></path>
</g>
<g id="Medium-M" transform="translate(0.300000, 0.000000)">
<path d="M50.50467,149.611301 C77.75077,149.611301 100.30977,127.052691 100.30977,99.8066015 C100.30977,72.6093015 77.70197,50.0019015 50.45587,50.0019015 C23.25857,50.0019015 0.7,72.6093015 0.7,99.8066015 C0.7,127.052691 23.30747,149.611301 50.50467,149.611301 Z M50.50467,141.310501 C27.45777,141.310501 9.04957,122.853501 9.04957,99.8066015 C9.04957,76.8085015 27.40897,58.3027015 50.45587,58.3027015 C73.50277,58.3027015 91.95977,76.8085015 92.0088671,99.8066015 C92.05777,122.853501 73.55157,141.310501 50.50467,141.310501 Z" id="Shape"></path>
<path d="M50.45587,25.0009507 C77.70197,25.0009507 100.30977,47.6083507 100.30977,74.8056507 C100.30977,74.8710948 100.30964,74.9365118 100.30938,75.0019015 L99.8322178,75.0015664 C97.6995557,51.5815367 76.5280991,33.3017507 50.4213361,33.3017507 C24.3145732,33.3017507 3.24806132,51.5815367 1.17616722,75.0015664 L0.701,75.0009507 L0.7,74.8056507 C0.7,47.8803237 22.8096545,25.4535577 49.6413605,25.0077149 Z" id="Combined-Shape"></path>
<path d="M50.45587,-1.77635684e-14 C77.70197,-1.77635684e-14 100.30977,22.6074 100.30977,49.8047 C100.30977,49.870144 100.30964,49.935561 100.30938,50.0009507 L99.8322178,50.0006156 C97.6995557,26.580586 76.5280991,8.3008 50.4213361,8.3008 C24.3145732,8.3008 3.24806132,26.580586 1.17616722,50.0006156 L0.701,50 L0.7,49.8047 C0.7,22.879373 22.8096545,0.45260699 49.6413605,0.0067642025 Z" id="Combined-Shape"></path>
</g>
</g>
</g>
</g>

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -20,6 +20,7 @@ struct MainApp: App {
@StateObject private var sceneModel = SceneModel()
@StateObject private var defaults = AppDefaults.shared
@State private var showSheet = false
@SceneBuilder var body: some Scene {
#if os(macOS)
@ -28,63 +29,70 @@ struct MainApp: App {
.frame(minWidth: 600, idealWidth: 1000, maxWidth: .infinity, minHeight: 600, idealHeight: 700, maxHeight: .infinity)
.environmentObject(sceneModel)
.environmentObject(defaults)
.sheet(isPresented: $showSheet, onDismiss: { showSheet = false }) {
AddWebFeedView()
}
.toolbar {
ToolbarItem {
Button(action: {}, label: {
Image(systemName: "plus").foregroundColor(.secondary)
}).help("New Feed")
ToolbarItem() {
Menu {
Button("Add Web Feed", action: { showSheet = true })
Button("Add Reddit Feed", action: { })
Button("Add Twitter Feed", action: { })
Button("Add Folder", action: { })
} label : {
AppAssets.addMenuImage
}
}
ToolbarItem {
Button(action: {}, label: {
Image(systemName: "folder.fill.badge.plus").foregroundColor(.pink)
}).help("New Folder")
}
ToolbarItem {
Button(action: {}, label: {
Image(systemName: "arrow.clockwise").foregroundColor(.secondary)
AppAssets.refreshImage
}).help("Refresh").padding(.trailing, 40)
}
ToolbarItem {
Button(action: {}, label: {
Image(systemName: "circle.dashed").foregroundColor(.orange)
AppAssets.markAllAsReadImagePDF
.resizable()
.scaledToFit()
.frame(width: 20, height: 20, alignment: .center)
}).help("Mark All as Read")
}
ToolbarItem {
Button(action: {}, label: {
Image(systemName: "arrow.triangle.turn.up.right.circle.fill").foregroundColor(.purple)
}).help("Go to Next Unread")
MacSearchField()
.frame(width: 200)
}
ToolbarItem {
Button(action: {}, label: {
Image(systemName: "star.fill").foregroundColor(.yellow)
AppAssets.nextUnreadArticleImage
}).help("Go to Next Unread").padding(.trailing, 40)
}
ToolbarItem {
Button(action: {}, label: {
AppAssets.toggleStarred
}).help("Mark as Starred")
}
ToolbarItem {
Button(action: {}, label: {
Image(systemName: "checkmark.circle.fill").foregroundColor(.green)
AppAssets.toggleRead
}).help("Mark as Unread")
}
ToolbarItem {
Button(action: {}, label: {
Image(systemName: "safari").foregroundColor(.blue)
AppAssets.openInBrowserImage
}).help("Open in Browser")
}
ToolbarItem {
Button(action: {}, label: {
Image(systemName: "square.and.arrow.up")
AppAssets.shareImage
}).help("Share")
}
ToolbarItem {
MacSearchField()
.frame(width: 300)
}
}
}
.commands {
@ -146,7 +154,9 @@ struct MainApp: App {
SceneNavigationView()
.environmentObject(sceneModel)
.environmentObject(defaults)
}.commands {
.modifier(PreferredColorSchemeModifier(preferredColorScheme: defaults.userInterfaceColorPalette))
}
.commands {
CommandGroup(after: .newItem, addition: {
Button("New Feed", action: {})
.keyboardShortcut("N")

View File

@ -8,43 +8,60 @@
import SwiftUI
fileprivate enum ToolbarSheets {
case none, web, twitter, reddit, folder, settings
}
fileprivate class SidebarToolbarViewModel: ObservableObject {
@Published var showSheet: Bool = false
@Published var sheetToShow: ToolbarSheets = .none {
didSet {
sheetToShow != .none ? (showSheet = true) : (showSheet = false)
}
}
@Published var showActionSheet: Bool = false
@Published var showAddSheet: Bool = false
}
struct SidebarToolbar: View {
@State private var showSettings: Bool = false
@State private var showAddSheet: Bool = false
var addActionSheetButtons = [
Button(action: {}, label: { Text("Add Feed") })
]
@EnvironmentObject private var appSettings: AppDefaults
@StateObject private var viewModel = SidebarToolbarViewModel()
var body: some View {
VStack {
Divider()
HStack(alignment: .center) {
Button(action: {
showSettings = true
viewModel.sheetToShow = .settings
}, label: {
Image(systemName: "gear")
AppAssets.settingsImage
.font(.title3)
.foregroundColor(.accentColor)
}).help("Settings")
Spacer()
Text("Last updated")
.font(.caption)
.foregroundColor(.secondary)
Spacer()
Button(action: {
showAddSheet = true
viewModel.showActionSheet = true
}, label: {
Image(systemName: "plus")
AppAssets.addMenuImage
.font(.title3)
.foregroundColor(.accentColor)
})
.help("Add")
.actionSheet(isPresented: $showAddSheet) {
.actionSheet(isPresented: $viewModel.showActionSheet) {
ActionSheet(title: Text("Add"), buttons: [
.cancel(),
.default(Text("Add Web Feed")),
.default(Text("Add Web Feed"), action: { viewModel.sheetToShow = .web }),
.default(Text("Add Twitter Feed")),
.default(Text("Add Reddit Feed")),
.default(Text("Add Folder"))
@ -56,8 +73,13 @@ struct SidebarToolbar: View {
.padding(.top, 4)
}
.background(VisualEffectBlur(blurStyle: .systemChromeMaterial).edgesIgnoringSafeArea(.bottom))
.sheet(isPresented: $showSettings, onDismiss: { showSettings = false }) {
SettingsView()
.sheet(isPresented: $viewModel.showSheet, onDismiss: { viewModel.sheetToShow = .none }) {
if viewModel.sheetToShow == .web {
AddWebFeedView()
}
if viewModel.sheetToShow == .settings {
SettingsView().modifier(PreferredColorSchemeModifier(preferredColorScheme: appSettings.userInterfaceColorPalette))
}
}
}

View File

@ -14,10 +14,12 @@ struct UnreadCountView: View {
var body: some View {
Text(verbatim: String(count))
.font(.footnote)
.font(.caption)
.fontWeight(.bold)
.padding(.horizontal, 7)
.padding(.vertical, 1)
.background(SwiftUI.Color.gray.opacity(0.5))
.background(AppAssets.sidebarUnreadCountBackground)
.foregroundColor(AppAssets.sidebarUnreadCountForeground)
.cornerRadius(8)
}
}

View File

@ -0,0 +1,19 @@
//
// String+URLChecker.swift
// NetNewsWire
//
// Created by Stuart Breckenridge on 3/7/20.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import Foundation
extension String {
/// Reference: [StackOverflow](https://stackoverflow.com/questions/161738/what-is-the-best-regular-expression-to-check-if-a-string-is-a-valid-url)
var isValidURL: Bool {
let regEx = "^([a-zA-Z0-9\\.\\-]+(\\:[a-zA-Z0-9\\.&amp;%\\$\\-]+)*@)*((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])|localhost|([a-zA-Z0-9\\-]+\\.)*[a-zA-Z0-9\\-]+\\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(\\:[0-9]+)*(/($|[a-zA-Z0-9\\.\\,\\?\\'\\\\\\+&amp;%\\$#\\=~_\\-]+))*$"
let predicate = NSPredicate(format:"SELF MATCHES %@", argumentArray:[regEx])
return predicate.evaluate(with: self)
}
}

View File

@ -0,0 +1,27 @@
//
// PreferredColorSchemeModifier.swift
// NetNewsWire
//
// Created by Maurice Parker on 7/3/20.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import SwiftUI
struct PreferredColorSchemeModifier: ViewModifier {
var preferredColorScheme: UserInterfaceColorPalette
@ViewBuilder
func body(content: Content) -> some View {
switch preferredColorScheme {
case .automatic:
content.preferredColorScheme(nil)
case .dark:
content.preferredColorScheme(.dark)
case .light:
content.preferredColorScheme(.light)
}
}
}

View File

@ -24,6 +24,37 @@ struct TimelineContainerView: View {
timelineModel.delegate = sceneModel
timelineModel.rebuildTimelineItems(feed)
}
.toolbar {
#if os(iOS)
ToolbarItem {
Button(action: {
}, label: {
AppAssets.markAllAsReadImage
.foregroundColor(.accentColor)
}).help("Mark All As Read")
}
ToolbarItem {
Spacer()
}
ToolbarItem {
Text("Last updated")
.font(.caption)
.foregroundColor(.secondary)
}
ToolbarItem {
Spacer()
}
ToolbarItem {
Button(action: {
}, label: {
AppAssets.nextUnreadArticleImage
.resizable()
.scaledToFit()
.frame(width: 22, height: 22, alignment: .center)
})
}
#endif
}
} else {
EmptyView()
}

View File

@ -19,12 +19,10 @@ struct TimelineItemStatusView: View {
.resizable()
.frame(width: 8, height: 8, alignment: .center)
.padding(.all, 2)
.foregroundColor(.accentColor)
case .showStar:
AppAssets.timelineStarred
.resizable()
.frame(width: 10, height: 10, alignment: .center)
.foregroundColor(.yellow)
case .showNone:
AppAssets.timelineUnread
.resizable()

View File

@ -15,6 +15,13 @@ struct TimelineItemView: View {
var timelineItem: TimelineItem
#if os(macOS)
var verticalPadding: CGFloat = 10
#endif
#if os(iOS)
var verticalPadding: CGFloat = 0
#endif
var body: some View {
VStack {
HStack(alignment: .top) {
@ -46,6 +53,7 @@ struct TimelineItemView: View {
}
}
}
.padding(.vertical, verticalPadding)
.onAppear {
articleIconImageLoader.loadImage(for: timelineItem.article)
}

View File

@ -103,6 +103,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
UNUserNotificationCenter.current().delegate = self
userNotificationManager = UserNotificationManager()
NotificationCenter.default.addObserver(self, selector: #selector(userDefaultsDidChange), name: UserDefaults.didChangeNotification, object: nil)
// extensionContainersFile = ExtensionContainersFile()
// extensionFeedAddRequestFile = ExtensionFeedAddRequestFile()
@ -388,3 +391,29 @@ private extension AppDelegate {
}
}
private extension AppDelegate {
@objc func userDefaultsDidChange() {
updateUserInterfaceStyle()
}
var window: UIWindow? {
guard let scene = UIApplication.shared.connectedScenes.first,
let windowSceneDelegate = scene.delegate as? UIWindowSceneDelegate,
let window = windowSceneDelegate.window else {
return nil
}
return window
}
func updateUserInterfaceStyle() {
// switch AppDefaults.shared.userInterfaceColorPalette {
// case .automatic:
// window?.overrideUserInterfaceStyle = .unspecified
// case .light:
// window?.overrideUserInterfaceStyle = .light
// case .dark:
// window?.overrideUserInterfaceStyle = .dark
// }
}
}

View File

@ -155,12 +155,12 @@ struct SettingsView: View {
var appearance: some View {
Section(header: Text("Appearance"), content: {
NavigationLink(
destination: EmptyView(),
destination: ColorPaletteContainerView().environmentObject(settings),
label: {
HStack {
Text("Color Palette")
Spacer()
Text("Automatic")
Text(settings.userInterfaceColorPalette.description)
.foregroundColor(.secondary)
}
})

View File

@ -0,0 +1,60 @@
//
// ColorPaletteContainerView.swift
// Multiplatform iOS
//
// Created by Rizwan on 02/07/20.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import SwiftUI
struct ColorPaletteContainerView: View {
private let colorPalettes = UserInterfaceColorPalette.allCases
@EnvironmentObject private var appSettings: AppDefaults
@Environment(\.presentationMode) var presentationMode
var body: some View {
List {
ForEach.init(0 ..< colorPalettes.count) { index in
Button(action: {
onTapColorPalette(at:index)
}) {
ColorPaletteView(colorPalette: colorPalettes[index])
}
}
}
.listStyle(InsetGroupedListStyle())
.navigationBarTitle("Color Palette", displayMode: .inline)
}
func onTapColorPalette(at index: Int) {
if let colorPalette = UserInterfaceColorPalette(rawValue: index) {
appSettings.userInterfaceColorPalette = colorPalette
}
self.presentationMode.wrappedValue.dismiss()
}
}
struct ColorPaletteView: View {
var colorPalette: UserInterfaceColorPalette
@EnvironmentObject private var appSettings: AppDefaults
var body: some View {
HStack {
Text(colorPalette.description).foregroundColor(.primary)
Spacer()
if colorPalette == appSettings.userInterfaceColorPalette {
Image(systemName: "checkmark")
.foregroundColor(.blue)
}
}
}
}
struct ColorPaletteContainerView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
ColorPaletteContainerView()
}
}
}

View File

@ -21,8 +21,14 @@
175942AB24AD533200585066 /* RefreshInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE4226F4DFA0010922C /* RefreshInterval.swift */; };
1776E88E24AC5F8A00E78166 /* AppDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1776E88D24AC5F8A00E78166 /* AppDefaults.swift */; };
1776E88F24AC5F8A00E78166 /* AppDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1776E88D24AC5F8A00E78166 /* AppDefaults.swift */; };
17925E1724AF41D000D3A4F6 /* String+URLChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17925E1624AF41D000D3A4F6 /* String+URLChecker.swift */; };
17925E1824AF41D000D3A4F6 /* String+URLChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17925E1624AF41D000D3A4F6 /* String+URLChecker.swift */; };
17930ED424AF10EE00A9BA52 /* AddWebFeedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17930ED324AF10EE00A9BA52 /* AddWebFeedView.swift */; };
17930ED524AF10EE00A9BA52 /* AddWebFeedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17930ED324AF10EE00A9BA52 /* AddWebFeedView.swift */; };
179DB1DFBCF9177104B12E0F /* AccountsNewsBlurWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179DBBA2B22A659F81EED6F9 /* AccountsNewsBlurWindowController.swift */; };
179DB3CE822BFCC2D774D9F4 /* AccountsNewsBlurWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179DBBA2B22A659F81EED6F9 /* AccountsNewsBlurWindowController.swift */; };
17D232A824AFF10A0005F075 /* AddWebFeedModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D232A724AFF10A0005F075 /* AddWebFeedModel.swift */; };
17D232A924AFF10A0005F075 /* AddWebFeedModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D232A724AFF10A0005F075 /* AddWebFeedModel.swift */; };
3B3A32A5238B820900314204 /* FeedWranglerAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B3A328B238B820900314204 /* FeedWranglerAccountViewController.swift */; };
3B826DCB2385C84800FC1ADB /* AccountsFeedWrangler.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3B826DB02385C84800FC1ADB /* AccountsFeedWrangler.xib */; };
3B826DCC2385C84800FC1ADB /* AccountsFeedWranglerWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B826DCA2385C84800FC1ADB /* AccountsFeedWranglerWindowController.swift */; };
@ -214,6 +220,8 @@
517A757A24451C0700B553B9 /* OAuthSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 517A755524451BD500B553B9 /* OAuthSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
517A757B24451C1500B553B9 /* OAuthSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 517A755324451BD500B553B9 /* OAuthSwift.framework */; };
517A757C24451C1500B553B9 /* OAuthSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 517A755324451BD500B553B9 /* OAuthSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
5181C5AD24AF89B1002E0F70 /* PreferredColorSchemeModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5181C5AC24AF89B1002E0F70 /* PreferredColorSchemeModifier.swift */; };
5181C5AE24AF89B1002E0F70 /* PreferredColorSchemeModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5181C5AC24AF89B1002E0F70 /* PreferredColorSchemeModifier.swift */; };
5183CCD0226E1E880010922C /* NonIntrinsicLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCCF226E1E880010922C /* NonIntrinsicLabel.swift */; };
5183CCDA226E31A50010922C /* NonIntrinsicImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCD9226E31A50010922C /* NonIntrinsicImageView.swift */; };
5183CCE5226F4DFA0010922C /* RefreshInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE4226F4DFA0010922C /* RefreshInterval.swift */; };
@ -627,6 +635,7 @@
6581C73D20CED60100F4AD34 /* SafariExtensionViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6581C73B20CED60100F4AD34 /* SafariExtensionViewController.xib */; };
6581C74020CED60100F4AD34 /* netnewswire-subscribe-to-feed.js in Resources */ = {isa = PBXBuildFile; fileRef = 6581C73F20CED60100F4AD34 /* netnewswire-subscribe-to-feed.js */; };
6581C74220CED60100F4AD34 /* ToolbarItemIcon.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 6581C74120CED60100F4AD34 /* ToolbarItemIcon.pdf */; };
65CBAD5A24AE03C20006DD91 /* ColorPaletteContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65CBAD5924AE03C20006DD91 /* ColorPaletteContainerView.swift */; };
65ED3FB7235DEF6C0081F399 /* ArticleArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F204DF1FAACBB30076E152 /* ArticleArray.swift */; };
65ED3FB8235DEF6C0081F399 /* CrashReporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848B937121C8C5540038DC0D /* CrashReporter.swift */; };
65ED3FB9235DEF6C0081F399 /* IconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847CD6C9232F4CBF00FAC46D /* IconView.swift */; };
@ -1717,8 +1726,11 @@
1729529A24AA1FD200D65E66 /* MacSearchField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacSearchField.swift; sourceTree = "<group>"; };
172952AF24AA287100D65E66 /* CompactSidebarContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompactSidebarContainerView.swift; sourceTree = "<group>"; };
1776E88D24AC5F8A00E78166 /* AppDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDefaults.swift; sourceTree = "<group>"; };
17925E1624AF41D000D3A4F6 /* String+URLChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+URLChecker.swift"; sourceTree = "<group>"; };
17930ED324AF10EE00A9BA52 /* AddWebFeedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddWebFeedView.swift; sourceTree = "<group>"; };
179DBBA2B22A659F81EED6F9 /* AccountsNewsBlurWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsNewsBlurWindowController.swift; sourceTree = "<group>"; };
17B223DB24AC24D2001E4592 /* TimelineLayoutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineLayoutView.swift; sourceTree = "<group>"; };
17D232A724AFF10A0005F075 /* AddWebFeedModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddWebFeedModel.swift; sourceTree = "<group>"; };
3B3A328B238B820900314204 /* FeedWranglerAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedWranglerAccountViewController.swift; sourceTree = "<group>"; };
3B826DB02385C84800FC1ADB /* AccountsFeedWrangler.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AccountsFeedWrangler.xib; sourceTree = "<group>"; };
3B826DCA2385C84800FC1ADB /* AccountsFeedWranglerWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsFeedWranglerWindowController.swift; sourceTree = "<group>"; };
@ -1835,6 +1847,7 @@
517630222336657E00E15FFF /* WebViewProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewProvider.swift; sourceTree = "<group>"; };
517A745A2443665000B553B9 /* UIPageViewController-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIPageViewController-Extensions.swift"; sourceTree = "<group>"; };
517A754424451BD500B553B9 /* OAuthSwift.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OAuthSwift.xcodeproj; path = submodules/OAuthSwift/OAuthSwift.xcodeproj; sourceTree = "<group>"; };
5181C5AC24AF89B1002E0F70 /* PreferredColorSchemeModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferredColorSchemeModifier.swift; sourceTree = "<group>"; };
5183CCCF226E1E880010922C /* NonIntrinsicLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonIntrinsicLabel.swift; sourceTree = "<group>"; };
5183CCD9226E31A50010922C /* NonIntrinsicImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonIntrinsicImageView.swift; sourceTree = "<group>"; };
5183CCE4226F4DFA0010922C /* RefreshInterval.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshInterval.swift; sourceTree = "<group>"; };
@ -1994,6 +2007,7 @@
6581C73F20CED60100F4AD34 /* netnewswire-subscribe-to-feed.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = "netnewswire-subscribe-to-feed.js"; sourceTree = "<group>"; };
6581C74120CED60100F4AD34 /* ToolbarItemIcon.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = ToolbarItemIcon.pdf; sourceTree = "<group>"; };
6581C74320CED60100F4AD34 /* Subscribe_to_Feed.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Subscribe_to_Feed.entitlements; sourceTree = "<group>"; };
65CBAD5924AE03C20006DD91 /* ColorPaletteContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPaletteContainerView.swift; sourceTree = "<group>"; };
65ED4083235DEF6C0081F399 /* NetNewsWire.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NetNewsWire.app; sourceTree = BUILT_PRODUCTS_DIR; };
65ED409D235DEF770081F399 /* Subscribe to Feed.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Subscribe to Feed.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
65ED409F235DEFF00081F399 /* container-migration.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "container-migration.plist"; sourceTree = "<group>"; };
@ -2394,10 +2408,20 @@
path = View;
sourceTree = "<group>";
};
17930ED224AF10CD00A9BA52 /* Add */ = {
isa = PBXGroup;
children = (
17D232A724AFF10A0005F075 /* AddWebFeedModel.swift */,
17930ED324AF10EE00A9BA52 /* AddWebFeedView.swift */,
);
path = Add;
sourceTree = "<group>";
};
17B223B924AC24A8001E4592 /* Submenus */ = {
isa = PBXGroup;
children = (
17B223DB24AC24D2001E4592 /* TimelineLayoutView.swift */,
65CBAD5924AE03C20006DD91 /* ColorPaletteContainerView.swift */,
);
path = Submenus;
sourceTree = "<group>";
@ -2557,6 +2581,7 @@
isa = PBXGroup;
children = (
514E6C0524AD2B5F00AC6F6E /* Image-Extensions.swift */,
5181C5AC24AF89B1002E0F70 /* PreferredColorSchemeModifier.swift */,
);
path = "SwiftUI Extensions";
sourceTree = "<group>";
@ -2663,10 +2688,10 @@
children = (
51919FED24AB85E400541E64 /* TimelineContainerView.swift */,
51919FF324AB869C00541E64 /* TimelineItem.swift */,
514E6C0124AD29A300AC6F6E /* TimelineItemStatusView.swift */,
514E6BD924ACEA0400AC6F6E /* TimelineItemView.swift */,
51919FF024AB864A00541E64 /* TimelineModel.swift */,
51919FF624AB8B7700541E64 /* TimelineView.swift */,
514E6C0124AD29A300AC6F6E /* TimelineItemStatusView.swift */,
);
path = Timeline;
sourceTree = "<group>";
@ -2748,12 +2773,14 @@
51E499D724A912C200B667CB /* SceneModel.swift */,
51E49A0224A91FF600B667CB /* SceneNavigationView.swift */,
51C0513824A77DF800194D5E /* Assets.xcassets */,
17930ED224AF10CD00A9BA52 /* Add */,
51A576B924AE617B00078888 /* Article */,
51919FB124AAB95300541E64 /* Images */,
514E6BFD24AD252400AC6F6E /* Previews */,
51E499FB24A9135A00B667CB /* Sidebar */,
514E6C0424AD2B0400AC6F6E /* SwiftUI Extensions */,
51919FCB24AB855000541E64 /* Timeline */,
17925E1624AF41D000D3A4F6 /* String+URLChecker.swift */,
);
path = Shared;
sourceTree = "<group>";
@ -4794,6 +4821,7 @@
51E498F524A8085D00B667CB /* TodayFeedDelegate.swift in Sources */,
172952B024AA287100D65E66 /* CompactSidebarContainerView.swift in Sources */,
172199F124AB716900A31D04 /* SidebarToolbar.swift in Sources */,
65CBAD5A24AE03C20006DD91 /* ColorPaletteContainerView.swift in Sources */,
51E4990B24A808C500B667CB /* ImageDownloader.swift in Sources */,
51E498F424A8085D00B667CB /* SmartFeedDelegate.swift in Sources */,
514E6BFF24AD255D00AC6F6E /* PreviewArticles.swift in Sources */,
@ -4808,6 +4836,7 @@
51E4996924A8760C00B667CB /* ArticleStylesManager.swift in Sources */,
51E498F324A8085D00B667CB /* PseudoFeed.swift in Sources */,
51A5769624AE617200078888 /* ArticleContainerView.swift in Sources */,
5181C5AD24AF89B1002E0F70 /* PreferredColorSchemeModifier.swift in Sources */,
51E4996B24A8762D00B667CB /* ArticleExtractor.swift in Sources */,
51E49A0324A91FF600B667CB /* SceneNavigationView.swift in Sources */,
51E4990124A808BB00B667CB /* FaviconURLFinder.swift in Sources */,
@ -4816,6 +4845,7 @@
51A576BE24AE637400078888 /* ArticleView.swift in Sources */,
51E4995324A8734D00B667CB /* RedditFeedProvider-Extensions.swift in Sources */,
172199C924AB228900A31D04 /* SettingsView.swift in Sources */,
17D232A824AFF10A0005F075 /* AddWebFeedModel.swift in Sources */,
51E4994224A8713C00B667CB /* ArticleStatusSyncTimer.swift in Sources */,
51E498F624A8085D00B667CB /* SearchFeedDelegate.swift in Sources */,
51E498F224A8085D00B667CB /* SmartFeedsController.swift in Sources */,
@ -4828,6 +4858,7 @@
514E6C0624AD2B5F00AC6F6E /* Image-Extensions.swift in Sources */,
51E4995624A8734D00B667CB /* TwitterFeedProvider-Extensions.swift in Sources */,
5125E6CA24AE461D002A7562 /* TimelineLayoutView.swift in Sources */,
17925E1724AF41D000D3A4F6 /* String+URLChecker.swift in Sources */,
51E4996824A8760C00B667CB /* ArticleStyle.swift in Sources */,
51E4990024A808BB00B667CB /* FaviconGenerator.swift in Sources */,
51E4997124A8764C00B667CB /* ActivityType.swift in Sources */,
@ -4839,6 +4870,7 @@
51E4993C24A8709900B667CB /* AppDelegate.swift in Sources */,
51E498F924A8085D00B667CB /* SmartFeed.swift in Sources */,
51A576BB24AE621800078888 /* ArticleModel.swift in Sources */,
17930ED424AF10EE00A9BA52 /* AddWebFeedView.swift in Sources */,
51E4995124A8734D00B667CB /* ExtensionPointManager.swift in Sources */,
51E4990C24A808C500B667CB /* AuthorAvatarDownloader.swift in Sources */,
51E4992124A8095000B667CB /* RSImage-Extensions.swift in Sources */,
@ -4865,6 +4897,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
17930ED524AF10EE00A9BA52 /* AddWebFeedView.swift in Sources */,
51E4993A24A8708800B667CB /* AppDelegate.swift in Sources */,
51E498CE24A8085D00B667CB /* UnreadFeed.swift in Sources */,
51E498C724A8085D00B667CB /* StarredFeedDelegate.swift in Sources */,
@ -4887,11 +4920,13 @@
51E4992224A8095600B667CB /* URL-Extensions.swift in Sources */,
51E4990424A808C300B667CB /* WebFeedIconDownloader.swift in Sources */,
51E498CB24A8085D00B667CB /* TodayFeedDelegate.swift in Sources */,
17D232A924AFF10A0005F075 /* AddWebFeedModel.swift in Sources */,
51E4993324A867E700B667CB /* AppNotifications.swift in Sources */,
51E4990624A808C300B667CB /* ImageDownloader.swift in Sources */,
51E4994F24A8734C00B667CB /* TwitterFeedProvider-Extensions.swift in Sources */,
51E498CA24A8085D00B667CB /* SmartFeedDelegate.swift in Sources */,
51E4990524A808C300B667CB /* FeaturedImageDownloader.swift in Sources */,
5181C5AE24AF89B1002E0F70 /* PreferredColorSchemeModifier.swift in Sources */,
51E4991624A8090300B667CB /* ArticleUtilities.swift in Sources */,
51919FF224AB864A00541E64 /* TimelineModel.swift in Sources */,
51E4991A24A8090F00B667CB /* IconImage.swift in Sources */,
@ -4901,6 +4936,7 @@
51E4993424A867E700B667CB /* UserInfoKey.swift in Sources */,
1776E88F24AC5F8A00E78166 /* AppDefaults.swift in Sources */,
1729529724AA1CD000D65E66 /* MacPreferencesView.swift in Sources */,
17925E1824AF41D000D3A4F6 /* String+URLChecker.swift in Sources */,
51E4994C24A8734C00B667CB /* RedditFeedProvider-Extensions.swift in Sources */,
1729529324AA1CAA00D65E66 /* AccountsPreferencesView.swift in Sources */,
51919FAD24AA8CCA00541E64 /* UnreadCountView.swift in Sources */,

View File

@ -1,10 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<PreActions>
<ExecutionAction
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
<ActionContent
title = "Run Script"
scriptText = "&quot;${PROJECT_DIR}/buildscripts/updateSecrets.sh&quot;&#10;">
<EnvironmentBuildable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "51C0513C24A77DF800194D5E"
BuildableName = "NetNewsWire.app"
BlueprintName = "Multiplatform iOS"
ReferencedContainer = "container:NetNewsWire.xcodeproj">
</BuildableReference>
</EnvironmentBuildable>
</ActionContent>
</ExecutionAction>
</PreActions>
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"

View File

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<PreActions>
<ExecutionAction
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
<ActionContent
title = "Run Script"
scriptText = "&quot;${PROJECT_DIR}/buildscripts/updateSecrets.sh&quot;&#10;">
<EnvironmentBuildable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "51C0514324A77DF800194D5E"
BuildableName = "NetNewsWire.app"
BlueprintName = "Multiplatform macOS"
ReferencedContainer = "container:NetNewsWire.xcodeproj">
</BuildableReference>
</EnvironmentBuildable>
</ActionContent>
</ExecutionAction>
</PreActions>
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "51C0514324A77DF800194D5E"
BuildableName = "NetNewsWire.app"
BlueprintName = "Multiplatform macOS"
ReferencedContainer = "container:NetNewsWire.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "51C0514324A77DF800194D5E"
BuildableName = "NetNewsWire.app"
BlueprintName = "Multiplatform macOS"
ReferencedContainer = "container:NetNewsWire.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "51C0514324A77DF800194D5E"
BuildableName = "NetNewsWire.app"
BlueprintName = "Multiplatform macOS"
ReferencedContainer = "container:NetNewsWire.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -12,7 +12,6 @@ import RSParser
import AppKit
typealias Font = NSFont
typealias FontDescriptor = NSFontDescriptor
typealias Color = NSColor
private let boldTrait = NSFontDescriptor.SymbolicTraits.bold
private let italicTrait = NSFontDescriptor.SymbolicTraits.italic
@ -21,7 +20,6 @@ private let monoSpaceTrait = NSFontDescriptor.SymbolicTraits.monoSpace
import UIKit
typealias Font = UIFont
typealias FontDescriptor = UIFontDescriptor
typealias Color = UIColor
private let boldTrait = UIFontDescriptor.SymbolicTraits.traitBold
private let italicTrait = UIFontDescriptor.SymbolicTraits.traitItalic

View File

@ -1,23 +1,18 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
},
"colors" : [
{
"idiom" : "universal",
"color" : {
"color-space" : "srgb",
"components" : {
"red" : "0x08",
"alpha" : "1.000",
"blue" : "0xEE",
"green" : "0x6A"
}
"green" : "0x6A",
"red" : "0x08"
}
},
"idiom" : "universal"
},
{
"idiom" : "universal",
"appearances" : [
{
"appearance" : "luminosity",
@ -27,12 +22,17 @@
"color" : {
"color-space" : "srgb",
"components" : {
"red" : "0x5E",
"alpha" : "1.000",
"blue" : "0xF4",
"green" : "0x9E"
"green" : "0x9E",
"red" : "0x5E"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
}
]
}

View File

@ -1,20 +1,20 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
},
"colors" : [
{
"idiom" : "universal",
"color" : {
"color-space" : "srgb",
"components" : {
"red" : "249",
"alpha" : "1.000",
"blue" : "52",
"green" : "198"
"green" : "198",
"red" : "249"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
}
]
}