Merge pull request #2 from Ranchero-Software/swiftui

Swiftui
This commit is contained in:
Stuart Breckenridge 2020-06-30 09:25:38 +08:00 committed by GitHub
commit 8dc2b1ec18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 242 additions and 5 deletions

View File

@ -0,0 +1,58 @@
//
// FeedImageLoader.swift
// NetNewsWire
//
// Created by Maurice Parker on 6/29/20.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import SwiftUI
import Account
final class FeedImageLoader: ObservableObject {
private var feed: Feed?
@Published var image: IconImage?
init() {
NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(webFeedIconDidBecomeAvailable(_:)), name: .WebFeedIconDidBecomeAvailable, object: nil)
}
func loadImage(for feed: Feed) {
self.feed = feed
if let webFeed = feed as? WebFeed {
if let feedIconImage = appDelegate.webFeedIconDownloader.icon(for: webFeed) {
image = feedIconImage
return
}
if let faviconImage = appDelegate.faviconDownloader.faviconAsIcon(for: webFeed) {
image = faviconImage
return
}
}
if let smallIconProvider = feed as? SmallIconProvider {
image = smallIconProvider.smallIcon
}
}
}
private extension FeedImageLoader {
@objc func faviconDidBecomeAvailable(_ note: Notification) {
guard let feed = feed else { return }
loadImage(for: feed)
}
@objc func webFeedIconDidBecomeAvailable(_ note: Notification) {
guard let feed = feed as? WebFeed, let noteFeed = note.userInfo?[UserInfoKey.webFeed] as? WebFeed, feed == noteFeed else {
return
}
loadImage(for: feed)
}
}

View File

@ -0,0 +1,37 @@
//
// IconImageView.swift
// NetNewsWire
//
// Created by Maurice Parker on 6/29/20.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import SwiftUI
struct IconImageView: View {
var iconImage: IconImage
var body: some View {
#if os(macOS)
return Image(nsImage: iconImage.image)
.resizable()
.scaledToFit()
.frame(width: 20, height: 20, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
.cornerRadius(4)
#endif
#if os(iOS)
return Image(uiImage: iconImage.image)
.resizable()
.scaledToFit()
.frame(width: 20, height: 20, alignment: /*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)
.cornerRadius(4)
#endif
}
}
struct IconImageView_Previews: PreviewProvider {
static var previews: some View {
IconImageView(iconImage: IconImage(AppAssets.faviconTemplateImage))
}
}

View File

@ -18,7 +18,7 @@ struct SceneNavigationView: View {
NavigationView {
#if os(macOS)
RegularSidebarContainerView()
.frame(minWidth: 100, idealWidth: 150, maxWidth: 200, maxHeight: .infinity)
.frame(minWidth: 100, idealWidth: 150, maxHeight: .infinity)
#else
if horizontalSizeClass == .compact {
CompactSidebarContainerView()

View File

@ -47,6 +47,9 @@ struct SidebarItem: Identifiable {
self.id = .feed(feed.feedID!)
self.represented = feed
self.unreadCount = unreadCount
if let container = feed as? Container, container.hasAtLeastOneWebFeed() {
self.children = [SidebarItem]()
}
}
mutating func addChild(_ sidebarItem: SidebarItem) {

View File

@ -0,0 +1,35 @@
//
// SidebarItemView.swift
// NetNewsWire
//
// Created by Maurice Parker on 6/29/20.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import SwiftUI
import Account
struct SidebarItemView: View {
@StateObject var feedImageLoader = FeedImageLoader()
var sidebarItem: SidebarItem
var body: some View {
HStack {
if let image = feedImageLoader.image {
IconImageView(iconImage: image)
}
Text(verbatim: sidebarItem.nameForDisplay)
Spacer()
if sidebarItem.unreadCount > 0 {
UnreadCountView(count: sidebarItem.unreadCount)
}
}
.onAppear {
if let feed = sidebarItem.represented as? Feed {
feedImageLoader.loadImage(for: feed)
}
}
}
}

View File

@ -7,6 +7,7 @@
//
import Foundation
import RSCore
import Account
protocol SidebarModelDelegate: class {
@ -20,6 +21,12 @@ class SidebarModel: ObservableObject {
@Published var sidebarItems = [SidebarItem]()
init() {
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidInitialize(_:)), name: .UnreadCountDidInitialize, object: nil)
}
// MARK: API
func rebuildSidebarItems() {
guard let delegate = delegate else { return }
var items = [SidebarItem]()
@ -30,7 +37,45 @@ class SidebarModel: ObservableObject {
}
items.append(smartFeedControllerItem)
for account in AccountManager.shared.sortedActiveAccounts {
var accountItem = SidebarItem(account)
for webFeed in sort(account.topLevelWebFeeds) {
accountItem.addChild(SidebarItem(webFeed, unreadCount: delegate.unreadCount(for: webFeed)))
}
for folder in sort(account.folders ?? Set<Folder>()) {
var folderItem = SidebarItem(folder, unreadCount: delegate.unreadCount(for: folder))
for webFeed in sort(folder.topLevelWebFeeds) {
folderItem.addChild(SidebarItem(webFeed, unreadCount: delegate.unreadCount(for: webFeed)))
}
accountItem.addChild(folderItem)
}
items.append(accountItem)
}
sidebarItems = items
}
}
// MARK: Private
private extension SidebarModel {
@objc func unreadCountDidInitialize(_ notification: Notification) {
guard notification.object is AccountManager else {
return
}
rebuildSidebarItems()
}
func sort(_ folders: Set<Folder>) -> [Folder] {
return folders.sorted(by: { $0.nameForDisplay.localizedStandardCompare($1.nameForDisplay) == .orderedAscending })
}
func sort(_ feeds: Set<WebFeed>) -> [Feed] {
return feeds.sorted(by: { $0.nameForDisplay.localizedStandardCompare($1.nameForDisplay) == .orderedAscending })
}
}

View File

@ -14,10 +14,8 @@ struct SidebarView: View {
var body: some View {
List {
ForEach(sidebarModel.sidebarItems) { section in
OutlineGroup(sidebarModel.sidebarItems, children: \.children) { sidebarItem in
Text(sidebarItem.nameForDisplay)
}
OutlineGroup(sidebarModel.sidebarItems, children: \.children) { sidebarItem in
SidebarItemView(sidebarItem: sidebarItem)
}
}
}

View File

@ -0,0 +1,29 @@
//
// UnreadCountView.swift
// NetNewsWire
//
// Created by Maurice Parker on 6/29/20.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import SwiftUI
struct UnreadCountView: View {
var count: Int
var body: some View {
Text(verbatim: String(count))
.font(.footnote)
.padding(.horizontal, 7)
.padding(.vertical, 1)
.background(SwiftUI.Color.gray.opacity(0.5))
.cornerRadius(8)
}
}
struct UnreadCountView_Previews: PreviewProvider {
static var previews: some View {
UnreadCountView(count: 123)
}
}

View File

@ -208,6 +208,14 @@
518ED21D23D0F26000E0A862 /* UIViewController-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 518ED21C23D0F26000E0A862 /* UIViewController-Extensions.swift */; };
51919FA624AA64B000541E64 /* SidebarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51919FA524AA64B000541E64 /* SidebarView.swift */; };
51919FA724AA64B000541E64 /* SidebarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51919FA524AA64B000541E64 /* SidebarView.swift */; };
51919FAC24AA8CCA00541E64 /* UnreadCountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51919FAB24AA8CCA00541E64 /* UnreadCountView.swift */; };
51919FAD24AA8CCA00541E64 /* UnreadCountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51919FAB24AA8CCA00541E64 /* UnreadCountView.swift */; };
51919FAF24AA8EFA00541E64 /* SidebarItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51919FAE24AA8EFA00541E64 /* SidebarItemView.swift */; };
51919FB024AA8EFA00541E64 /* SidebarItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51919FAE24AA8EFA00541E64 /* SidebarItemView.swift */; };
51919FB324AAB97900541E64 /* FeedImageLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51919FB224AAB97900541E64 /* FeedImageLoader.swift */; };
51919FB424AAB97900541E64 /* FeedImageLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51919FB224AAB97900541E64 /* FeedImageLoader.swift */; };
51919FB624AABCA100541E64 /* IconImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51919FB524AABCA100541E64 /* IconImageView.swift */; };
51919FB724AABCA100541E64 /* IconImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51919FB524AABCA100541E64 /* IconImageView.swift */; };
51934CCB230F599B006127BE /* InteractiveNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51934CC1230F5963006127BE /* InteractiveNavigationController.swift */; };
51934CCE2310792F006127BE /* ActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51934CCD2310792F006127BE /* ActivityManager.swift */; };
51938DF2231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51938DF1231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift */; };
@ -1797,6 +1805,10 @@
518B2EE92351B4C200400001 /* NetNewsWire_iOSTests_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = NetNewsWire_iOSTests_target.xcconfig; sourceTree = "<group>"; };
518ED21C23D0F26000E0A862 /* UIViewController-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController-Extensions.swift"; sourceTree = "<group>"; };
51919FA524AA64B000541E64 /* SidebarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarView.swift; sourceTree = "<group>"; };
51919FAB24AA8CCA00541E64 /* UnreadCountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnreadCountView.swift; sourceTree = "<group>"; };
51919FAE24AA8EFA00541E64 /* SidebarItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarItemView.swift; sourceTree = "<group>"; };
51919FB224AAB97900541E64 /* FeedImageLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedImageLoader.swift; sourceTree = "<group>"; };
51919FB524AABCA100541E64 /* IconImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconImageView.swift; sourceTree = "<group>"; };
51934CC1230F5963006127BE /* InteractiveNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InteractiveNavigationController.swift; sourceTree = "<group>"; };
51934CCD2310792F006127BE /* ActivityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityManager.swift; sourceTree = "<group>"; };
51938DF1231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTimelineFeedDelegate.swift; sourceTree = "<group>"; };
@ -2565,6 +2577,15 @@
path = NNW3;
sourceTree = "<group>";
};
51919FB124AAB95300541E64 /* Images */ = {
isa = PBXGroup;
children = (
51919FB224AAB97900541E64 /* FeedImageLoader.swift */,
51919FB524AABCA100541E64 /* IconImageView.swift */,
);
path = Images;
sourceTree = "<group>";
};
51934CCC231078DC006127BE /* Activity */ = {
isa = PBXGroup;
children = (
@ -2629,6 +2650,7 @@
51E4992824A866F000B667CB /* AppDefaults.swift */,
51E4995824A873F900B667CB /* ErrorHandler.swift */,
51C0513824A77DF800194D5E /* Assets.xcassets */,
51919FB124AAB95300541E64 /* Images */,
51E499FB24A9135A00B667CB /* Sidebar */,
);
path = Shared;
@ -2810,6 +2832,8 @@
51408B7D24A9EC6F0073CF4E /* SidebarItem.swift */,
51E499FC24A9137600B667CB /* SidebarModel.swift */,
51919FA524AA64B000541E64 /* SidebarView.swift */,
51919FAB24AA8CCA00541E64 /* UnreadCountView.swift */,
51919FAE24AA8EFA00541E64 /* SidebarItemView.swift */,
);
path = Sidebar;
sourceTree = "<group>";
@ -4650,6 +4674,7 @@
51E498F124A8085D00B667CB /* StarredFeedDelegate.swift in Sources */,
51E498FF24A808BB00B667CB /* SingleFaviconDownloader.swift in Sources */,
51E4997224A8784300B667CB /* DefaultFeedsImporter.swift in Sources */,
51919FAF24AA8EFA00541E64 /* SidebarItemView.swift in Sources */,
51E4990D24A808C500B667CB /* RSHTMLMetadata+Extension.swift in Sources */,
51E49A0024A91FC100B667CB /* RegularSidebarContainerView.swift in Sources */,
51E4995C24A875F300B667CB /* ArticleRenderer.swift in Sources */,
@ -4679,6 +4704,7 @@
51E4994224A8713C00B667CB /* ArticleStatusSyncTimer.swift in Sources */,
51E498F624A8085D00B667CB /* SearchFeedDelegate.swift in Sources */,
51E498F224A8085D00B667CB /* SmartFeedsController.swift in Sources */,
51919FB624AABCA100541E64 /* IconImageView.swift in Sources */,
51919FA624AA64B000541E64 /* SidebarView.swift in Sources */,
51E4997024A8764C00B667CB /* ActivityManager.swift in Sources */,
51E4990F24A808CC00B667CB /* HTMLMetadataDownloader.swift in Sources */,
@ -4691,6 +4717,7 @@
51E4997124A8764C00B667CB /* ActivityType.swift in Sources */,
51E4991E24A8094300B667CB /* RSImage-AppIcons.swift in Sources */,
51E499D824A912C200B667CB /* SceneModel.swift in Sources */,
51919FB324AAB97900541E64 /* FeedImageLoader.swift in Sources */,
51E4991324A808FB00B667CB /* AddWebFeedDefaultContainer.swift in Sources */,
51E4993C24A8709900B667CB /* AppDelegate.swift in Sources */,
51E498F924A8085D00B667CB /* SmartFeed.swift in Sources */,
@ -4699,6 +4726,7 @@
51E4992124A8095000B667CB /* RSImage-Extensions.swift in Sources */,
51E4990324A808BB00B667CB /* FaviconDownloader.swift in Sources */,
51E4990224A808BB00B667CB /* ColorHash.swift in Sources */,
51919FAC24AA8CCA00541E64 /* UnreadCountView.swift in Sources */,
51E4991924A8090A00B667CB /* CacheCleaner.swift in Sources */,
51E498F724A8085D00B667CB /* SearchTimelineFeedDelegate.swift in Sources */,
51E4993524A867E800B667CB /* AppNotifications.swift in Sources */,
@ -4717,12 +4745,14 @@
51E4993A24A8708800B667CB /* AppDelegate.swift in Sources */,
51E498CE24A8085D00B667CB /* UnreadFeed.swift in Sources */,
51E498C724A8085D00B667CB /* StarredFeedDelegate.swift in Sources */,
51919FB724AABCA100541E64 /* IconImageView.swift in Sources */,
51E498FA24A808BA00B667CB /* SingleFaviconDownloader.swift in Sources */,
51E4993F24A8713B00B667CB /* ArticleStatusSyncTimer.swift in Sources */,
51E4993724A8680E00B667CB /* Reachability.swift in Sources */,
51E4994B24A8734C00B667CB /* SendToMicroBlogCommand.swift in Sources */,
51E4996F24A8764C00B667CB /* ActivityType.swift in Sources */,
51E4994E24A8734C00B667CB /* SendToMarsEditCommand.swift in Sources */,
51919FB024AA8EFA00541E64 /* SidebarItemView.swift in Sources */,
51E4996624A8760B00B667CB /* ArticleStyle.swift in Sources */,
51E4996C24A8762D00B667CB /* ExtractedArticle.swift in Sources */,
51E4990824A808C300B667CB /* RSHTMLMetadata+Extension.swift in Sources */,
@ -4745,11 +4775,13 @@
1729529724AA1CD000D65E66 /* MacPreferencesView.swift in Sources */,
51E4994C24A8734C00B667CB /* RedditFeedProvider-Extensions.swift in Sources */,
1729529324AA1CAA00D65E66 /* AccountsPreferencesView.swift in Sources */,
51919FAD24AA8CCA00541E64 /* UnreadCountView.swift in Sources */,
51E4994124A8713B00B667CB /* RefreshInterval.swift in Sources */,
51E498C924A8085D00B667CB /* PseudoFeed.swift in Sources */,
51E498FC24A808BA00B667CB /* FaviconURLFinder.swift in Sources */,
51E4991C24A8092000B667CB /* NSAttributedString+NetNewsWire.swift in Sources */,
51E499D924A912C200B667CB /* SceneModel.swift in Sources */,
51919FB424AAB97900541E64 /* FeedImageLoader.swift in Sources */,
51E4994A24A8734C00B667CB /* ExtensionPointManager.swift in Sources */,
51E4996D24A8762D00B667CB /* ArticleExtractor.swift in Sources */,
51E4994024A8713B00B667CB /* AccountRefreshTimer.swift in Sources */,