Merge branch 'main' of https://github.com/Ranchero-Software/NetNewsWire into main
This commit is contained in:
commit
46085d0a3d
|
@ -194,6 +194,7 @@ final class CloudKitAccountDelegate: AccountDelegate {
|
|||
removeWebFeedFromCloud(for: account, with: feed, from: container) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
account.clearWebFeedMetadata(feed)
|
||||
container.removeWebFeed(feed)
|
||||
completion(.success(()))
|
||||
case .failure(let error):
|
||||
|
|
|
@ -280,7 +280,7 @@ extension RedditFeedProvider: OAuth2SwiftProvider {
|
|||
consumerSecret: "",
|
||||
authorizeUrl: "https://www.reddit.com/api/v1/authorize.compact?",
|
||||
accessTokenUrl: "https://www.reddit.com/api/v1/access_token",
|
||||
responseType: "token")
|
||||
responseType: "code")
|
||||
oauth2.accessTokenBasicAuthentification = true
|
||||
return oauth2
|
||||
}
|
||||
|
@ -293,12 +293,7 @@ extension RedditFeedProvider: OAuth2SwiftProvider {
|
|||
let state = generateState(withLength: 20)
|
||||
let scope = "identity mysubreddits read"
|
||||
let params = [
|
||||
"client_id" : SecretsManager.provider.redditConsumerKey,
|
||||
"response_type" : "code",
|
||||
"state" : state,
|
||||
"redirect_uri" : "netnewswire://success",
|
||||
"duration" : "permanent",
|
||||
"scope" : scope
|
||||
]
|
||||
return (state: state, scope: scope, params: params)
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ final class RedditLinkData: Codable {
|
|||
guard let url = url else { return "" }
|
||||
|
||||
if url.hasSuffix(".gif") {
|
||||
return "<img src=\"\(url)\">"
|
||||
return "<img class=\"nnw-nozoom\" src=\"\(url)\">"
|
||||
}
|
||||
|
||||
if isVideo ?? false, let videoURL = media?.video?.hlsURL {
|
||||
|
@ -109,12 +109,25 @@ final class RedditLinkData: Codable {
|
|||
if let width = media?.video?.width, let height = media?.video?.height {
|
||||
html += "width=\"\(width)\" height=\"\(height)\" "
|
||||
}
|
||||
html += "src=\"\(videoURL)\" autoplay muted loop></video>"
|
||||
html += "src=\"\(videoURL)\"></video>"
|
||||
return html
|
||||
}
|
||||
|
||||
if let imageVariantURL = preview?.images?.first?.variants?.mp4?.source?.url {
|
||||
var html = "<video class=\"nnwAnimatedGIF\" "
|
||||
if let previewImageURL = preview?.images?.first?.source?.url {
|
||||
html += "poster=\"\(previewImageURL)\" "
|
||||
}
|
||||
if let width = preview?.images?.first?.variants?.mp4?.source?.width, let height = preview?.images?.first?.variants?.mp4?.source?.height {
|
||||
html += "width=\"\(width)\" height=\"\(height)\" "
|
||||
}
|
||||
html += "src=\"\(imageVariantURL)\" autoplay muted loop></video>"
|
||||
html += linkURL(url)
|
||||
return html
|
||||
}
|
||||
|
||||
if let videoPreviewURL = preview?.videoPreview?.url {
|
||||
var html = "<video "
|
||||
var html = "<video class=\"nnwAnimatedGIF\" "
|
||||
if let previewImageURL = preview?.images?.first?.source?.url {
|
||||
html += "poster=\"\(previewImageURL)\" "
|
||||
}
|
||||
|
|
|
@ -23,6 +23,42 @@ struct RedditPreview: Codable {
|
|||
struct RedditPreviewImage: Codable {
|
||||
|
||||
let source: RedditPreviewImageSource?
|
||||
let variants: RedditPreviewImageVariants?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case source = "source"
|
||||
case variants = "variants"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct RedditPreviewImageSource: Codable {
|
||||
|
||||
let url: String?
|
||||
let width: Int?
|
||||
let height: Int?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case url = "url"
|
||||
case width = "width"
|
||||
case height = "height"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct RedditPreviewImageVariants: Codable {
|
||||
|
||||
let mp4: RedditPreviewImageVariantsMP4?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case mp4 = "mp4"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct RedditPreviewImageVariantsMP4: Codable {
|
||||
|
||||
let source: RedditPreviewImageVariantsMP4Source?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case source = "source"
|
||||
|
@ -30,7 +66,7 @@ struct RedditPreviewImage: Codable {
|
|||
|
||||
}
|
||||
|
||||
struct RedditPreviewImageSource: Codable {
|
||||
struct RedditPreviewImageVariantsMP4Source: Codable {
|
||||
|
||||
let url: String?
|
||||
let width: Int?
|
||||
|
|
|
@ -36,8 +36,10 @@ struct TwitterExtendedMedia: Codable {
|
|||
switch type {
|
||||
case "photo":
|
||||
html += renderPhotoAsHTML()
|
||||
case "video", "animated_gif":
|
||||
case "video":
|
||||
html += renderVideoAsHTML()
|
||||
case "animated_gif":
|
||||
html += renderAnimatedGIFAsHTML()
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
@ -74,6 +76,21 @@ private extension TwitterExtendedMedia {
|
|||
return html
|
||||
}
|
||||
|
||||
func renderAnimatedGIFAsHTML() -> String {
|
||||
guard let bestVariantURL = findBestVariant()?.url else { return "" }
|
||||
|
||||
var html = "<video class=\"nnwAnimatedGIF\" "
|
||||
|
||||
if let httpsMediaURL = httpsMediaURL {
|
||||
html += "poster=\"\(httpsMediaURL)\" "
|
||||
} else if let mediaURL = mediaURL {
|
||||
html += "poster=\"\(mediaURL)\" "
|
||||
}
|
||||
|
||||
html += "src=\"\(bestVariantURL)\" autoplay muted loop></video>"
|
||||
return html
|
||||
}
|
||||
|
||||
func findBestVariant() -> TwitterVideo.Variant? {
|
||||
var best: TwitterVideo.Variant? = nil
|
||||
if let variants = video?.variants {
|
||||
|
|
|
@ -59,6 +59,20 @@ final class FeedlyCreateFeedsForCollectionFoldersOperation: FeedlyOperation {
|
|||
|
||||
// find an existing feed previously added to the account
|
||||
if let feed = account.existingWebFeed(withWebFeedID: collectionFeed.id) {
|
||||
|
||||
// If the feed was renamed on Feedly, ensure we ingest the new name.
|
||||
if feed.nameForDisplay != collectionFeed.title {
|
||||
feed.name = collectionFeed.title
|
||||
|
||||
// Let the rest of the app (e.g.: the sidebar) know the feed name changed
|
||||
// `editedName` would post this if its value is changing.
|
||||
// Setting the `name` property has no side effects like this.
|
||||
if feed.editedName != nil {
|
||||
feed.editedName = nil
|
||||
} else {
|
||||
feed.postDisplayNameDidChangeNotification()
|
||||
}
|
||||
}
|
||||
return (feed, folder)
|
||||
} else {
|
||||
// find an existing feed we created below in an earlier value
|
||||
|
|
|
@ -312,14 +312,14 @@ private extension LocalAccountDelegate {
|
|||
return
|
||||
}
|
||||
|
||||
let feed = account.createWebFeed(with: nil, url: url.absoluteString, webFeedID: url.absoluteString, homePageURL: nil)
|
||||
feed.editedName = editedName
|
||||
container.addWebFeed(feed)
|
||||
|
||||
InitialFeedDownloader.download(url) { parsedFeed in
|
||||
self.refreshProgress.completeTask()
|
||||
|
||||
if let parsedFeed = parsedFeed {
|
||||
let feed = account.createWebFeed(with: nil, url: url.absoluteString, webFeedID: url.absoluteString, homePageURL: nil)
|
||||
feed.editedName = editedName
|
||||
container.addWebFeed(feed)
|
||||
|
||||
account.update(feed, with: parsedFeed, {_ in
|
||||
BatchUpdate.shared.end()
|
||||
completion(.success(feed))
|
||||
|
|
|
@ -263,7 +263,7 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
|
|||
switch result {
|
||||
case .success:
|
||||
DispatchQueue.main.async {
|
||||
self.clearFolderRelationship(for: feed, withFolderName: folder.name ?? "")
|
||||
self.clearFolderRelationship(for: feed, folderExternalID: folder.externalID)
|
||||
}
|
||||
case .failure(let error):
|
||||
os_log(.error, log: self.log, "Remove feed error: %@.", error.localizedDescription)
|
||||
|
@ -390,8 +390,33 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
|
|||
}
|
||||
|
||||
func removeWebFeed(for account: Account, with feed: WebFeed, from container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
account.clearWebFeedMetadata(feed)
|
||||
deleteSubscription(for: account, with: feed, from: container, completion: completion)
|
||||
guard let subscriptionID = feed.externalID else {
|
||||
completion(.failure(ReaderAPIAccountDelegateError.invalidParameter))
|
||||
return
|
||||
}
|
||||
|
||||
refreshProgress.addToNumberOfTasksAndRemaining(1)
|
||||
caller.deleteSubscription(subscriptionID: subscriptionID) { result in
|
||||
self.refreshProgress.completeTask()
|
||||
switch result {
|
||||
case .success:
|
||||
DispatchQueue.main.async {
|
||||
account.clearWebFeedMetadata(feed)
|
||||
account.removeWebFeed(feed)
|
||||
if let folders = account.folders {
|
||||
for folder in folders {
|
||||
folder.removeWebFeed(feed)
|
||||
}
|
||||
}
|
||||
completion(.success(()))
|
||||
}
|
||||
case .failure(let error):
|
||||
DispatchQueue.main.async {
|
||||
let wrappedError = AccountError.wrappedError(error: error, account: account)
|
||||
completion(.failure(wrappedError))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func moveWebFeed(for account: Account, with feed: WebFeed, from: Container, to: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
|
@ -410,14 +435,14 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
|
|||
}
|
||||
|
||||
func addWebFeed(for account: Account, with feed: WebFeed, to container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
if let folder = container as? Folder, let feedName = feed.externalID {
|
||||
if let folder = container as? Folder, let feedExternalID = feed.externalID {
|
||||
refreshProgress.addToNumberOfTasksAndRemaining(1)
|
||||
caller.createTagging(subscriptionID: feedName, tagName: folder.name ?? "") { result in
|
||||
caller.createTagging(subscriptionID: feedExternalID, tagName: folder.name ?? "") { result in
|
||||
self.refreshProgress.completeTask()
|
||||
switch result {
|
||||
case .success:
|
||||
DispatchQueue.main.async {
|
||||
self.saveFolderRelationship(for: feed, withFolderName: folder.name ?? "", id: feed.externalID!)
|
||||
self.saveFolderRelationship(for: feed, folderExternalID: folder.externalID, feedExternalID: feedExternalID)
|
||||
account.removeWebFeed(feed)
|
||||
folder.addWebFeed(feed)
|
||||
completion(.success(()))
|
||||
|
@ -561,11 +586,21 @@ private extension ReaderAPIAccountDelegate {
|
|||
caller.retrieveTags { result in
|
||||
switch result {
|
||||
case .success(let tags):
|
||||
BatchUpdate.shared.perform {
|
||||
self.syncFolders(account, tags)
|
||||
}
|
||||
self.refreshProgress.completeTask()
|
||||
self.refreshFeeds(account, completion: completion)
|
||||
self.caller.retrieveSubscriptions { result in
|
||||
self.refreshProgress.completeTask()
|
||||
switch result {
|
||||
case .success(let subscriptions):
|
||||
BatchUpdate.shared.perform {
|
||||
self.syncFolders(account, tags)
|
||||
self.syncFeeds(account, subscriptions)
|
||||
self.syncFeedFolderRelationship(account, subscriptions)
|
||||
}
|
||||
completion(.success(()))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
@ -575,58 +610,45 @@ private extension ReaderAPIAccountDelegate {
|
|||
func syncFolders(_ account: Account, _ tags: [ReaderAPITag]?) {
|
||||
guard let tags = tags else { return }
|
||||
assert(Thread.isMainThread)
|
||||
|
||||
let folderTags = tags.filter{ $0.tagID.contains("/label/") }
|
||||
guard !folderTags.isEmpty else { return }
|
||||
|
||||
os_log(.debug, log: log, "Syncing folders with %ld tags.", folderTags.count)
|
||||
|
||||
os_log(.debug, log: log, "Syncing folders with %ld tags.", tags.count)
|
||||
|
||||
let readerFolderNames = tags.compactMap { $0.folderName }
|
||||
let readerFolderExternalIDs = folderTags.compactMap { $0.tagID }
|
||||
|
||||
// Delete any folders not at Reader
|
||||
if let folders = account.folders {
|
||||
folders.forEach { folder in
|
||||
if !readerFolderNames.contains(folder.name ?? "") {
|
||||
if !readerFolderExternalIDs.contains(folder.externalID ?? "") {
|
||||
for feed in folder.topLevelWebFeeds {
|
||||
account.addWebFeed(feed)
|
||||
clearFolderRelationship(for: feed, withFolderName: folder.name ?? "")
|
||||
clearFolderRelationship(for: feed, folderExternalID: folder.externalID)
|
||||
}
|
||||
account.removeFolder(folder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let folderNames: [String] = {
|
||||
let folderExternalIDs: [String] = {
|
||||
if let folders = account.folders {
|
||||
return folders.map { $0.name ?? "" }
|
||||
return folders.compactMap { $0.externalID }
|
||||
} else {
|
||||
return [String]()
|
||||
}
|
||||
}()
|
||||
|
||||
// Make any folders Reader has, but we don't
|
||||
tags.forEach { tag in
|
||||
if let tagFolderName = tag.folderName, !folderNames.contains(tagFolderName) {
|
||||
let folder = account.ensureFolder(with: tagFolderName)
|
||||
folderTags.forEach { tag in
|
||||
if !folderExternalIDs.contains(tag.tagID) {
|
||||
let folder = account.ensureFolder(with: tag.folderName ?? "None")
|
||||
folder?.externalID = tag.tagID
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func refreshFeeds(_ account: Account, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
caller.retrieveSubscriptions { result in
|
||||
switch result {
|
||||
case .success(let subscriptions):
|
||||
self.refreshProgress.completeTask()
|
||||
BatchUpdate.shared.perform {
|
||||
self.syncFeeds(account, subscriptions)
|
||||
self.syncTaggings(account, subscriptions)
|
||||
}
|
||||
completion(.success(()))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func syncFeeds(_ account: Account, _ subscriptions: [ReaderAPISubscription]?) {
|
||||
|
||||
guard let subscriptions = subscriptions else { return }
|
||||
|
@ -649,6 +671,7 @@ private extension ReaderAPIAccountDelegate {
|
|||
|
||||
for feed in account.topLevelWebFeeds {
|
||||
if !subFeedIds.contains(feed.webFeedID) {
|
||||
account.clearWebFeedMetadata(feed)
|
||||
account.removeWebFeed(feed)
|
||||
}
|
||||
}
|
||||
|
@ -670,43 +693,38 @@ private extension ReaderAPIAccountDelegate {
|
|||
|
||||
}
|
||||
|
||||
func syncTaggings(_ account: Account, _ subscriptions: [ReaderAPISubscription]?) {
|
||||
func syncFeedFolderRelationship(_ account: Account, _ subscriptions: [ReaderAPISubscription]?) {
|
||||
guard let subscriptions = subscriptions else { return }
|
||||
assert(Thread.isMainThread)
|
||||
os_log(.debug, log: log, "Syncing taggings with %ld subscriptions.", subscriptions.count)
|
||||
|
||||
// Set up some structures to make syncing easier
|
||||
let folderDict = nameToFolderDictionary(with: account.folders)
|
||||
let folderDict = externalIDToFolderDictionary(with: account.folders)
|
||||
let taggingsDict = subscriptions.reduce([String: [ReaderAPISubscription]]()) { (dict, subscription) in
|
||||
var taggedFeeds = dict
|
||||
|
||||
// For each category that this feed belongs to, add the feed to that name in the dict
|
||||
subscription.categories.forEach({ (category) in
|
||||
let categoryName = category.categoryLabel.replacingOccurrences(of: "user/-/label/", with: "")
|
||||
|
||||
if var taggedFeed = taggedFeeds[categoryName] {
|
||||
if var taggedFeed = taggedFeeds[category.categoryId] {
|
||||
taggedFeed.append(subscription)
|
||||
taggedFeeds[categoryName] = taggedFeed
|
||||
taggedFeeds[category.categoryId] = taggedFeed
|
||||
} else {
|
||||
taggedFeeds[categoryName] = [subscription]
|
||||
taggedFeeds[category.categoryId] = [subscription]
|
||||
}
|
||||
})
|
||||
|
||||
return taggedFeeds
|
||||
}
|
||||
|
||||
var taggedFeedIDs = Set<String>()
|
||||
|
||||
// Sync the folders
|
||||
for (folderName, groupedTaggings) in taggingsDict {
|
||||
guard let folder = folderDict[folderName] else { return }
|
||||
for (folderExternalID, groupedTaggings) in taggingsDict {
|
||||
guard let folder = folderDict[folderExternalID] else { return }
|
||||
let taggingFeedIDs = groupedTaggings.map { $0.feedID }
|
||||
|
||||
// Move any feeds not in the folder to the account
|
||||
for feed in folder.topLevelWebFeeds {
|
||||
if !taggingFeedIDs.contains(feed.webFeedID) {
|
||||
folder.removeWebFeed(feed)
|
||||
clearFolderRelationship(for: feed, withFolderName: folder.name ?? "")
|
||||
clearFolderRelationship(for: feed, folderExternalID: folder.externalID)
|
||||
account.addWebFeed(feed)
|
||||
}
|
||||
}
|
||||
|
@ -720,14 +738,15 @@ private extension ReaderAPIAccountDelegate {
|
|||
guard let feed = account.existingWebFeed(withWebFeedID: taggingFeedID) else {
|
||||
continue
|
||||
}
|
||||
saveFolderRelationship(for: feed, withFolderName: folderName, id: String(subscription.feedID))
|
||||
saveFolderRelationship(for: feed, folderExternalID: folderExternalID, feedExternalID: subscription.feedID)
|
||||
folder.addWebFeed(feed)
|
||||
taggedFeedIDs.insert(taggingFeedID)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let taggedFeedIDs = Set(subscriptions.filter({ !$0.categories.isEmpty }).map { String($0.feedID) })
|
||||
|
||||
// Remove all feeds from the account container that have a tag
|
||||
for feed in account.topLevelWebFeeds {
|
||||
if taggedFeedIDs.contains(feed.webFeedID) {
|
||||
|
@ -736,18 +755,19 @@ private extension ReaderAPIAccountDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
func nameToFolderDictionary(with folders: Set<Folder>?) -> [String: Folder] {
|
||||
func externalIDToFolderDictionary(with folders: Set<Folder>?) -> [String: Folder] {
|
||||
guard let folders = folders else {
|
||||
return [String: Folder]()
|
||||
}
|
||||
|
||||
var d = [String: Folder]()
|
||||
|
||||
for folder in folders {
|
||||
let name = folder.name ?? ""
|
||||
if d[name] == nil {
|
||||
d[name] = folder
|
||||
if let externalID = folder.externalID, d[externalID] == nil {
|
||||
d[externalID] = folder
|
||||
}
|
||||
}
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
|
@ -784,19 +804,19 @@ private extension ReaderAPIAccountDelegate {
|
|||
|
||||
}
|
||||
|
||||
func clearFolderRelationship(for feed: WebFeed, withFolderName folderName: String) {
|
||||
if var folderRelationship = feed.folderRelationship {
|
||||
folderRelationship[folderName] = nil
|
||||
feed.folderRelationship = folderRelationship
|
||||
}
|
||||
func clearFolderRelationship(for feed: WebFeed, folderExternalID: String?) {
|
||||
guard var folderRelationship = feed.folderRelationship, let folderExternalID = folderExternalID else { return }
|
||||
folderRelationship[folderExternalID] = nil
|
||||
feed.folderRelationship = folderRelationship
|
||||
}
|
||||
|
||||
func saveFolderRelationship(for feed: WebFeed, withFolderName folderName: String, id: String) {
|
||||
func saveFolderRelationship(for feed: WebFeed, folderExternalID: String?, feedExternalID: String) {
|
||||
guard let folderExternalID = folderExternalID else { return }
|
||||
if var folderRelationship = feed.folderRelationship {
|
||||
folderRelationship[folderName] = id
|
||||
folderRelationship[folderExternalID] = feedExternalID
|
||||
feed.folderRelationship = folderRelationship
|
||||
} else {
|
||||
feed.folderRelationship = [folderName: id]
|
||||
feed.folderRelationship = [folderExternalID: feedExternalID]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -889,15 +909,23 @@ private extension ReaderAPIAccountDelegate {
|
|||
account.fetchArticleIDsForStatusesWithoutArticlesNewerThanCutoffDate { articleIDsResult in
|
||||
|
||||
func process(_ fetchedArticleIDs: Set<String>) {
|
||||
guard !fetchedArticleIDs.isEmpty else {
|
||||
completion()
|
||||
return
|
||||
}
|
||||
|
||||
os_log(.debug, log: self.log, "Refreshing missing articles...")
|
||||
let group = DispatchGroup()
|
||||
|
||||
let articleIDs = Array(fetchedArticleIDs)
|
||||
let chunkedArticleIDs = articleIDs.chunked(into: 100)
|
||||
|
||||
self.refreshProgress.addToNumberOfTasksAndRemaining(chunkedArticleIDs.count - 1)
|
||||
|
||||
for chunk in chunkedArticleIDs {
|
||||
group.enter()
|
||||
self.caller.retrieveEntries(articleIDs: chunk) { result in
|
||||
self.refreshProgress.completeTask()
|
||||
|
||||
switch result {
|
||||
case .success(let entries):
|
||||
|
@ -1052,7 +1080,7 @@ private extension ReaderAPIAccountDelegate {
|
|||
switch result {
|
||||
case .success:
|
||||
DispatchQueue.main.async {
|
||||
self.clearFolderRelationship(for: feed, withFolderName: folder.name ?? "")
|
||||
self.clearFolderRelationship(for: feed, folderExternalID: folder.externalID)
|
||||
folder.removeWebFeed(feed)
|
||||
account.addFeedIfNotInAnyFolder(feed)
|
||||
completion(.success(()))
|
||||
|
@ -1072,35 +1100,5 @@ private extension ReaderAPIAccountDelegate {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
func deleteSubscription(for account: Account, with feed: WebFeed, from container: Container?, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
|
||||
// This error should never happen
|
||||
guard let subscriptionID = feed.externalID else {
|
||||
completion(.failure(ReaderAPIAccountDelegateError.invalidParameter))
|
||||
return
|
||||
}
|
||||
|
||||
caller.deleteSubscription(subscriptionID: subscriptionID) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
DispatchQueue.main.async {
|
||||
account.removeWebFeed(feed)
|
||||
if let folders = account.folders {
|
||||
for folder in folders {
|
||||
folder.removeWebFeed(feed)
|
||||
}
|
||||
}
|
||||
completion(.success(()))
|
||||
}
|
||||
case .failure(let error):
|
||||
DispatchQueue.main.async {
|
||||
let wrappedError = AccountError.wrappedError(error: error, account: account)
|
||||
completion(.failure(wrappedError))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,11 +12,6 @@ import RSCore
|
|||
import RSWeb
|
||||
import Articles
|
||||
|
||||
extension Notification.Name {
|
||||
static let appleColorPreferencesChangedNotification = Notification.Name("AppleColorPreferencesChangedNotification")
|
||||
static let appleInterfaceThemeChangedNotification = Notification.Name("AppleInterfaceThemeChangedNotification")
|
||||
}
|
||||
|
||||
protocol DetailWebViewControllerDelegate: class {
|
||||
func mouseDidEnter(_: DetailWebViewController, link: String)
|
||||
func mouseDidExit(_: DetailWebViewController, link: String)
|
||||
|
@ -125,9 +120,6 @@ final class DetailWebViewController: NSViewController, WKUIDelegate {
|
|||
NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil)
|
||||
|
||||
DistributedNotificationCenter.default().addObserver(self, selector: #selector(appleColorPreferencesChanged(_:)), name: .appleColorPreferencesChangedNotification, object: nil)
|
||||
DistributedNotificationCenter.default().addObserver(self, selector: #selector(appleInterfaceThemeChanged(_:)), name: .appleInterfaceThemeChangedNotification, object: nil)
|
||||
|
||||
webView.loadFileURL(ArticleRenderer.blank.url, allowingReadAccessTo: ArticleRenderer.blank.baseURL)
|
||||
}
|
||||
|
||||
|
@ -145,14 +137,6 @@ final class DetailWebViewController: NSViewController, WKUIDelegate {
|
|||
reloadArticleImage()
|
||||
}
|
||||
|
||||
@objc func appleColorPreferencesChanged(_ note: Notification) {
|
||||
reloadHTML()
|
||||
}
|
||||
|
||||
@objc func appleInterfaceThemeChanged(_ note: Notification) {
|
||||
reloadHTML()
|
||||
}
|
||||
|
||||
// MARK: Media Functions
|
||||
|
||||
func stopMediaPlayback() {
|
||||
|
|
|
@ -1,123 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="17506" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17506"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="ExtensionPointAddViewController" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="tableView" destination="lyM-Zu-Let" id="JDz-05-OOg"/>
|
||||
<outlet property="view" destination="c22-O7-iKe" id="Vfr-rK-EHC"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<customView id="c22-O7-iKe">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="272"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<scrollView autohidesScrollers="YES" horizontalLineScroll="42" horizontalPageScroll="10" verticalLineScroll="42" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="y2z-6c-TH0">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="272"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<clipView key="contentView" id="qCn-Bf-ICO">
|
||||
<rect key="frame" x="1" y="1" width="478" height="270"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnSelection="YES" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="40" rowSizeStyle="automatic" viewBased="YES" id="lyM-Zu-Let">
|
||||
<rect key="frame" x="0.0" y="0.0" width="478" height="270"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<size key="intercellSpacing" width="3" height="2"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<tableViewGridLines key="gridStyleMask" horizontal="YES"/>
|
||||
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
||||
<tableColumns>
|
||||
<tableColumn width="466" minWidth="40" maxWidth="1000" id="SlU-lH-CzT">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
</tableHeaderCell>
|
||||
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="Nhn-I6-76l">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView identifier="Cell" id="EGi-CQ-lPc" customClass="AccountsAddTableCellView" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="11" y="1" width="475" height="40"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<stackView distribution="fill" orientation="horizontal" alignment="centerY" spacing="17" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="iCD-Yx-4V5">
|
||||
<rect key="frame" x="20" y="8" width="133" height="24"/>
|
||||
<subviews>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="KmN-Zk-TBU">
|
||||
<rect key="frame" x="0.0" y="0.0" width="24" height="24"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="24" id="dbz-aC-h0q"/>
|
||||
<constraint firstAttribute="width" constant="24" id="jN0-Et-ysS"/>
|
||||
</constraints>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="oGL-yl-27S"/>
|
||||
</imageView>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="uyu-5W-IaW">
|
||||
<rect key="frame" x="39" y="4" width="96" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="iOW-VJ-bkx">
|
||||
<font key="font" textStyle="body" name=".SFNS-Regular"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<visibilityPriorities>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
</visibilityPriorities>
|
||||
<customSpacing>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
<button id="y48-E2-CL3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="475" height="40"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="bevel" bezelStyle="rounded" alignment="center" imageScaling="proportionallyDown" inset="2" id="yf7-Ye-Pcd">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" textStyle="body" name=".SFNS-Regular"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="pressed:" target="EGi-CQ-lPc" id="2a9-Bp-K3K"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="iCD-Yx-4V5" firstAttribute="centerY" secondItem="EGi-CQ-lPc" secondAttribute="centerY" id="IS1-7W-BWY"/>
|
||||
<constraint firstItem="iCD-Yx-4V5" firstAttribute="leading" secondItem="EGi-CQ-lPc" secondAttribute="leading" constant="20" id="IsY-WH-f93"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="accountImageView" destination="KmN-Zk-TBU" id="ksF-ga-V5P"/>
|
||||
<outlet property="accountNameLabel" destination="uyu-5W-IaW" id="reb-QA-Xpx"/>
|
||||
<outlet property="imageView" destination="KmN-Zk-TBU" id="Tfy-Eb-Isb"/>
|
||||
<outlet property="titleLabel" destination="uyu-5W-IaW" id="QAe-Gk-Eeo"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
</prototypeCellViews>
|
||||
</tableColumn>
|
||||
</tableColumns>
|
||||
</tableView>
|
||||
</subviews>
|
||||
</clipView>
|
||||
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="qOf-Dj-ubR">
|
||||
<rect key="frame" x="1" y="255" width="478" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="XFQ-Xy-wny">
|
||||
<rect key="frame" x="224" y="17" width="15" height="102"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
<point key="canvasLocation" x="139" y="154"/>
|
||||
</customView>
|
||||
</objects>
|
||||
</document>
|
|
@ -1,29 +0,0 @@
|
|||
//
|
||||
// AccountsAddTableCellView.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Maurice Parker on 5/1/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import AppKit
|
||||
import Account
|
||||
|
||||
protocol AccountsAddTableCellViewDelegate: class {
|
||||
func addAccount(_ accountType: AccountType)
|
||||
}
|
||||
|
||||
class AccountsAddTableCellView: NSTableCellView {
|
||||
|
||||
weak var delegate: AccountsAddTableCellViewDelegate?
|
||||
var accountType: AccountType?
|
||||
|
||||
@IBOutlet weak var accountImageView: NSImageView?
|
||||
@IBOutlet weak var accountNameLabel: NSTextField?
|
||||
|
||||
@IBAction func pressed(_ sender: Any) {
|
||||
guard let accountType = accountType else { return }
|
||||
delegate?.addAccount(accountType)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,260 +0,0 @@
|
|||
//
|
||||
// AccountsAddViewController.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Maurice Parker on 5/1/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import AppKit
|
||||
import Account
|
||||
import RSCore
|
||||
|
||||
class AccountsAddViewController: NSViewController {
|
||||
|
||||
@IBOutlet weak var tableView: NSTableView!
|
||||
|
||||
private var accountsAddWindowController: NSWindowController?
|
||||
|
||||
#if DEBUG
|
||||
private var addableAccountTypes: [AccountType] = [.onMyMac, .cloudKit, .bazQux, .feedbin, .feedly, .feedWrangler, .inoreader, .newsBlur, .theOldReader, .freshRSS]
|
||||
#else
|
||||
private var addableAccountTypes: [AccountType] = [.onMyMac, .cloudKit, .bazQux, .feedbin, .feedly, .feedWrangler, .inoreader, .newsBlur, .theOldReader, .freshRSS]
|
||||
#endif
|
||||
|
||||
init() {
|
||||
super.init(nibName: "AccountsAdd", bundle: nil)
|
||||
}
|
||||
|
||||
public required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
tableView.dataSource = self
|
||||
tableView.delegate = self
|
||||
restrictAccounts()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - NSTableViewDataSource
|
||||
|
||||
extension AccountsAddViewController: NSTableViewDataSource {
|
||||
|
||||
func numberOfRows(in tableView: NSTableView) -> Int {
|
||||
return addableAccountTypes.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - NSTableViewDelegate
|
||||
|
||||
extension AccountsAddViewController: NSTableViewDelegate {
|
||||
|
||||
private static let cellIdentifier = NSUserInterfaceItemIdentifier(rawValue: "AccountCell")
|
||||
|
||||
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
||||
|
||||
if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "Cell"), owner: nil) as? AccountsAddTableCellView {
|
||||
|
||||
cell.accountType = addableAccountTypes[row]
|
||||
cell.delegate = self
|
||||
|
||||
switch addableAccountTypes[row] {
|
||||
case .onMyMac:
|
||||
cell.accountNameLabel?.stringValue = Account.defaultLocalAccountName
|
||||
cell.accountImageView?.image = AppAssets.accountLocal
|
||||
case .cloudKit:
|
||||
cell.accountNameLabel?.stringValue = NSLocalizedString("iCloud", comment: "iCloud")
|
||||
cell.accountImageView?.image = AppAssets.accountCloudKit
|
||||
case .feedbin:
|
||||
cell.accountNameLabel?.stringValue = NSLocalizedString("Feedbin", comment: "Feedbin")
|
||||
cell.accountImageView?.image = AppAssets.accountFeedbin
|
||||
case .feedWrangler:
|
||||
cell.accountNameLabel?.stringValue = NSLocalizedString("Feed Wrangler", comment: "Feed Wrangler")
|
||||
cell.accountImageView?.image = AppAssets.accountFeedWrangler
|
||||
case .freshRSS:
|
||||
cell.accountNameLabel?.stringValue = NSLocalizedString("FreshRSS", comment: "FreshRSS")
|
||||
cell.accountImageView?.image = AppAssets.accountFreshRSS
|
||||
case .feedly:
|
||||
cell.accountNameLabel?.stringValue = NSLocalizedString("Feedly", comment: "Feedly")
|
||||
cell.accountImageView?.image = AppAssets.accountFeedly
|
||||
case .newsBlur:
|
||||
cell.accountNameLabel?.stringValue = NSLocalizedString("NewsBlur", comment: "NewsBlur")
|
||||
cell.accountImageView?.image = AppAssets.accountNewsBlur
|
||||
case .inoreader:
|
||||
cell.accountNameLabel?.stringValue = NSLocalizedString("Inoreader", comment: "Inoreader")
|
||||
cell.accountImageView?.image = AppAssets.accountInoreader
|
||||
case .bazQux:
|
||||
cell.accountNameLabel?.stringValue = NSLocalizedString("Bazqux", comment: "Bazqux")
|
||||
cell.accountImageView?.image = AppAssets.accountBazQux
|
||||
case .theOldReader:
|
||||
cell.accountNameLabel?.stringValue = NSLocalizedString("The Old Reader", comment: "The Old Reader")
|
||||
cell.accountImageView?.image = AppAssets.accountTheOldReader
|
||||
}
|
||||
return cell
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: AccountsAddTableCellViewDelegate
|
||||
|
||||
extension AccountsAddViewController: AccountsAddTableCellViewDelegate {
|
||||
|
||||
func addAccount(_ accountType: AccountType) {
|
||||
|
||||
switch accountType {
|
||||
case .onMyMac:
|
||||
let accountsAddLocalWindowController = AccountsAddLocalWindowController()
|
||||
accountsAddLocalWindowController.runSheetOnWindow(self.view.window!)
|
||||
accountsAddWindowController = accountsAddLocalWindowController
|
||||
|
||||
case .cloudKit:
|
||||
let accountsAddCloudKitWindowController = AccountsAddCloudKitWindowController()
|
||||
accountsAddCloudKitWindowController.runSheetOnWindow(self.view.window!) { response in
|
||||
if response == NSApplication.ModalResponse.OK {
|
||||
self.restrictAccounts()
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
}
|
||||
accountsAddWindowController = accountsAddCloudKitWindowController
|
||||
|
||||
case .feedbin:
|
||||
let accountsFeedbinWindowController = AccountsFeedbinWindowController()
|
||||
accountsFeedbinWindowController.runSheetOnWindow(self.view.window!)
|
||||
accountsAddWindowController = accountsFeedbinWindowController
|
||||
|
||||
case .feedWrangler:
|
||||
let accountsFeedWranglerWindowController = AccountsFeedWranglerWindowController()
|
||||
accountsFeedWranglerWindowController.runSheetOnWindow(self.view.window!)
|
||||
accountsAddWindowController = accountsFeedWranglerWindowController
|
||||
|
||||
case .freshRSS:
|
||||
let accountsReaderAPIWindowController = AccountsReaderAPIWindowController()
|
||||
accountsReaderAPIWindowController.accountType = .freshRSS
|
||||
accountsReaderAPIWindowController.runSheetOnWindow(self.view.window!)
|
||||
accountsAddWindowController = accountsReaderAPIWindowController
|
||||
|
||||
case .feedly:
|
||||
let addAccount = OAuthAccountAuthorizationOperation(accountType: .feedly)
|
||||
addAccount.delegate = self
|
||||
addAccount.presentationAnchor = self.view.window!
|
||||
runAwaitingFeedlyLoginAlertModal(forLifetimeOf: addAccount)
|
||||
MainThreadOperationQueue.shared.add(addAccount)
|
||||
|
||||
case .newsBlur:
|
||||
let accountsNewsBlurWindowController = AccountsNewsBlurWindowController()
|
||||
accountsNewsBlurWindowController.runSheetOnWindow(self.view.window!)
|
||||
accountsAddWindowController = accountsNewsBlurWindowController
|
||||
|
||||
case .inoreader:
|
||||
let accountsReaderAPIWindowController = AccountsReaderAPIWindowController()
|
||||
accountsReaderAPIWindowController.accountType = .inoreader
|
||||
accountsReaderAPIWindowController.runSheetOnWindow(self.view.window!)
|
||||
accountsAddWindowController = accountsReaderAPIWindowController
|
||||
|
||||
case .bazQux:
|
||||
let accountsReaderAPIWindowController = AccountsReaderAPIWindowController()
|
||||
accountsReaderAPIWindowController.accountType = .bazQux
|
||||
accountsReaderAPIWindowController.runSheetOnWindow(self.view.window!)
|
||||
accountsAddWindowController = accountsReaderAPIWindowController
|
||||
|
||||
case .theOldReader:
|
||||
let accountsReaderAPIWindowController = AccountsReaderAPIWindowController()
|
||||
accountsReaderAPIWindowController.accountType = .theOldReader
|
||||
accountsReaderAPIWindowController.runSheetOnWindow(self.view.window!)
|
||||
accountsAddWindowController = accountsReaderAPIWindowController
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private func runAwaitingFeedlyLoginAlertModal(forLifetimeOf operation: OAuthAccountAuthorizationOperation) {
|
||||
let alert = NSAlert()
|
||||
alert.alertStyle = .informational
|
||||
alert.messageText = NSLocalizedString("Waiting for access to Feedly",
|
||||
comment: "Alert title when adding a Feedly account and waiting for authorization from the user.")
|
||||
|
||||
alert.informativeText = NSLocalizedString("Your default web browser will open the Feedly login for you to authorize access.",
|
||||
comment: "Alert informative text when adding a Feedly account and waiting for authorization from the user.")
|
||||
|
||||
alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "Cancel"))
|
||||
|
||||
let attachedWindow = self.view.window!
|
||||
|
||||
alert.beginSheetModal(for: attachedWindow) { response in
|
||||
if response == .alertFirstButtonReturn {
|
||||
operation.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
operation.completionBlock = { _ in
|
||||
guard alert.window.isVisible else {
|
||||
return
|
||||
}
|
||||
attachedWindow.endSheet(alert.window)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: OAuthAccountAuthorizationOperationDelegate
|
||||
|
||||
extension AccountsAddViewController: OAuthAccountAuthorizationOperationDelegate {
|
||||
|
||||
func oauthAccountAuthorizationOperation(_ operation: OAuthAccountAuthorizationOperation, didCreate account: Account) {
|
||||
// `OAuthAccountAuthorizationOperation` is using `ASWebAuthenticationSession` which bounces the user
|
||||
// to their browser on macOS for authorizing NetNewsWire to access the user's Feedly account.
|
||||
// When this authorization is granted, the browser remains the foreground app which is unfortunate
|
||||
// because the user probably wants to see the result of authorizing NetNewsWire to act on their behalf.
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
|
||||
account.refreshAll { [weak self] result in
|
||||
switch result {
|
||||
case .success:
|
||||
break
|
||||
case .failure(let error):
|
||||
self?.presentError(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func oauthAccountAuthorizationOperation(_ operation: OAuthAccountAuthorizationOperation, didFailWith error: Error) {
|
||||
// `OAuthAccountAuthorizationOperation` is using `ASWebAuthenticationSession` which bounces the user
|
||||
// to their browser on macOS for authorizing NetNewsWire to access the user's Feedly account.
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
|
||||
view.window?.presentError(error)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private extension AccountsAddViewController {
|
||||
|
||||
func restrictAccounts() {
|
||||
func removeAccountType(_ accountType: AccountType) {
|
||||
if let index = addableAccountTypes.firstIndex(of: accountType) {
|
||||
addableAccountTypes.remove(at: index)
|
||||
}
|
||||
}
|
||||
|
||||
if AppDefaults.shared.isDeveloperBuild {
|
||||
removeAccountType(.cloudKit)
|
||||
removeAccountType(.feedly)
|
||||
removeAccountType(.feedWrangler)
|
||||
removeAccountType(.inoreader)
|
||||
return
|
||||
}
|
||||
|
||||
if AccountManager.shared.accounts.firstIndex(where: { $0.type == .cloudKit }) != nil {
|
||||
removeAccountType(.cloudKit)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -58,7 +58,7 @@ class AccountsFeedbinWindowController: NSWindowController {
|
|||
return
|
||||
}
|
||||
|
||||
guard !AccountManager.shared.duplicateServiceAccount(type: .feedbin, username: usernameTextField.stringValue) else {
|
||||
guard account != nil || !AccountManager.shared.duplicateServiceAccount(type: .feedbin, username: usernameTextField.stringValue) else {
|
||||
self.errorMessageLabel.stringValue = NSLocalizedString("There is already a Feedbin account with that username created.", comment: "Duplicate Error")
|
||||
return
|
||||
}
|
||||
|
|
|
@ -11,14 +11,19 @@ import Account
|
|||
import SwiftUI
|
||||
import RSCore
|
||||
|
||||
// MARK: - AccountsPreferencesAddAccountDelegate
|
||||
protocol AccountsPreferencesAddAccountDelegate {
|
||||
func presentSheetForAccount(_ accountType: AccountType)
|
||||
}
|
||||
|
||||
// MARK: - AccountsPreferencesViewController
|
||||
final class AccountsPreferencesViewController: NSViewController {
|
||||
|
||||
@IBOutlet weak var tableView: NSTableView!
|
||||
@IBOutlet weak var detailView: NSView!
|
||||
@IBOutlet weak var deleteButton: NSButton!
|
||||
var addAccountDelegate: AccountsPreferencesAddAccountDelegate?
|
||||
|
||||
var addAccountWindowController: NSWindowController?
|
||||
|
||||
private var sortedAccounts = [Account]()
|
||||
|
||||
|
@ -39,6 +44,11 @@ final class AccountsPreferencesViewController: NSViewController {
|
|||
var rTable = tableView.frame
|
||||
rTable.size.width = tableView.superview!.frame.size.width
|
||||
tableView.frame = rTable
|
||||
|
||||
// Set initial row selection
|
||||
if sortedAccounts.count > 0 {
|
||||
tableView.selectRow(0)
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func addAccount(_ sender: Any) {
|
||||
|
@ -119,7 +129,7 @@ extension AccountsPreferencesViewController: NSTableViewDelegate {
|
|||
let selectedRow = tableView.selectedRow
|
||||
if tableView.selectedRow == -1 {
|
||||
deleteButton.isEnabled = false
|
||||
showController(AccountsAddViewController())
|
||||
hideController()
|
||||
return
|
||||
} else {
|
||||
deleteButton.isEnabled = true
|
||||
|
@ -137,18 +147,13 @@ extension AccountsPreferencesViewController: NSTableViewDelegate {
|
|||
|
||||
}
|
||||
|
||||
// MARK: - AccountsPreferencesAddAccountDelegate
|
||||
protocol AccountsPreferencesAddAccountDelegate {
|
||||
func presentSheetForAccount(_ accountType: AccountType)
|
||||
}
|
||||
|
||||
extension AccountsPreferencesViewController: AccountsPreferencesAddAccountDelegate {
|
||||
func presentSheetForAccount(_ accountType: AccountType) {
|
||||
switch accountType {
|
||||
case .onMyMac:
|
||||
let accountsAddLocalWindowController = AccountsAddLocalWindowController()
|
||||
accountsAddLocalWindowController.runSheetOnWindow(self.view.window!)
|
||||
|
||||
addAccountWindowController = accountsAddLocalWindowController
|
||||
case .cloudKit:
|
||||
let accountsAddCloudKitWindowController = AccountsAddCloudKitWindowController()
|
||||
accountsAddCloudKitWindowController.runSheetOnWindow(self.view.window!) { response in
|
||||
|
@ -156,17 +161,20 @@ extension AccountsPreferencesViewController: AccountsPreferencesAddAccountDelega
|
|||
self.tableView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
addAccountWindowController = accountsAddCloudKitWindowController
|
||||
case .feedbin:
|
||||
let accountsFeedbinWindowController = AccountsFeedbinWindowController()
|
||||
accountsFeedbinWindowController.runSheetOnWindow(self.view.window!)
|
||||
addAccountWindowController = accountsFeedbinWindowController
|
||||
case .feedWrangler:
|
||||
let accountsFeedWranglerWindowController = AccountsFeedWranglerWindowController()
|
||||
accountsFeedWranglerWindowController.runSheetOnWindow(self.view.window!)
|
||||
case .freshRSS:
|
||||
addAccountWindowController = accountsFeedWranglerWindowController
|
||||
case .freshRSS, .inoreader, .bazQux, .theOldReader:
|
||||
let accountsReaderAPIWindowController = AccountsReaderAPIWindowController()
|
||||
accountsReaderAPIWindowController.accountType = .freshRSS
|
||||
accountsReaderAPIWindowController.accountType = accountType
|
||||
accountsReaderAPIWindowController.runSheetOnWindow(self.view.window!)
|
||||
addAccountWindowController = accountsReaderAPIWindowController
|
||||
case .feedly:
|
||||
let addAccount = OAuthAccountAuthorizationOperation(accountType: .feedly)
|
||||
addAccount.delegate = self
|
||||
|
@ -176,18 +184,7 @@ extension AccountsPreferencesViewController: AccountsPreferencesAddAccountDelega
|
|||
case .newsBlur:
|
||||
let accountsNewsBlurWindowController = AccountsNewsBlurWindowController()
|
||||
accountsNewsBlurWindowController.runSheetOnWindow(self.view.window!)
|
||||
case .inoreader:
|
||||
let accountsReaderAPIWindowController = AccountsReaderAPIWindowController()
|
||||
accountsReaderAPIWindowController.accountType = .inoreader
|
||||
accountsReaderAPIWindowController.runSheetOnWindow(self.view.window!)
|
||||
case .bazQux:
|
||||
let accountsReaderAPIWindowController = AccountsReaderAPIWindowController()
|
||||
accountsReaderAPIWindowController.accountType = .bazQux
|
||||
accountsReaderAPIWindowController.runSheetOnWindow(self.view.window!)
|
||||
case .theOldReader:
|
||||
let accountsReaderAPIWindowController = AccountsReaderAPIWindowController()
|
||||
accountsReaderAPIWindowController.accountType = .theOldReader
|
||||
accountsReaderAPIWindowController.runSheetOnWindow(self.view.window!)
|
||||
addAccountWindowController = accountsNewsBlurWindowController
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,11 +225,7 @@ private extension AccountsPreferencesViewController {
|
|||
}
|
||||
|
||||
func showController(_ controller: NSViewController) {
|
||||
|
||||
if let controller = children.first {
|
||||
children.removeAll()
|
||||
controller.view.removeFromSuperview()
|
||||
}
|
||||
hideController()
|
||||
|
||||
addChild(controller)
|
||||
controller.view.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
@ -241,6 +234,13 @@ private extension AccountsPreferencesViewController {
|
|||
|
||||
}
|
||||
|
||||
func hideController() {
|
||||
if let controller = children.first {
|
||||
children.removeAll()
|
||||
controller.view.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension AccountsPreferencesViewController: OAuthAccountAuthorizationOperationDelegate {
|
||||
|
|
|
@ -31,7 +31,7 @@ private enum AddAccountSections: Int, CaseIterable {
|
|||
var sectionFooter: String {
|
||||
switch self {
|
||||
case .local:
|
||||
return NSLocalizedString("This account does not sync subscriptions across devices.", comment: "Local Account")
|
||||
return NSLocalizedString("Local accounts do not sync subscriptions across devices.", comment: "Local Account")
|
||||
case .icloud:
|
||||
return NSLocalizedString("Use your iCloud account to sync your subscriptions across your iOS and macOS devices.", comment: "iCloud Account")
|
||||
case .web:
|
||||
|
@ -117,7 +117,9 @@ struct AddAccountsView: View {
|
|||
.frame(width: 80)
|
||||
})
|
||||
}
|
||||
}.padding(.vertical, 8)
|
||||
}
|
||||
.padding(.top, 12)
|
||||
.padding(.bottom, 4)
|
||||
}
|
||||
.pickerStyle(RadioGroupPickerStyle())
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
|
@ -130,28 +132,29 @@ struct AddAccountsView: View {
|
|||
Text("Local")
|
||||
.font(.headline)
|
||||
.padding(.horizontal)
|
||||
|
||||
Picker(selection: $selectedAccount, label: Text(""), content: {
|
||||
ForEach(AddAccountSections.local.sectionContent, id: \.self, content: { account in
|
||||
HStack(alignment: .top) {
|
||||
HStack(alignment: .center) {
|
||||
account.image()
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 25, height: 25, alignment: .center)
|
||||
.offset(CGSize(width: 0, height: -2.5))
|
||||
.padding(.leading, 4)
|
||||
|
||||
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(account.localizedAccountName())
|
||||
Text(AddAccountSections.local.sectionFooter).foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
}
|
||||
Text(account.localizedAccountName())
|
||||
}
|
||||
.tag(account)
|
||||
})
|
||||
})
|
||||
.pickerStyle(RadioGroupPickerStyle())
|
||||
.offset(x: 7.5, y: 0)
|
||||
|
||||
Text(AddAccountSections.local.sectionFooter).foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
.padding(.horizontal)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -161,27 +164,28 @@ struct AddAccountsView: View {
|
|||
Text("iCloud")
|
||||
.font(.headline)
|
||||
.padding(.horizontal)
|
||||
.padding(.top, 8)
|
||||
|
||||
Picker(selection: $selectedAccount, label: Text(""), content: {
|
||||
ForEach(AddAccountSections.icloud.sectionContent, id: \.self, content: { account in
|
||||
HStack(alignment: .top) {
|
||||
HStack(alignment: .center) {
|
||||
account.image()
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 25, height: 25, alignment: .center)
|
||||
.offset(CGSize(width: 0, height: -5))
|
||||
.padding(.leading, 4)
|
||||
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(account.localizedAccountName())
|
||||
Text(AddAccountSections.icloud.sectionFooter).foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
}
|
||||
Text(account.localizedAccountName())
|
||||
}
|
||||
.tag(account)
|
||||
})
|
||||
})
|
||||
.offset(x: 7.5, y: 0)
|
||||
.disabled(isCloudInUse())
|
||||
|
||||
Text(AddAccountSections.icloud.sectionFooter).foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
.padding(.horizontal)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,6 +194,8 @@ struct AddAccountsView: View {
|
|||
Text("Web")
|
||||
.font(.headline)
|
||||
.padding(.horizontal)
|
||||
.padding(.top, 8)
|
||||
|
||||
Picker(selection: $selectedAccount, label: Text(""), content: {
|
||||
ForEach(AddAccountSections.web.sectionContent.filter({ isRestricted($0) != true }), id: \.self, content: { account in
|
||||
|
||||
|
@ -200,15 +206,17 @@ struct AddAccountsView: View {
|
|||
.frame(width: 25, height: 25, alignment: .center)
|
||||
.padding(.leading, 4)
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
Text(account.localizedAccountName())
|
||||
}
|
||||
Text(account.localizedAccountName())
|
||||
}
|
||||
.tag(account)
|
||||
|
||||
})
|
||||
})
|
||||
.offset(x: 7.5, y: 0)
|
||||
|
||||
Text(AddAccountSections.web.sectionFooter).foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
.padding(.horizontal)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -218,29 +226,25 @@ struct AddAccountsView: View {
|
|||
.font(.headline)
|
||||
.padding(.horizontal)
|
||||
.padding(.top, 8)
|
||||
|
||||
Picker(selection: $selectedAccount, label: Text(""), content: {
|
||||
ForEach(AddAccountSections.selfhosted.sectionContent, id: \.self, content: { account in
|
||||
HStack(alignment: .top) {
|
||||
HStack(alignment: .center) {
|
||||
account.image()
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 25, height: 25, alignment: .center)
|
||||
.offset(CGSize(width: 0, height: -4))
|
||||
.padding(.leading, 4)
|
||||
|
||||
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(account.localizedAccountName())
|
||||
Text("Web and self-hosted accounts sync across all signed-in devices.")
|
||||
.font(.caption)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
|
||||
Text(account.localizedAccountName())
|
||||
}.tag(account)
|
||||
})
|
||||
})
|
||||
.offset(x: 7.5, y: 0)
|
||||
|
||||
|
||||
Text(AddAccountSections.selfhosted.sectionFooter).foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
.padding(.horizontal)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
//
|
||||
// EnableExtensionPointView.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Maurice Parker on 10/30/20.
|
||||
// Copyright © 2020 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import AppKit
|
||||
import SwiftUI
|
||||
import RSCore
|
||||
|
||||
struct EnableExtensionPointView: View {
|
||||
|
||||
weak var parent: NSHostingController<EnableExtensionPointView>? // required because presentationMode.dismiss() doesn't work
|
||||
weak var enabler: ExtensionPointPreferencesEnabler?
|
||||
@State private var extensionPointTypeName = String(describing: Self.feedProviderExtensionPointTypes.first!)
|
||||
|
||||
init(enabler: ExtensionPointPreferencesEnabler?) {
|
||||
self.enabler = enabler
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text("Choose an extension point to add...")
|
||||
.font(.headline)
|
||||
.padding()
|
||||
|
||||
feedProviderExtensionPoints
|
||||
sendToCommandExtensionPoints
|
||||
|
||||
HStack(spacing: 12) {
|
||||
Spacer()
|
||||
if #available(OSX 11.0, *) {
|
||||
Button(action: {
|
||||
parent?.dismiss(nil)
|
||||
}, label: {
|
||||
Text("Cancel")
|
||||
.frame(width: 80)
|
||||
})
|
||||
.help("Cancel")
|
||||
.keyboardShortcut(.cancelAction)
|
||||
|
||||
} else {
|
||||
Button(action: {
|
||||
parent?.dismiss(nil)
|
||||
}, label: {
|
||||
Text("Cancel")
|
||||
.frame(width: 80)
|
||||
})
|
||||
.accessibility(label: Text("Add Account"))
|
||||
}
|
||||
if #available(OSX 11.0, *) {
|
||||
Button(action: {
|
||||
enabler?.enable(typeFromName(extensionPointTypeName))
|
||||
parent?.dismiss(nil)
|
||||
}, label: {
|
||||
Text("Continue")
|
||||
.frame(width: 80)
|
||||
})
|
||||
.help("Add Account")
|
||||
.keyboardShortcut(.defaultAction)
|
||||
|
||||
} else {
|
||||
Button(action: {
|
||||
enabler?.enable(typeFromName(extensionPointTypeName))
|
||||
parent?.dismiss(nil)
|
||||
}, label: {
|
||||
Text("Continue")
|
||||
.frame(width: 80)
|
||||
})
|
||||
}
|
||||
}
|
||||
.padding(.top, 12)
|
||||
.padding(.bottom, 4)
|
||||
}
|
||||
.pickerStyle(RadioGroupPickerStyle())
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.frame(width: 420)
|
||||
.padding()
|
||||
}
|
||||
|
||||
var feedProviderExtensionPoints: some View {
|
||||
VStack(alignment: .leading) {
|
||||
let extensionPointTypeNames = Self.feedProviderExtensionPointTypes.map { String(describing: $0) }
|
||||
if extensionPointTypeNames.count > 0 {
|
||||
Text("Feed Provider")
|
||||
.font(.headline)
|
||||
.padding(.horizontal)
|
||||
|
||||
Picker(selection: $extensionPointTypeName, label: Text(""), content: {
|
||||
ForEach(extensionPointTypeNames, id: \.self, content: { extensionPointTypeName in
|
||||
let extensionPointType = typeFromName(extensionPointTypeName)
|
||||
HStack(alignment: .center) {
|
||||
Image(nsImage: extensionPointType.image)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 25, height: 25, alignment: .center)
|
||||
.padding(.leading, 4)
|
||||
|
||||
|
||||
Text(extensionPointType.title)
|
||||
}
|
||||
.tag(extensionPointTypeNames)
|
||||
})
|
||||
})
|
||||
.pickerStyle(RadioGroupPickerStyle())
|
||||
.offset(x: 7.5, y: 0)
|
||||
|
||||
Text("An extension point that makes websites appear to provide RSS feeds for their content.")
|
||||
.foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
.padding(.horizontal)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var sendToCommandExtensionPoints: some View {
|
||||
VStack(alignment: .leading) {
|
||||
let extensionPointTypeNames = Self.sendToCommandExtensionPointTypes.map { String(describing: $0) }
|
||||
if extensionPointTypeNames.count > 0 {
|
||||
Text("Third-Party Integration")
|
||||
.font(.headline)
|
||||
.padding(.horizontal)
|
||||
.padding(.top, 8)
|
||||
|
||||
Picker(selection: $extensionPointTypeName, label: Text(""), content: {
|
||||
ForEach(extensionPointTypeNames, id: \.self, content: { extensionPointTypeName in
|
||||
let extensionPointType = typeFromName(extensionPointTypeName)
|
||||
HStack(alignment: .center) {
|
||||
Image(nsImage: extensionPointType.image)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 25, height: 25, alignment: .center)
|
||||
.padding(.leading, 4)
|
||||
|
||||
|
||||
Text(extensionPointType.title)
|
||||
}
|
||||
.tag(extensionPointTypeNames)
|
||||
})
|
||||
})
|
||||
.pickerStyle(RadioGroupPickerStyle())
|
||||
.offset(x: 7.5, y: 0)
|
||||
|
||||
Text("An extension point that enables a share menu item that passes article data to a third-party application.")
|
||||
.foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
.padding(.horizontal)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static var sendToCommandExtensionPointTypes: [ExtensionPoint.Type] {
|
||||
return ExtensionPointManager.shared.availableExtensionPointTypes.filter({ $0 is SendToCommand.Type })
|
||||
}
|
||||
|
||||
static var feedProviderExtensionPointTypes: [ExtensionPoint.Type] {
|
||||
return ExtensionPointManager.shared.availableExtensionPointTypes.filter({ !($0 is SendToCommand.Type) })
|
||||
}
|
||||
|
||||
func typeFromName(_ name: String) -> ExtensionPoint.Type {
|
||||
for type in ExtensionPointManager.shared.possibleExtensionPointTypes {
|
||||
if name == String(describing: type) {
|
||||
return type
|
||||
}
|
||||
}
|
||||
fatalError()
|
||||
}
|
||||
}
|
|
@ -1,121 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="17505" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17505"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="ExtensionPointAddViewController" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="tableView" destination="lyM-Zu-Let" id="JDz-05-OOg"/>
|
||||
<outlet property="view" destination="c22-O7-iKe" id="Vfr-rK-EHC"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<customView id="c22-O7-iKe">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="272"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<scrollView autohidesScrollers="YES" horizontalLineScroll="42" horizontalPageScroll="10" verticalLineScroll="42" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="y2z-6c-TH0">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="272"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<clipView key="contentView" id="qCn-Bf-ICO">
|
||||
<rect key="frame" x="1" y="1" width="478" height="270"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnSelection="YES" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="40" rowSizeStyle="automatic" viewBased="YES" id="lyM-Zu-Let">
|
||||
<rect key="frame" x="0.0" y="0.0" width="478" height="270"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<size key="intercellSpacing" width="3" height="2"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<tableViewGridLines key="gridStyleMask" horizontal="YES"/>
|
||||
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
||||
<tableColumns>
|
||||
<tableColumn width="466" minWidth="40" maxWidth="1000" id="SlU-lH-CzT">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
</tableHeaderCell>
|
||||
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="Nhn-I6-76l">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView identifier="Cell" id="EGi-CQ-lPc" customClass="ExtensionPointAddTableCellView" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="1" y="1" width="475" height="40"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<stackView distribution="fill" orientation="horizontal" alignment="centerY" spacing="17" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="iCD-Yx-4V5">
|
||||
<rect key="frame" x="20" y="8" width="174" height="24"/>
|
||||
<subviews>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="KmN-Zk-TBU">
|
||||
<rect key="frame" x="0.0" y="0.0" width="24" height="24"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="24" id="dbz-aC-h0q"/>
|
||||
<constraint firstAttribute="width" constant="24" id="jN0-Et-ysS"/>
|
||||
</constraints>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="oGL-yl-27S"/>
|
||||
</imageView>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="uyu-5W-IaW">
|
||||
<rect key="frame" x="39" y="0.0" width="137" height="24"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="iOW-VJ-bkx">
|
||||
<font key="font" metaFont="system" size="20"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<visibilityPriorities>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
</visibilityPriorities>
|
||||
<customSpacing>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
<button id="y48-E2-CL3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="475" height="40"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="bevel" bezelStyle="rounded" alignment="center" imageScaling="proportionallyDown" inset="2" id="yf7-Ye-Pcd">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="pressed:" target="EGi-CQ-lPc" id="hgF-B0-Dyh"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="iCD-Yx-4V5" firstAttribute="centerY" secondItem="EGi-CQ-lPc" secondAttribute="centerY" id="IS1-7W-BWY"/>
|
||||
<constraint firstItem="iCD-Yx-4V5" firstAttribute="leading" secondItem="EGi-CQ-lPc" secondAttribute="leading" constant="20" id="IsY-WH-f93"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="imageView" destination="KmN-Zk-TBU" id="Tfy-Eb-Isb"/>
|
||||
<outlet property="titleLabel" destination="uyu-5W-IaW" id="QAe-Gk-Eeo"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
</prototypeCellViews>
|
||||
</tableColumn>
|
||||
</tableColumns>
|
||||
</tableView>
|
||||
</subviews>
|
||||
</clipView>
|
||||
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="qOf-Dj-ubR">
|
||||
<rect key="frame" x="1" y="255" width="478" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="XFQ-Xy-wny">
|
||||
<rect key="frame" x="224" y="17" width="15" height="102"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
<point key="canvasLocation" x="139" y="154"/>
|
||||
</customView>
|
||||
</objects>
|
||||
</document>
|
|
@ -1,28 +0,0 @@
|
|||
//
|
||||
// ExtensionPointAddTableCellView.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Maurice Parker on 4/6/20.
|
||||
// Copyright © 2020 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import AppKit
|
||||
|
||||
protocol ExtensionPointTableCellViewDelegate: class {
|
||||
func addExtensionPoint(_ extensionPointType: ExtensionPoint.Type)
|
||||
}
|
||||
|
||||
class ExtensionPointAddTableCellView: NSTableCellView {
|
||||
|
||||
weak var delegate: ExtensionPointTableCellViewDelegate?
|
||||
var extensionPointType: ExtensionPoint.Type?
|
||||
|
||||
@IBOutlet weak var templateImageView: NSImageView?
|
||||
@IBOutlet weak var titleLabel: NSTextField?
|
||||
|
||||
@IBAction func pressed(_ sender: Any) {
|
||||
guard let extensionPointType = extensionPointType else { return }
|
||||
delegate?.addExtensionPoint(extensionPointType)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
//
|
||||
// ExtensionPointAddViewController.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Maurice Parker on 4/6/20.
|
||||
// Copyright © 2020 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import AppKit
|
||||
|
||||
class ExtensionPointAddViewController: NSViewController {
|
||||
|
||||
@IBOutlet weak var tableView: NSTableView!
|
||||
|
||||
private var availableExtensionPointTypes = [ExtensionPoint.Type]()
|
||||
private var extensionPointAddWindowController: NSWindowController?
|
||||
|
||||
init() {
|
||||
super.init(nibName: "ExtensionPointAdd", bundle: nil)
|
||||
}
|
||||
|
||||
public required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
tableView.dataSource = self
|
||||
tableView.delegate = self
|
||||
availableExtensionPointTypes = ExtensionPointManager.shared.availableExtensionPointTypes.sorted(by: { $0.title < $1.title })
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - NSTableViewDataSource
|
||||
|
||||
extension ExtensionPointAddViewController: NSTableViewDataSource {
|
||||
|
||||
func numberOfRows(in tableView: NSTableView) -> Int {
|
||||
return availableExtensionPointTypes.count
|
||||
}
|
||||
|
||||
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - NSTableViewDelegate
|
||||
|
||||
extension ExtensionPointAddViewController: NSTableViewDelegate {
|
||||
|
||||
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
||||
if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "Cell"), owner: nil) as? ExtensionPointAddTableCellView {
|
||||
let extensionPointType = availableExtensionPointTypes[row]
|
||||
cell.extensionPointType = extensionPointType
|
||||
cell.delegate = self
|
||||
cell.titleLabel?.stringValue = extensionPointType.title
|
||||
cell.imageView?.image = extensionPointType.templateImage
|
||||
return cell
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ExtensionPointAddViewController: ExtensionPointTableCellViewDelegate {
|
||||
|
||||
func addExtensionPoint(_ extensionPointType: ExtensionPoint.Type) {
|
||||
let windowController = ExtensionPointEnableWindowController()
|
||||
windowController.extensionPointType = extensionPointType
|
||||
windowController.runSheetOnWindow(self.view.window!)
|
||||
extensionPointAddWindowController = windowController
|
||||
}
|
||||
|
||||
}
|
|
@ -29,7 +29,7 @@ class ExtensionPointDetailViewController: NSViewController {
|
|||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
guard let extensionPoint = extensionPoint else { return }
|
||||
imageView.image = extensionPoint.templateImage
|
||||
imageView.image = extensionPoint.image
|
||||
titleLabel.stringValue = extensionPoint.title
|
||||
descriptionLabel.attributedStringValue = extensionPoint.description
|
||||
}
|
||||
|
|
|
@ -1,116 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="16096" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="16096"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="ExtensionPointEnableWindowController" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="descriptionLabel" destination="thC-ep-vXS" id="o9I-vp-z54"/>
|
||||
<outlet property="enableButton" destination="sGb-z5-IdF" id="yNw-Nn-4Kq"/>
|
||||
<outlet property="imageView" destination="LSA-B8-aGZ" id="AN5-t1-d52"/>
|
||||
<outlet property="titleLabel" destination="iAC-tU-rvZ" id="vMx-2H-b44"/>
|
||||
<outlet property="window" destination="HNe-Jr-kev" id="C8n-l1-WhI"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="HNe-Jr-kev">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="196" y="240" width="407" height="156"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
|
||||
<view key="contentView" wantsLayer="YES" id="qAd-AQ-5ue">
|
||||
<rect key="frame" x="0.0" y="0.0" width="407" height="156"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<stackView distribution="fill" orientation="horizontal" alignment="bottom" spacing="19" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="nLd-4a-dQg">
|
||||
<rect key="frame" x="109" y="89" width="189" height="51"/>
|
||||
<subviews>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="LSA-B8-aGZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="36" height="36"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="36" id="SuU-du-YHk"/>
|
||||
<constraint firstAttribute="height" constant="36" id="qxc-dc-d8U"/>
|
||||
</constraints>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="NSAdvanced" id="5qe-pZ-t40"/>
|
||||
</imageView>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="iAC-tU-rvZ">
|
||||
<rect key="frame" x="53" y="13" width="138" height="38"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="Extension" id="kuv-Xu-aIk">
|
||||
<font key="font" metaFont="system" size="32"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<visibilityPriorities>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
</visibilityPriorities>
|
||||
<customSpacing>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="sGb-z5-IdF">
|
||||
<rect key="frame" x="312" y="13" width="81" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Enable" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Oh8-q3-Aup">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<string key="keyEquivalent" base64-UTF8="YES">
|
||||
DQ
|
||||
</string>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="enable:" target="-2" id="BN5-u0-DNe"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="aKy-4s-WDM">
|
||||
<rect key="frame" x="231" y="13" width="82" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="2nM-LA-6fh">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<string key="keyEquivalent" base64-UTF8="YES">
|
||||
Gw
|
||||
</string>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="cancel:" target="-2" id="WK9-uJ-mIw"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="thC-ep-vXS">
|
||||
<rect key="frame" x="52" y="57" width="304" height="16"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="300" id="igx-s6-xe9"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" selectable="YES" allowsUndo="NO" alignment="left" allowsEditingTextAttributes="YES" id="aUU-dO-RNt">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="iAC-tU-rvZ" firstAttribute="top" secondItem="qAd-AQ-5ue" secondAttribute="top" constant="16" id="Cxn-GQ-jzh"/>
|
||||
<constraint firstAttribute="bottom" secondItem="sGb-z5-IdF" secondAttribute="bottom" constant="20" id="Moe-ce-JeY"/>
|
||||
<constraint firstAttribute="trailing" secondItem="sGb-z5-IdF" secondAttribute="trailing" constant="20" id="OdS-3p-qyB"/>
|
||||
<constraint firstItem="sGb-z5-IdF" firstAttribute="leading" secondItem="aKy-4s-WDM" secondAttribute="trailing" constant="11" id="QPh-zm-9uL"/>
|
||||
<constraint firstItem="thC-ep-vXS" firstAttribute="centerX" secondItem="qAd-AQ-5ue" secondAttribute="centerX" id="fC4-fE-SyO"/>
|
||||
<constraint firstItem="aKy-4s-WDM" firstAttribute="centerY" secondItem="sGb-z5-IdF" secondAttribute="centerY" id="naD-Tq-iwx"/>
|
||||
<constraint firstItem="thC-ep-vXS" firstAttribute="top" secondItem="nLd-4a-dQg" secondAttribute="bottom" constant="16" id="qRM-G0-del"/>
|
||||
<constraint firstItem="aKy-4s-WDM" firstAttribute="top" secondItem="thC-ep-vXS" secondAttribute="bottom" constant="16" id="vrt-3v-j4f"/>
|
||||
<constraint firstItem="nLd-4a-dQg" firstAttribute="centerX" secondItem="qAd-AQ-5ue" secondAttribute="centerX" id="xXl-e5-lnN"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="-2" id="fo9-G5-zJh"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="103.5" y="89.5"/>
|
||||
</window>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="NSAdvanced" width="32" height="32"/>
|
||||
</resources>
|
||||
</document>
|
|
@ -33,7 +33,7 @@ class ExtensionPointEnableWindowController: NSWindowController {
|
|||
super.windowDidLoad()
|
||||
guard let extensionPointType = extensionPointType else { return }
|
||||
|
||||
imageView.image = extensionPointType.templateImage
|
||||
imageView.image = extensionPointType.image
|
||||
titleLabel.stringValue = extensionPointType.title
|
||||
descriptionLabel.attributedStringValue = extensionPointType.description
|
||||
}
|
||||
|
|
|
@ -7,6 +7,14 @@
|
|||
//
|
||||
|
||||
import AppKit
|
||||
import SwiftUI
|
||||
import AuthenticationServices
|
||||
import OAuthSwift
|
||||
import Secrets
|
||||
|
||||
protocol ExtensionPointPreferencesEnabler: class {
|
||||
func enable(_ extensionPointType: ExtensionPoint.Type)
|
||||
}
|
||||
|
||||
final class ExtensionPointPreferencesViewController: NSViewController {
|
||||
|
||||
|
@ -15,6 +23,8 @@ final class ExtensionPointPreferencesViewController: NSViewController {
|
|||
@IBOutlet weak var deleteButton: NSButton!
|
||||
|
||||
private var activeExtensionPoints = [ExtensionPoint]()
|
||||
private var callbackURL: URL? = nil
|
||||
private var oauth: OAuthSwift?
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
@ -30,11 +40,17 @@ final class ExtensionPointPreferencesViewController: NSViewController {
|
|||
tableView.frame = rTable
|
||||
|
||||
showDefaultView()
|
||||
|
||||
// Set initial row selection
|
||||
if activeExtensionPoints.count > 0 {
|
||||
tableView.selectRow(0)
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func enableExtensionPoints(_ sender: Any) {
|
||||
tableView.selectRowIndexes([], byExtendingSelection: false)
|
||||
showController(ExtensionPointAddViewController())
|
||||
let controller = NSHostingController(rootView: EnableExtensionPointView(enabler: self))
|
||||
controller.rootView.parent = controller
|
||||
presentAsSheet(controller)
|
||||
}
|
||||
|
||||
@IBAction func disableExtensionPoint(_ sender: Any) {
|
||||
|
@ -44,8 +60,7 @@ final class ExtensionPointPreferencesViewController: NSViewController {
|
|||
|
||||
let extensionPoint = activeExtensionPoints[tableView.selectedRow]
|
||||
ExtensionPointManager.shared.deactivateExtensionPoint(extensionPoint.extensionPointID)
|
||||
|
||||
showController(ExtensionPointAddViewController())
|
||||
hideController()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,7 +87,7 @@ extension ExtensionPointPreferencesViewController: NSTableViewDelegate {
|
|||
if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "Cell"), owner: nil) as? NSTableCellView {
|
||||
let extensionPoint = activeExtensionPoints[row]
|
||||
cell.textField?.stringValue = extensionPoint.title
|
||||
cell.imageView?.image = extensionPoint.templateImage
|
||||
cell.imageView?.image = extensionPoint.image
|
||||
return cell
|
||||
}
|
||||
return nil
|
||||
|
@ -83,6 +98,7 @@ extension ExtensionPointPreferencesViewController: NSTableViewDelegate {
|
|||
let selectedRow = tableView.selectedRow
|
||||
if tableView.selectedRow == -1 {
|
||||
deleteButton.isEnabled = false
|
||||
hideController()
|
||||
return
|
||||
} else {
|
||||
deleteButton.isEnabled = true
|
||||
|
@ -96,6 +112,62 @@ extension ExtensionPointPreferencesViewController: NSTableViewDelegate {
|
|||
|
||||
}
|
||||
|
||||
// MARK: ExtensionPointPreferencesViewController
|
||||
|
||||
extension ExtensionPointPreferencesViewController: ExtensionPointPreferencesEnabler {
|
||||
|
||||
func enable(_ extensionPointType: ExtensionPoint.Type) {
|
||||
if let oauth1 = extensionPointType as? OAuth1SwiftProvider.Type {
|
||||
enableOauth1(oauth1, extensionPointType: extensionPointType)
|
||||
} else if let oauth2 = extensionPointType as? OAuth2SwiftProvider.Type {
|
||||
enableOauth2(oauth2, extensionPointType: extensionPointType)
|
||||
} else {
|
||||
ExtensionPointManager.shared.activateExtensionPoint(extensionPointType) { result in
|
||||
if case .failure(let error) = result {
|
||||
self.presentError(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension ExtensionPointPreferencesViewController: OAuthSwiftURLHandlerType {
|
||||
|
||||
public func handle(_ url: URL) {
|
||||
let session = ASWebAuthenticationSession(url: url, callbackURLScheme: callbackURL!.scheme, completionHandler: { (url, error) in
|
||||
if let callbackedURL = url {
|
||||
OAuth1Swift.handle(url: callbackedURL)
|
||||
}
|
||||
|
||||
guard let error = error else { return }
|
||||
|
||||
self.oauth?.cancel()
|
||||
self.oauth = nil
|
||||
|
||||
if case ASWebAuthenticationSessionError.canceledLogin = error {
|
||||
print("Login cancelled.")
|
||||
} else {
|
||||
NSApplication.shared.presentError(error)
|
||||
}
|
||||
})
|
||||
|
||||
session.presentationContextProvider = self
|
||||
if !session.start() {
|
||||
print("Session failed to start!!!")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
extension ExtensionPointPreferencesViewController: ASWebAuthenticationPresentationContextProviding {
|
||||
|
||||
public func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
|
||||
return view.window!
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private extension ExtensionPointPreferencesViewController {
|
||||
|
@ -107,20 +179,77 @@ private extension ExtensionPointPreferencesViewController {
|
|||
func showDefaultView() {
|
||||
activeExtensionPoints = Array(ExtensionPointManager.shared.activeExtensionPoints.values).sorted(by: { $0.title < $1.title })
|
||||
tableView.reloadData()
|
||||
showController(ExtensionPointAddViewController())
|
||||
}
|
||||
|
||||
func showController(_ controller: NSViewController) {
|
||||
|
||||
if let controller = children.first {
|
||||
children.removeAll()
|
||||
controller.view.removeFromSuperview()
|
||||
}
|
||||
hideController()
|
||||
|
||||
addChild(controller)
|
||||
controller.view.translatesAutoresizingMaskIntoConstraints = false
|
||||
detailView.addSubview(controller.view)
|
||||
detailView.addFullSizeConstraints(forSubview: controller.view)
|
||||
}
|
||||
|
||||
func hideController() {
|
||||
if let controller = children.first {
|
||||
children.removeAll()
|
||||
controller.view.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
func enableOauth1(_ provider: OAuth1SwiftProvider.Type, extensionPointType: ExtensionPoint.Type) {
|
||||
callbackURL = provider.callbackURL
|
||||
|
||||
let oauth1 = provider.oauth1Swift
|
||||
self.oauth = oauth1
|
||||
oauth1.authorizeURLHandler = self
|
||||
|
||||
oauth1.authorize(withCallbackURL: callbackURL!) { [weak self] result in
|
||||
guard let self = self else { return }
|
||||
|
||||
switch result {
|
||||
case .success(let tokenSuccess):
|
||||
ExtensionPointManager.shared.activateExtensionPoint(extensionPointType, tokenSuccess: tokenSuccess) { result in
|
||||
if case .failure(let error) = result {
|
||||
self.presentError(error)
|
||||
}
|
||||
}
|
||||
case .failure(let oauthSwiftError):
|
||||
self.presentError(oauthSwiftError)
|
||||
}
|
||||
|
||||
self.oauth?.cancel()
|
||||
self.oauth = nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func enableOauth2(_ provider: OAuth2SwiftProvider.Type, extensionPointType: ExtensionPoint.Type) {
|
||||
callbackURL = provider.callbackURL
|
||||
|
||||
let oauth2 = provider.oauth2Swift
|
||||
self.oauth = oauth2
|
||||
oauth2.authorizeURLHandler = self
|
||||
|
||||
let oauth2Vars = provider.oauth2Vars
|
||||
|
||||
oauth2.authorize(withCallbackURL: callbackURL!, scope: oauth2Vars.scope, state: oauth2Vars.state, parameters: oauth2Vars.params) { [weak self] result in
|
||||
guard let self = self else { return }
|
||||
|
||||
switch result {
|
||||
case .success(let tokenSuccess):
|
||||
ExtensionPointManager.shared.activateExtensionPoint(extensionPointType, tokenSuccess: tokenSuccess) { result in
|
||||
if case .failure(let error) = result {
|
||||
self.presentError(error)
|
||||
}
|
||||
}
|
||||
case .failure(let oauthSwiftError):
|
||||
self.presentError(oauthSwiftError)
|
||||
}
|
||||
|
||||
self.oauth?.cancel()
|
||||
self.oauth = nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "freshrss-any.pdf",
|
||||
"filename" : "FreshRSS.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
|
|
Binary file not shown.
|
@ -1,8 +1,18 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "reddit_logo.pdf",
|
||||
"idiom" : "universal"
|
||||
"filename" : "reddit24x24.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "reddit48x48.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
|
BIN
Mac/Resources/Assets.xcassets/extensionPointReddit.imageset/reddit24x24.png
vendored
Normal file
BIN
Mac/Resources/Assets.xcassets/extensionPointReddit.imageset/reddit24x24.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
BIN
Mac/Resources/Assets.xcassets/extensionPointReddit.imageset/reddit48x48.png
vendored
Normal file
BIN
Mac/Resources/Assets.xcassets/extensionPointReddit.imageset/reddit48x48.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.3 KiB |
Binary file not shown.
|
@ -1,8 +1,18 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "twitter.pdf",
|
||||
"idiom" : "universal"
|
||||
"filename" : "twitter24x24.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "twitter48x48.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
|
Binary file not shown.
BIN
Mac/Resources/Assets.xcassets/extensionPointTwitter.imageset/twitter24x24.png
vendored
Normal file
BIN
Mac/Resources/Assets.xcassets/extensionPointTwitter.imageset/twitter24x24.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
BIN
Mac/Resources/Assets.xcassets/extensionPointTwitter.imageset/twitter48x48.png
vendored
Normal file
BIN
Mac/Resources/Assets.xcassets/extensionPointTwitter.imageset/twitter48x48.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.9 KiB |
|
@ -11,11 +11,6 @@ import Combine
|
|||
import RSCore
|
||||
import Articles
|
||||
|
||||
extension Notification.Name {
|
||||
static let appleColorPreferencesChangedNotification = Notification.Name("AppleColorPreferencesChangedNotification")
|
||||
static let appleInterfaceThemeChangedNotification = Notification.Name("AppleInterfaceThemeChangedNotification")
|
||||
}
|
||||
|
||||
protocol WebViewControllerDelegate: class {
|
||||
func webViewController(_: WebViewController, articleExtractorButtonStateDidUpdate: ArticleExtractorButtonState)
|
||||
}
|
||||
|
@ -67,8 +62,6 @@ class WebViewController: NSViewController {
|
|||
NotificationCenter.default.addObserver(self, selector: #selector(webFeedIconDidBecomeAvailable(_:)), name: .WebFeedIconDidBecomeAvailable, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(avatarDidBecomeAvailable(_:)), name: .AvatarDidBecomeAvailable, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil)
|
||||
DistributedNotificationCenter.default().addObserver(self, selector: #selector(appleColorPreferencesChanged(_:)), name: .appleColorPreferencesChangedNotification, object: nil)
|
||||
DistributedNotificationCenter.default().addObserver(self, selector: #selector(appleInterfaceThemeChanged(_:)), name: .appleInterfaceThemeChangedNotification, object: nil)
|
||||
|
||||
statusBarView = WebStatusBarView()
|
||||
statusBarView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
@ -100,14 +93,6 @@ class WebViewController: NSViewController {
|
|||
reloadArticleImage()
|
||||
}
|
||||
|
||||
@objc func appleColorPreferencesChanged(_ note: Notification) {
|
||||
loadWebView()
|
||||
}
|
||||
|
||||
@objc func appleInterfaceThemeChanged(_ note: Notification) {
|
||||
loadWebView()
|
||||
}
|
||||
|
||||
// MARK: API
|
||||
|
||||
func focus() {
|
||||
|
|
|
@ -105,12 +105,6 @@
|
|||
510C418424E5D1B4008226FD /* ExtensionFeedAddRequestFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B5C8BF23F3866C00032075 /* ExtensionFeedAddRequestFile.swift */; };
|
||||
510C418524E5D1B4008226FD /* ExtensionContainers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B5C87623F22B8200032075 /* ExtensionContainers.swift */; };
|
||||
510C418624E5D1B4008226FD /* ExtensionFeedAddRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B5C87A23F2317700032075 /* ExtensionFeedAddRequest.swift */; };
|
||||
510C43ED243C0973009F70C3 /* ExtensionPointAdd.xib in Resources */ = {isa = PBXBuildFile; fileRef = 510C43EC243C0973009F70C3 /* ExtensionPointAdd.xib */; };
|
||||
510C43EE243C0973009F70C3 /* ExtensionPointAdd.xib in Resources */ = {isa = PBXBuildFile; fileRef = 510C43EC243C0973009F70C3 /* ExtensionPointAdd.xib */; };
|
||||
510C43F0243C0A80009F70C3 /* ExtensionPointAddViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510C43EF243C0A80009F70C3 /* ExtensionPointAddViewController.swift */; };
|
||||
510C43F1243C0A80009F70C3 /* ExtensionPointAddViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510C43EF243C0A80009F70C3 /* ExtensionPointAddViewController.swift */; };
|
||||
510C43F3243C11FE009F70C3 /* ExtensionPointAddTableCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510C43F2243C11FE009F70C3 /* ExtensionPointAddTableCellView.swift */; };
|
||||
510C43F4243C11FE009F70C3 /* ExtensionPointAddTableCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510C43F2243C11FE009F70C3 /* ExtensionPointAddTableCellView.swift */; };
|
||||
510C43F7243D035C009F70C3 /* ExtensionPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510C43F6243D035C009F70C3 /* ExtensionPoint.swift */; };
|
||||
510C43F8243D035C009F70C3 /* ExtensionPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510C43F6243D035C009F70C3 /* ExtensionPoint.swift */; };
|
||||
51102165233A7D6C0007A5F7 /* ArticleExtractorButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51102164233A7D6C0007A5F7 /* ArticleExtractorButton.swift */; };
|
||||
|
@ -222,8 +216,6 @@
|
|||
515A5108243D0CCD0089E588 /* TwitterFeedProvider-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 515A5106243D0CCD0089E588 /* TwitterFeedProvider-Extensions.swift */; };
|
||||
515A5148243E64BA0089E588 /* ExtensionPointEnableWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 515A5147243E64BA0089E588 /* ExtensionPointEnableWindowController.swift */; };
|
||||
515A5149243E64BA0089E588 /* ExtensionPointEnableWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 515A5147243E64BA0089E588 /* ExtensionPointEnableWindowController.swift */; };
|
||||
515A5168243E66910089E588 /* ExtensionPointEnable.xib in Resources */ = {isa = PBXBuildFile; fileRef = 515A5167243E66910089E588 /* ExtensionPointEnable.xib */; };
|
||||
515A5169243E66910089E588 /* ExtensionPointEnable.xib in Resources */ = {isa = PBXBuildFile; fileRef = 515A5167243E66910089E588 /* ExtensionPointEnable.xib */; };
|
||||
515A516E243E7F950089E588 /* ExtensionPointDetail.xib in Resources */ = {isa = PBXBuildFile; fileRef = 515A516D243E7F950089E588 /* ExtensionPointDetail.xib */; };
|
||||
515A516F243E7F950089E588 /* ExtensionPointDetail.xib in Resources */ = {isa = PBXBuildFile; fileRef = 515A516D243E7F950089E588 /* ExtensionPointDetail.xib */; };
|
||||
515A5171243E802B0089E588 /* ExtensionPointDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 515A5170243E802B0089E588 /* ExtensionPointDetailViewController.swift */; };
|
||||
|
@ -300,6 +292,8 @@
|
|||
5183CCE5226F4DFA0010922C /* RefreshInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE4226F4DFA0010922C /* RefreshInterval.swift */; };
|
||||
5183CCE6226F4E110010922C /* RefreshInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE4226F4DFA0010922C /* RefreshInterval.swift */; };
|
||||
5183CCE8226F68D90010922C /* AccountRefreshTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE7226F68D90010922C /* AccountRefreshTimer.swift */; };
|
||||
5183CFAF254C78C8006B83A5 /* EnableExtensionPointView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CFAE254C78C8006B83A5 /* EnableExtensionPointView.swift */; };
|
||||
5183CFB0254C78C8006B83A5 /* EnableExtensionPointView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CFAE254C78C8006B83A5 /* EnableExtensionPointView.swift */; };
|
||||
518651B223555EB20078E021 /* NNW3Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 518651AB23555EB20078E021 /* NNW3Document.swift */; };
|
||||
518651DA235621840078E021 /* ImageTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 518651D9235621840078E021 /* ImageTransition.swift */; };
|
||||
51868BF1254386630011A17B /* SidebarDeleteItemsAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51868BF0254386630011A17B /* SidebarDeleteItemsAlert.swift */; };
|
||||
|
@ -662,9 +656,6 @@
|
|||
51EF0F7A22771B890050506E /* ColorHash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51EF0F78227716380050506E /* ColorHash.swift */; };
|
||||
51EF0F7E2277A57D0050506E /* MasterTimelineAccessibilityCellLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51EF0F7D2277A57D0050506E /* MasterTimelineAccessibilityCellLayout.swift */; };
|
||||
51EF0F802277A8330050506E /* MasterTimelineCellLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51EF0F7F2277A8330050506E /* MasterTimelineCellLayout.swift */; };
|
||||
51EF0F8E2279C9260050506E /* AccountsAdd.xib in Resources */ = {isa = PBXBuildFile; fileRef = 51EF0F8D2279C9260050506E /* AccountsAdd.xib */; };
|
||||
51EF0F902279C9500050506E /* AccountsAddViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51EF0F8F2279C9500050506E /* AccountsAddViewController.swift */; };
|
||||
51EF0F922279CA620050506E /* AccountsAddTableCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51EF0F912279CA620050506E /* AccountsAddTableCellView.swift */; };
|
||||
51EFDA1A24E6159C0085C3D6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 849C64671ED37A5D003D8FC0 /* Assets.xcassets */; };
|
||||
51EFDA1B24E6D16A0085C3D6 /* SafariExt.js in Resources */ = {isa = PBXBuildFile; fileRef = 515D4FCB2325815A00EE1167 /* SafariExt.js */; };
|
||||
51EFDA1D24E6E27E0085C3D6 /* icon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 51EFDA1C24E6E27E0085C3D6 /* icon.icns */; };
|
||||
|
@ -737,7 +728,6 @@
|
|||
65ED3FCF235DEF6C0081F399 /* TimelineContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8405DD9822153B6B008CE1BF /* TimelineContainerView.swift */; };
|
||||
65ED3FD0235DEF6C0081F399 /* Author+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A2678B20130ECF00A8D3C0 /* Author+Scriptability.swift */; };
|
||||
65ED3FD1235DEF6C0081F399 /* PseudoFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F2D5351FC22FCB00998D64 /* PseudoFeed.swift */; };
|
||||
65ED3FD2235DEF6C0081F399 /* AccountsAddViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51EF0F8F2279C9500050506E /* AccountsAddViewController.swift */; };
|
||||
65ED3FD3235DEF6C0081F399 /* NSScriptCommand+NetNewsWire.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57BE6DF204CD35F00D11AAC /* NSScriptCommand+NetNewsWire.swift */; };
|
||||
65ED3FD4235DEF6C0081F399 /* Article+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D553737C20186C1F006D8857 /* Article+Scriptability.swift */; };
|
||||
65ED3FD5235DEF6C0081F399 /* SmartFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845EE7C01FC2488C00854A1F /* SmartFeed.swift */; };
|
||||
|
@ -798,7 +788,6 @@
|
|||
65ED400D235DEF6C0081F399 /* SmartFeedDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84DEE56422C32CA4005FC42C /* SmartFeedDelegate.swift */; };
|
||||
65ED400E235DEF6C0081F399 /* ImageDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845213221FCA5B10003B6E93 /* ImageDownloader.swift */; };
|
||||
65ED400F235DEF6C0081F399 /* LegacyArticleExtractorButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51FA73B62332D5F70090D516 /* LegacyArticleExtractorButton.swift */; };
|
||||
65ED4010235DEF6C0081F399 /* AccountsAddTableCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51EF0F912279CA620050506E /* AccountsAddTableCellView.swift */; };
|
||||
65ED4011235DEF6C0081F399 /* AddFolderWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97421ED9EAA9007D329B /* AddFolderWindowController.swift */; };
|
||||
65ED4012235DEF6C0081F399 /* TimelineContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8405DDA422168C62008CE1BF /* TimelineContainerViewController.swift */; };
|
||||
65ED4013235DEF6C0081F399 /* MainWIndowKeyboardHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844B5B661FEA18E300C7C76A /* MainWIndowKeyboardHandler.swift */; };
|
||||
|
@ -853,7 +842,6 @@
|
|||
65ED4051235DEF6C0081F399 /* TimelineKeyboardShortcuts.plist in Resources */ = {isa = PBXBuildFile; fileRef = 845479871FEB77C000AD8B59 /* TimelineKeyboardShortcuts.plist */; };
|
||||
65ED4052235DEF6C0081F399 /* template.html in Resources */ = {isa = PBXBuildFile; fileRef = 848362FE2262A30E00DA1D35 /* template.html */; };
|
||||
65ED4054235DEF6C0081F399 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 848363062262A3DD00DA1D35 /* Main.storyboard */; };
|
||||
65ED4055235DEF6C0081F399 /* AccountsAdd.xib in Resources */ = {isa = PBXBuildFile; fileRef = 51EF0F8D2279C9260050506E /* AccountsAdd.xib */; };
|
||||
65ED4056235DEF6C0081F399 /* NetNewsWire.sdef in Resources */ = {isa = PBXBuildFile; fileRef = 84C9FC8A22629E8F00D921D6 /* NetNewsWire.sdef */; };
|
||||
65ED4057235DEF6C0081F399 /* AccountsDetail.xib in Resources */ = {isa = PBXBuildFile; fileRef = 84C9FC7422629E1200D921D6 /* AccountsDetail.xib */; };
|
||||
65ED4058235DEF6C0081F399 /* main.js in Resources */ = {isa = PBXBuildFile; fileRef = 517630032336215100E15FFF /* main.js */; };
|
||||
|
@ -1501,9 +1489,6 @@
|
|||
510C416524E5CDE3008226FD /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
510C416624E5CDE3008226FD /* ShareExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ShareExtension.entitlements; sourceTree = "<group>"; };
|
||||
510C418724E5D2E3008226FD /* NetNewsWire_shareextension_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = NetNewsWire_shareextension_target.xcconfig; sourceTree = "<group>"; };
|
||||
510C43EC243C0973009F70C3 /* ExtensionPointAdd.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ExtensionPointAdd.xib; sourceTree = "<group>"; };
|
||||
510C43EF243C0A80009F70C3 /* ExtensionPointAddViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionPointAddViewController.swift; sourceTree = "<group>"; };
|
||||
510C43F2243C11FE009F70C3 /* ExtensionPointAddTableCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionPointAddTableCellView.swift; sourceTree = "<group>"; };
|
||||
510C43F6243D035C009F70C3 /* ExtensionPoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionPoint.swift; sourceTree = "<group>"; };
|
||||
51102164233A7D6C0007A5F7 /* ArticleExtractorButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleExtractorButton.swift; sourceTree = "<group>"; };
|
||||
51107745243BEE2500D97C8C /* ExtensionPointPreferencesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionPointPreferencesViewController.swift; sourceTree = "<group>"; };
|
||||
|
@ -1566,7 +1551,6 @@
|
|||
515A50E5243D07A90089E588 /* ExtensionPointManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionPointManager.swift; sourceTree = "<group>"; };
|
||||
515A5106243D0CCD0089E588 /* TwitterFeedProvider-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TwitterFeedProvider-Extensions.swift"; sourceTree = "<group>"; };
|
||||
515A5147243E64BA0089E588 /* ExtensionPointEnableWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionPointEnableWindowController.swift; sourceTree = "<group>"; };
|
||||
515A5167243E66910089E588 /* ExtensionPointEnable.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ExtensionPointEnable.xib; sourceTree = "<group>"; };
|
||||
515A516D243E7F950089E588 /* ExtensionPointDetail.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ExtensionPointDetail.xib; sourceTree = "<group>"; };
|
||||
515A5170243E802B0089E588 /* ExtensionPointDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionPointDetailViewController.swift; sourceTree = "<group>"; };
|
||||
515A5176243E90200089E588 /* ExtensionPointIdentifer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionPointIdentifer.swift; sourceTree = "<group>"; };
|
||||
|
@ -1626,6 +1610,7 @@
|
|||
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>"; };
|
||||
5183CCE7226F68D90010922C /* AccountRefreshTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountRefreshTimer.swift; sourceTree = "<group>"; };
|
||||
5183CFAE254C78C8006B83A5 /* EnableExtensionPointView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnableExtensionPointView.swift; sourceTree = "<group>"; };
|
||||
518651AB23555EB20078E021 /* NNW3Document.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NNW3Document.swift; sourceTree = "<group>"; };
|
||||
518651D9235621840078E021 /* ImageTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageTransition.swift; sourceTree = "<group>"; };
|
||||
51868BF0254386630011A17B /* SidebarDeleteItemsAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarDeleteItemsAlert.swift; sourceTree = "<group>"; };
|
||||
|
@ -1774,9 +1759,6 @@
|
|||
51EF0F78227716380050506E /* ColorHash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorHash.swift; sourceTree = "<group>"; };
|
||||
51EF0F7D2277A57D0050506E /* MasterTimelineAccessibilityCellLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterTimelineAccessibilityCellLayout.swift; sourceTree = "<group>"; };
|
||||
51EF0F7F2277A8330050506E /* MasterTimelineCellLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterTimelineCellLayout.swift; sourceTree = "<group>"; };
|
||||
51EF0F8D2279C9260050506E /* AccountsAdd.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AccountsAdd.xib; sourceTree = "<group>"; };
|
||||
51EF0F8F2279C9500050506E /* AccountsAddViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsAddViewController.swift; sourceTree = "<group>"; };
|
||||
51EF0F912279CA620050506E /* AccountsAddTableCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsAddTableCellView.swift; sourceTree = "<group>"; };
|
||||
51EFDA1C24E6E27E0085C3D6 /* icon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = icon.icns; sourceTree = "<group>"; };
|
||||
51F805D32428499E0022C792 /* NetNewsWire-dev.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "NetNewsWire-dev.entitlements"; sourceTree = "<group>"; };
|
||||
51F805ED24284C1C0022C792 /* NetNewsWire-dev.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "NetNewsWire-dev.entitlements"; sourceTree = "<group>"; };
|
||||
|
@ -2321,14 +2303,11 @@
|
|||
51107744243BEDD300D97C8C /* ExtensionPoints */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
510C43EC243C0973009F70C3 /* ExtensionPointAdd.xib */,
|
||||
510C43F2243C11FE009F70C3 /* ExtensionPointAddTableCellView.swift */,
|
||||
510C43EF243C0A80009F70C3 /* ExtensionPointAddViewController.swift */,
|
||||
515A5167243E66910089E588 /* ExtensionPointEnable.xib */,
|
||||
515A5147243E64BA0089E588 /* ExtensionPointEnableWindowController.swift */,
|
||||
51107745243BEE2500D97C8C /* ExtensionPointPreferencesViewController.swift */,
|
||||
5183CFAE254C78C8006B83A5 /* EnableExtensionPointView.swift */,
|
||||
515A516D243E7F950089E588 /* ExtensionPointDetail.xib */,
|
||||
515A5170243E802B0089E588 /* ExtensionPointDetailViewController.swift */,
|
||||
515A5147243E64BA0089E588 /* ExtensionPointEnableWindowController.swift */,
|
||||
51107745243BEE2500D97C8C /* ExtensionPointPreferencesViewController.swift */,
|
||||
);
|
||||
path = ExtensionPoints;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3350,9 +3329,6 @@
|
|||
children = (
|
||||
178A9F9C2549449F00AB7E9D /* AddAccountsView.swift */,
|
||||
84C9FC7222629E1200D921D6 /* AccountsPreferencesViewController.swift */,
|
||||
51EF0F8D2279C9260050506E /* AccountsAdd.xib */,
|
||||
51EF0F8F2279C9500050506E /* AccountsAddViewController.swift */,
|
||||
51EF0F912279CA620050506E /* AccountsAddTableCellView.swift */,
|
||||
84C9FC7422629E1200D921D6 /* AccountsDetail.xib */,
|
||||
5144EA2E2279FAB600D19003 /* AccountsDetailViewController.swift */,
|
||||
5144EA50227B8E4500D19003 /* AccountsFeedbin.xib */,
|
||||
|
@ -4192,13 +4168,10 @@
|
|||
65ED4051235DEF6C0081F399 /* TimelineKeyboardShortcuts.plist in Resources */,
|
||||
65ED4052235DEF6C0081F399 /* template.html in Resources */,
|
||||
65ED4054235DEF6C0081F399 /* Main.storyboard in Resources */,
|
||||
510C43EE243C0973009F70C3 /* ExtensionPointAdd.xib in Resources */,
|
||||
65ED4055235DEF6C0081F399 /* AccountsAdd.xib in Resources */,
|
||||
65ED4056235DEF6C0081F399 /* NetNewsWire.sdef in Resources */,
|
||||
65ED4057235DEF6C0081F399 /* AccountsDetail.xib in Resources */,
|
||||
65ED4058235DEF6C0081F399 /* main.js in Resources */,
|
||||
65ED40A1235DEFF00081F399 /* container-migration.plist in Resources */,
|
||||
515A5169243E66910089E588 /* ExtensionPointEnable.xib in Resources */,
|
||||
65ED4059235DEF6C0081F399 /* AccountsAddLocal.xib in Resources */,
|
||||
65ED405A235DEF6C0081F399 /* main_mac.js in Resources */,
|
||||
65ED405B235DEF6C0081F399 /* KeyboardShortcuts.html in Resources */,
|
||||
|
@ -4290,7 +4263,6 @@
|
|||
845479881FEB77C000AD8B59 /* TimelineKeyboardShortcuts.plist in Resources */,
|
||||
848362FF2262A30E00DA1D35 /* template.html in Resources */,
|
||||
848363082262A3DD00DA1D35 /* Main.storyboard in Resources */,
|
||||
51EF0F8E2279C9260050506E /* AccountsAdd.xib in Resources */,
|
||||
84C9FC8F22629E8F00D921D6 /* NetNewsWire.sdef in Resources */,
|
||||
84C9FC7D22629E1200D921D6 /* AccountsDetail.xib in Resources */,
|
||||
517630042336215100E15FFF /* main.js in Resources */,
|
||||
|
@ -4298,7 +4270,6 @@
|
|||
5144EA362279FC3D00D19003 /* AccountsAddLocal.xib in Resources */,
|
||||
5142194B2353C1CF00E07E2C /* main_mac.js in Resources */,
|
||||
84C9FC8C22629E8F00D921D6 /* KeyboardShortcuts.html in Resources */,
|
||||
515A5168243E66910089E588 /* ExtensionPointEnable.xib in Resources */,
|
||||
B27EEBF9244D15F3000932E6 /* shared.css in Resources */,
|
||||
5144EA3B227A379E00D19003 /* ImportOPMLSheet.xib in Resources */,
|
||||
844B5B691FEA20DF00C7C76A /* SidebarKeyboardShortcuts.plist in Resources */,
|
||||
|
@ -4317,7 +4288,6 @@
|
|||
3B826DCB2385C84800FC1ADB /* AccountsFeedWrangler.xib in Resources */,
|
||||
55E15BCB229D65A900D6602A /* AccountsReaderAPI.xib in Resources */,
|
||||
49F40DF82335B71000552BF4 /* newsfoot.js in Resources */,
|
||||
510C43ED243C0973009F70C3 /* ExtensionPointAdd.xib in Resources */,
|
||||
51333D3B2468615D00EB5C91 /* AddRedditFeedSheet.xib in Resources */,
|
||||
BDCB516724282C8A00102A80 /* AccountsNewsBlur.xib in Resources */,
|
||||
514A89A2244FD63F0085E65D /* AddTwitterFeedSheet.xib in Resources */,
|
||||
|
@ -4843,13 +4813,13 @@
|
|||
65ED3FCF235DEF6C0081F399 /* TimelineContainerView.swift in Sources */,
|
||||
65ED3FD0235DEF6C0081F399 /* Author+Scriptability.swift in Sources */,
|
||||
65ED3FD1235DEF6C0081F399 /* PseudoFeed.swift in Sources */,
|
||||
65ED3FD2235DEF6C0081F399 /* AccountsAddViewController.swift in Sources */,
|
||||
65ED3FD3235DEF6C0081F399 /* NSScriptCommand+NetNewsWire.swift in Sources */,
|
||||
65ED3FD4235DEF6C0081F399 /* Article+Scriptability.swift in Sources */,
|
||||
515A5172243E802B0089E588 /* ExtensionPointDetailViewController.swift in Sources */,
|
||||
65ED3FD5235DEF6C0081F399 /* SmartFeed.swift in Sources */,
|
||||
51333D1724685D2E00EB5C91 /* AddRedditFeedWindowController.swift in Sources */,
|
||||
65ED3FD6235DEF6C0081F399 /* MarkStatusCommand.swift in Sources */,
|
||||
5183CFB0254C78C8006B83A5 /* EnableExtensionPointView.swift in Sources */,
|
||||
65ED3FD7235DEF6C0081F399 /* NSApplication+Scriptability.swift in Sources */,
|
||||
65ED3FD8235DEF6C0081F399 /* NSView-Extensions.swift in Sources */,
|
||||
51A052CF244FB9D7006C2024 /* AddFeedWIndowController.swift in Sources */,
|
||||
|
@ -4916,7 +4886,6 @@
|
|||
65ED400D235DEF6C0081F399 /* SmartFeedDelegate.swift in Sources */,
|
||||
65ED400E235DEF6C0081F399 /* ImageDownloader.swift in Sources */,
|
||||
65ED400F235DEF6C0081F399 /* LegacyArticleExtractorButton.swift in Sources */,
|
||||
65ED4010235DEF6C0081F399 /* AccountsAddTableCellView.swift in Sources */,
|
||||
65ED4011235DEF6C0081F399 /* AddFolderWindowController.swift in Sources */,
|
||||
65ED4012235DEF6C0081F399 /* TimelineContainerViewController.swift in Sources */,
|
||||
65ED4013235DEF6C0081F399 /* MainWIndowKeyboardHandler.swift in Sources */,
|
||||
|
@ -4934,7 +4903,6 @@
|
|||
65ED401C235DEF6C0081F399 /* FaviconGenerator.swift in Sources */,
|
||||
65ED401D235DEF6C0081F399 /* RefreshInterval.swift in Sources */,
|
||||
65ED401E235DEF6C0081F399 /* TimelineCellData.swift in Sources */,
|
||||
510C43F1243C0A80009F70C3 /* ExtensionPointAddViewController.swift in Sources */,
|
||||
65ED401F235DEF6C0081F399 /* BuiltinSmartFeedInspectorViewController.swift in Sources */,
|
||||
65ED4020235DEF6C0081F399 /* AppDelegate+Scriptability.swift in Sources */,
|
||||
65ED4021235DEF6C0081F399 /* NNW3Document.swift in Sources */,
|
||||
|
@ -4974,7 +4942,6 @@
|
|||
65ED403D235DEF6C0081F399 /* TimelineTableCellView.swift in Sources */,
|
||||
65ED403E235DEF6C0081F399 /* TimelineCellAppearance.swift in Sources */,
|
||||
51C4CFF124D37D1F00AF9874 /* Secrets.swift in Sources */,
|
||||
510C43F4243C11FE009F70C3 /* ExtensionPointAddTableCellView.swift in Sources */,
|
||||
65ED403F235DEF6C0081F399 /* ArticleRenderer.swift in Sources */,
|
||||
65ED4040235DEF6C0081F399 /* GeneralPrefencesViewController.swift in Sources */,
|
||||
179DB1DFBCF9177104B12E0F /* AccountsNewsBlurWindowController.swift in Sources */,
|
||||
|
@ -5204,7 +5171,6 @@
|
|||
8405DD9922153B6B008CE1BF /* TimelineContainerView.swift in Sources */,
|
||||
D5A2678C20130ECF00A8D3C0 /* Author+Scriptability.swift in Sources */,
|
||||
84F2D5371FC22FCC00998D64 /* PseudoFeed.swift in Sources */,
|
||||
51EF0F902279C9500050506E /* AccountsAddViewController.swift in Sources */,
|
||||
D57BE6E0204CD35F00D11AAC /* NSScriptCommand+NetNewsWire.swift in Sources */,
|
||||
D553738B20186C20006D8857 /* Article+Scriptability.swift in Sources */,
|
||||
845EE7C11FC2488C00854A1F /* SmartFeed.swift in Sources */,
|
||||
|
@ -5220,7 +5186,6 @@
|
|||
849A97671ED9EB96007D329B /* UnreadCountView.swift in Sources */,
|
||||
510C418024E5D1AE008226FD /* ExtensionFeedAddRequestFile.swift in Sources */,
|
||||
51FE10092346739D0056195D /* ActivityType.swift in Sources */,
|
||||
510C43F3243C11FE009F70C3 /* ExtensionPointAddTableCellView.swift in Sources */,
|
||||
840BEE4121D70E64009BBAFA /* CrashReportWindowController.swift in Sources */,
|
||||
8426118A1FCB67AA0086A189 /* WebFeedIconDownloader.swift in Sources */,
|
||||
84C9FC7B22629E1200D921D6 /* PreferencesControlsBackgroundView.swift in Sources */,
|
||||
|
@ -5245,6 +5210,7 @@
|
|||
51107746243BEE2500D97C8C /* ExtensionPointPreferencesViewController.swift in Sources */,
|
||||
519B8D332143397200FA689C /* SharingServiceDelegate.swift in Sources */,
|
||||
FF3ABF1523259DDB0074C542 /* ArticleSorter.swift in Sources */,
|
||||
5183CFAF254C78C8006B83A5 /* EnableExtensionPointView.swift in Sources */,
|
||||
84E8E0DB202EC49300562D8F /* TimelineViewController+ContextualMenus.swift in Sources */,
|
||||
849A97791ED9EC04007D329B /* ArticleStringFormatter.swift in Sources */,
|
||||
B24E9ADC245AB88400DA5718 /* NSAttributedString+NetNewsWire.swift in Sources */,
|
||||
|
@ -5281,7 +5247,6 @@
|
|||
84DEE56522C32CA4005FC42C /* SmartFeedDelegate.swift in Sources */,
|
||||
845213231FCA5B11003B6E93 /* ImageDownloader.swift in Sources */,
|
||||
51FA73B72332D5F70090D516 /* LegacyArticleExtractorButton.swift in Sources */,
|
||||
51EF0F922279CA620050506E /* AccountsAddTableCellView.swift in Sources */,
|
||||
849A97431ED9EAA9007D329B /* AddFolderWindowController.swift in Sources */,
|
||||
8405DDA522168C62008CE1BF /* TimelineContainerViewController.swift in Sources */,
|
||||
844B5B671FEA18E300C7C76A /* MainWIndowKeyboardHandler.swift in Sources */,
|
||||
|
@ -5305,7 +5270,6 @@
|
|||
518651B223555EB20078E021 /* NNW3Document.swift in Sources */,
|
||||
D5F4EDB5200744A700B9E363 /* ScriptingObject.swift in Sources */,
|
||||
D5F4EDB920074D7C00B9E363 /* Folder+Scriptability.swift in Sources */,
|
||||
510C43F0243C0A80009F70C3 /* ExtensionPointAddViewController.swift in Sources */,
|
||||
849A97781ED9EC04007D329B /* TimelineCellLayout.swift in Sources */,
|
||||
84E8E0EB202F693600562D8F /* DetailWebView.swift in Sources */,
|
||||
849A976C1ED9EBC8007D329B /* TimelineTableRowView.swift in Sources */,
|
||||
|
|
|
@ -52,8 +52,10 @@ function wrapTables() {
|
|||
// on an iphone when viewing an article.
|
||||
function inlineVideos() {
|
||||
document.querySelectorAll("video").forEach(element => {
|
||||
element.setAttribute("playsinline", true)
|
||||
element.setAttribute("controls", true)
|
||||
element.setAttribute("playsinline", true);
|
||||
if (!element.classList.contains("nnwAnimatedGIF")) {
|
||||
element.setAttribute("controls", true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -102,8 +104,13 @@ function stopMediaPlayback() {
|
|||
element.src = iframeSrc;
|
||||
});
|
||||
|
||||
// We pause all videos that have controls. Video without controls shouldn't
|
||||
// have sound and are actually converted gifs. Basically if the user can't
|
||||
// start the video again, don't stop it.
|
||||
document.querySelectorAll("video, audio").forEach(element => {
|
||||
element.pause();
|
||||
if (element.hasAttribute("controls")) {
|
||||
element.pause();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ protocol ExtensionPoint {
|
|||
static var isSinglton: Bool { get }
|
||||
static var isDeveloperBuildRestricted: Bool { get }
|
||||
static var title: String { get }
|
||||
static var templateImage: RSImage { get }
|
||||
static var image: RSImage { get }
|
||||
static var description: NSAttributedString { get }
|
||||
|
||||
var title: String { get }
|
||||
|
@ -28,8 +28,8 @@ protocol ExtensionPoint {
|
|||
|
||||
extension ExtensionPoint {
|
||||
|
||||
var templateImage: RSImage {
|
||||
return extensionPointID.extensionPointType.templateImage
|
||||
var image: RSImage {
|
||||
return extensionPointID.extensionPointType.image
|
||||
}
|
||||
|
||||
var description: NSAttributedString {
|
||||
|
|
|
@ -14,7 +14,7 @@ extension RedditFeedProvider: ExtensionPoint {
|
|||
static var isSinglton = false
|
||||
static var isDeveloperBuildRestricted = true
|
||||
static var title = NSLocalizedString("Reddit", comment: "Reddit")
|
||||
static var templateImage = AppAssets.extensionPointReddit
|
||||
static var image = AppAssets.extensionPointReddit
|
||||
static var description: NSAttributedString = {
|
||||
return RedditFeedProvider.makeAttrString("This extension enables you to subscribe to Reddit URL's as if they were RSS feeds. It only works with \(Account.defaultLocalAccountName) or iCloud accounts.")
|
||||
}()
|
||||
|
|
|
@ -15,7 +15,7 @@ final class SendToMarsEditCommand: ExtensionPoint, SendToCommand {
|
|||
static var isSinglton = true
|
||||
static var isDeveloperBuildRestricted = false
|
||||
static var title = NSLocalizedString("MarsEdit", comment: "MarsEdit")
|
||||
static var templateImage = AppAssets.extensionPointMarsEdit
|
||||
static var image = AppAssets.extensionPointMarsEdit
|
||||
static var description: NSAttributedString = {
|
||||
let attrString = SendToMarsEditCommand.makeAttrString("This extension enables share menu functionality to send selected article text to MarsEdit. You need the MarsEdit application for this to work.")
|
||||
let range = NSRange(location: 81, length: 8)
|
||||
|
|
|
@ -17,7 +17,7 @@ final class SendToMicroBlogCommand: ExtensionPoint, SendToCommand {
|
|||
static var isSinglton = true
|
||||
static var isDeveloperBuildRestricted = false
|
||||
static var title: String = NSLocalizedString("Micro.blog", comment: "Micro.blog")
|
||||
static var templateImage = AppAssets.extensionPointMicroblog
|
||||
static var image = AppAssets.extensionPointMicroblog
|
||||
static var description: NSAttributedString = {
|
||||
let attrString = SendToMicroBlogCommand.makeAttrString("This extension enables share menu functionality to send selected article text to Micro.blog. You need the Micro.blog application for this to work.")
|
||||
let range = NSRange(location: 81, length: 10)
|
||||
|
|
|
@ -14,7 +14,7 @@ extension TwitterFeedProvider: ExtensionPoint {
|
|||
static var isSinglton = false
|
||||
static var isDeveloperBuildRestricted = true
|
||||
static var title = NSLocalizedString("Twitter", comment: "Twitter")
|
||||
static var templateImage = AppAssets.extensionPointTwitter
|
||||
static var image = AppAssets.extensionPointTwitter
|
||||
static var description: NSAttributedString = {
|
||||
return TwitterFeedProvider.makeAttrString("This extension enables you to subscribe to Twitter URL's as if they were RSS feeds. It only works with \(Account.defaultLocalAccountName) or iCloud accounts.")
|
||||
}()
|
||||
|
|
|
@ -41,7 +41,7 @@ extension ExtensionPointInspectorViewController {
|
|||
|
||||
if section == 0 {
|
||||
let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "SectionHeader") as! ImageHeaderView
|
||||
headerView.imageView.image = extensionPoint.templateImage
|
||||
headerView.imageView.image = extensionPoint.image
|
||||
return headerView
|
||||
} else {
|
||||
return super.tableView(tableView, viewForHeaderInSection: section)
|
||||
|
|
|
@ -34,7 +34,7 @@ class AddExtensionPointViewController: UITableViewController, AddExtensionPointD
|
|||
|
||||
let extensionPointType = availableExtensionPointTypes[indexPath.row]
|
||||
cell.comboNameLabel?.text = extensionPointType.title
|
||||
cell.comboImage?.image = extensionPointType.templateImage
|
||||
cell.comboImage?.image = extensionPointType.image
|
||||
|
||||
return cell
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ class EnableExtensionPointViewController: UITableViewController {
|
|||
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||
if section == 0 {
|
||||
let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "SectionHeader") as! ImageHeaderView
|
||||
headerView.imageView.image = extensionPointType?.templateImage
|
||||
headerView.imageView.image = extensionPointType?.image
|
||||
return headerView
|
||||
} else {
|
||||
return super.tableView(tableView, viewForHeaderInSection: section)
|
||||
|
|
|
@ -153,7 +153,7 @@ class SettingsViewController: UITableViewController {
|
|||
let acctCell = tableView.dequeueReusableCell(withIdentifier: "SettingsComboTableViewCell", for: indexPath) as! SettingsComboTableViewCell
|
||||
acctCell.applyThemeProperties()
|
||||
let extensionPoint = extensionPoints[indexPath.row]
|
||||
acctCell.comboImage?.image = extensionPoint.templateImage
|
||||
acctCell.comboImage?.image = extensionPoint.image
|
||||
acctCell.comboNameLabel?.text = extensionPoint.title
|
||||
cell = acctCell
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// High Level Settings common to both the Mac application and any extensions we bundle with it
|
||||
MARKETING_VERSION = 5.1.1
|
||||
CURRENT_PROJECT_VERSION = 3012
|
||||
MARKETING_VERSION = 5.1.2
|
||||
CURRENT_PROJECT_VERSION = 3016
|
||||
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon
|
||||
|
|
Loading…
Reference in New Issue