Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
205b544acd | ||
|
|
56f6ec1356 | ||
|
|
ab71c51380 | ||
|
|
7a805ccdc4 | ||
|
|
f4f4bc9271 | ||
|
|
64637243b5 | ||
|
|
5894b12c1d | ||
|
|
0700eebb13 | ||
|
|
4c4a133fe2 | ||
|
|
ccca329630 |
19
CHANGELOG.md
19
CHANGELOG.md
@@ -5,6 +5,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project does adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
|
||||
## [1.5.4] – 2025-12-02
|
||||
### Added
|
||||
- *UI:* Tooltip explanation for all appearance settings
|
||||
- *Status Bar Menu:* Hold down option key before opening the menu bar icon to show hidden articles (if option "Show only unread" is active)
|
||||
|
||||
|
||||
## [1.5.3] – 2025-10-29
|
||||
### Fixed
|
||||
- *Notifications:* Use user-provided feed title instead of server provided title
|
||||
|
||||
|
||||
## [1.5.2] – 2025-10-29
|
||||
### Added
|
||||
- *Notifications:* Reply with "Open in background", "Mark read & dismiss", or "Open but keep unread"
|
||||
|
||||
|
||||
## [1.5.1] – 2025-10-27
|
||||
### Fixed
|
||||
- *Status Bar Menu:* Simplified options for "Show only unread"
|
||||
@@ -220,6 +236,9 @@ and this project does adhere to [Semantic Versioning](https://semver.org/spec/v2
|
||||
Initial release
|
||||
|
||||
|
||||
[1.5.4]: https://github.com/relikd/baRSS/compare/v1.5.3...v1.5.4
|
||||
[1.5.3]: https://github.com/relikd/baRSS/compare/v1.5.2...v1.5.3
|
||||
[1.5.2]: https://github.com/relikd/baRSS/compare/v1.5.1...v1.5.2
|
||||
[1.5.1]: https://github.com/relikd/baRSS/compare/v1.5.0...v1.5.1
|
||||
[1.5.0]: https://github.com/relikd/baRSS/compare/v1.4.1...v1.5.0
|
||||
[1.4.1]: https://github.com/relikd/baRSS/compare/v1.4.0...v1.4.1
|
||||
|
||||
6
Config-debug.xcconfig
Normal file
6
Config-debug.xcconfig
Normal file
@@ -0,0 +1,6 @@
|
||||
// Configuration settings file format documentation can be found at:
|
||||
// https://help.apple.com/xcode/#/dev745c5c974
|
||||
|
||||
#include "Config.xcconfig"
|
||||
|
||||
PRODUCT_BUNDLE_IDENTIFIER = de.relikd.baRSS.beta
|
||||
12
Config.xcconfig
Normal file
12
Config.xcconfig
Normal file
@@ -0,0 +1,12 @@
|
||||
// Configuration settings file format documentation can be found at:
|
||||
// https://help.apple.com/xcode/#/dev745c5c974
|
||||
|
||||
CODE_SIGN_STYLE = Manual
|
||||
CODE_SIGN_IDENTITY = Apple Development
|
||||
ENABLE_HARDENED_RUNTIME = YES
|
||||
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14
|
||||
MARKETING_VERSION = 1.5.4
|
||||
PRODUCT_NAME = baRSS
|
||||
PRODUCT_BUNDLE_IDENTIFIER = de.relikd.baRSS
|
||||
CURRENT_PROJECT_VERSION = 16928
|
||||
11
README.md
11
README.md
@@ -1,4 +1,4 @@
|
||||
[](#download--install)
|
||||
[](#download--install)
|
||||
[](https://github.com/relikd/baRSS/releases)
|
||||
[](https://github.com/relikd/baRSS/releases)
|
||||
[](LICENSE)
|
||||
@@ -35,7 +35,7 @@ Further, tuning the update frequently will decrease the traffic even more.
|
||||
Download & Install
|
||||
------------------
|
||||
|
||||
Requires macOS High Sierra (10.13) or higher.
|
||||
Requires macOS Mojave (10.14) or higher.
|
||||
|
||||
### Easy way
|
||||
Go to [releases](https://github.com/relikd/baRSS/releases) and downloaded the latest version.
|
||||
@@ -69,19 +69,24 @@ Hidden options
|
||||
baRSS has no option to launch it on start.
|
||||
However, you can still add the application to auto boot by adding it to the system login items:
|
||||
|
||||
`System Preferences > User > Login Items` (macOS 10-12)
|
||||
`System Preferences > User > Login Items` (macOS 10.x-12)
|
||||
`System Preferences > General > Login Items & Extensions` (macOS 13+)
|
||||
|
||||
|
||||
### UI options
|
||||
|
||||
1. If you hold down the option key and click on an article item, you can mark a single item (un-)read without opening it.
|
||||
I am still searching for a way to keep the menu open after click (if you know of a way, let me know!).
|
||||
|
||||
2. To add websites without RSS feed you can use the regex converter.
|
||||
Hold down the option key in the feed edit modal and click the red regex button.
|
||||
Though, admittedly, this is for experts only.
|
||||
I still have to find a nice user-friendly way to achieve this.
|
||||
|
||||
3. The option “Show only unread” will hide all items which have been read.
|
||||
You can hold down option key before opening the menu bar icon to show hidden articles regardless.
|
||||
This is a nice way to quickly lookup a hidden article without going into settings and twiddling with the checkbox.
|
||||
|
||||
|
||||
### CLI options
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
54AD90EA2E30C48400160925 /* Quartz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 54AD90E92E30C48400160925 /* Quartz.framework */; };
|
||||
54AD90EE2E30C48400160925 /* PreviewViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 54AD90ED2E30C48400160925 /* PreviewViewController.m */; };
|
||||
54AD90F12E30C48400160925 /* PreviewViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54AD90EF2E30C48400160925 /* PreviewViewController.xib */; };
|
||||
54AD90F72E30C48400160925 /* QLOPML.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 54AD90E72E30C48400160925 /* QLOPML.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
54AD90F72E30C48400160925 /* QLOPML.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 54AD90E72E30C48400160925 /* QLOPML.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
54B51704226DC339006C1B29 /* ModalFeedEditView.m in Sources */ = {isa = PBXBuildFile; fileRef = 54B51703226DC339006C1B29 /* ModalFeedEditView.m */; };
|
||||
54B517072270E990006C1B29 /* NSView+Ext.m in Sources */ = {isa = PBXBuildFile; fileRef = 54B517062270E92A006C1B29 /* NSView+Ext.m */; };
|
||||
54B6F14A231551B3002C94C9 /* FaviconDownload.m in Sources */ = {isa = PBXBuildFile; fileRef = 54B6F149231551B3002C94C9 /* FaviconDownload.m */; };
|
||||
@@ -105,15 +105,15 @@
|
||||
name = "Embed Frameworks";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
54AD90F62E30C48400160925 /* Embed App Extensions */ = {
|
||||
54AD90F62E30C48400160925 /* Embed Foundation Extensions */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 13;
|
||||
files = (
|
||||
54AD90F72E30C48400160925 /* QLOPML.appex in Embed App Extensions */,
|
||||
54AD90F72E30C48400160925 /* QLOPML.appex in Embed Foundation Extensions */,
|
||||
);
|
||||
name = "Embed App Extensions";
|
||||
name = "Embed Foundation Extensions";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
@@ -156,6 +156,8 @@
|
||||
546A6A2B22C584AF0034E806 /* SettingsAppearanceView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SettingsAppearanceView.h; sourceTree = "<group>"; };
|
||||
546A6A2D22C585580034E806 /* SettingsAboutView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SettingsAboutView.m; sourceTree = "<group>"; };
|
||||
546A6A2E22C585580034E806 /* SettingsAboutView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SettingsAboutView.h; sourceTree = "<group>"; };
|
||||
546BD1882EDE156000943942 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
|
||||
546BD1892EDE156000943942 /* Config-debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Config-debug.xcconfig"; sourceTree = "<group>"; };
|
||||
546FC43B21188AD5007CC3A3 /* SettingsFeeds.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SettingsFeeds.h; sourceTree = "<group>"; };
|
||||
546FC43C21188AD5007CC3A3 /* SettingsFeeds.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SettingsFeeds.m; sourceTree = "<group>"; };
|
||||
546FC44021189975007CC3A3 /* SettingsGeneral.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SettingsGeneral.h; sourceTree = "<group>"; };
|
||||
@@ -365,6 +367,8 @@
|
||||
54ACC27321061B3B0020715F = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
546BD1882EDE156000943942 /* Config.xcconfig */,
|
||||
546BD1892EDE156000943942 /* Config-debug.xcconfig */,
|
||||
540CD14821C094A2004AB594 /* README.md */,
|
||||
54892F1D2235285700271CBA /* CHANGELOG.md */,
|
||||
54ACC27E21061B3B0020715F /* baRSS */,
|
||||
@@ -536,7 +540,7 @@
|
||||
54ACC27A21061B3B0020715F /* Resources */,
|
||||
544DCCBB212A2B4D002DBC46 /* Embed Frameworks */,
|
||||
54FB05D12305BFAB00A088AD /* dynamic app name in db migration */,
|
||||
54AD90F62E30C48400160925 /* Embed App Extensions */,
|
||||
54AD90F62E30C48400160925 /* Embed Foundation Extensions */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -572,7 +576,7 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = YES;
|
||||
LastUpgradeCheck = 1640;
|
||||
LastUpgradeCheck = 2600;
|
||||
ORGANIZATIONNAME = relikd;
|
||||
TargetAttributes = {
|
||||
54ACC27B21061B3B0020715F = {
|
||||
@@ -772,6 +776,7 @@
|
||||
/* Begin XCBuildConfiguration section */
|
||||
54ACC28E21061B3C0020715F /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 546BD1892EDE156000943942 /* Config-debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
@@ -805,7 +810,6 @@
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 16720;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = UY657LKNHJ;
|
||||
@@ -822,8 +826,6 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MARKETING_VERSION = 1.5.1;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
@@ -832,6 +834,7 @@
|
||||
};
|
||||
54ACC28F21061B3C0020715F /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 546BD1882EDE156000943942 /* Config.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
@@ -866,7 +869,6 @@
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 16720;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = UY657LKNHJ;
|
||||
@@ -880,8 +882,6 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
||||
MARKETING_VERSION = 1.5.1;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
@@ -931,8 +931,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
"$(FRAMEWORK_SEARCH_PATHS)",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = de.relikd.baRSS.beta;
|
||||
PRODUCT_NAME = "$(TARGET_NAME) Beta";
|
||||
PRODUCT_NAME = "$(inherited) Beta";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
@@ -981,8 +980,6 @@
|
||||
"@executable_path/../Frameworks",
|
||||
"$(FRAMEWORK_SEARCH_PATHS)",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = de.relikd.baRSS;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@@ -1003,7 +1000,7 @@
|
||||
);
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = de.relikd.baRSS.beta.QLOPML;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(inherited).QLOPML";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
@@ -1021,7 +1018,7 @@
|
||||
"@executable_path/../../../../Frameworks",
|
||||
);
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = de.relikd.baRSS.QLOPML;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(inherited).QLOPML";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1640"
|
||||
LastUpgradeVersion = "2600"
|
||||
version = "1.8">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -225,9 +225,11 @@
|
||||
return nil; // if success == NO, do not modify unread state & exit
|
||||
}
|
||||
|
||||
NSInteger countChange = 0;
|
||||
for (FeedArticle *fa in list) {
|
||||
if (fa.unread == markRead) { // only if differs
|
||||
fa.unread = !markRead;
|
||||
countChange += markRead ? -1 : +1;
|
||||
}
|
||||
}
|
||||
[self saveContext:moc andParent:YES];
|
||||
@@ -242,8 +244,7 @@
|
||||
}
|
||||
|
||||
[moc reset];
|
||||
NSNumber *num = [NSNumber numberWithInteger: (markRead ? -1 : +1) * (NSInteger)list.count ];
|
||||
PostNotification(kNotificationTotalUnreadCountChanged, num);
|
||||
PostNotification(kNotificationTotalUnreadCountChanged, @(countChange));
|
||||
return dbRefs;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#import "UserPrefs.h"
|
||||
#import "StoreCoordinator.h"
|
||||
#import "Feed+Ext.h"
|
||||
#import "FeedGroup+Ext.h"
|
||||
#import "FeedArticle+Ext.h"
|
||||
|
||||
/**
|
||||
@@ -9,6 +10,11 @@
|
||||
*/
|
||||
static NSString* const kNotifyIdGlobal = @"global";
|
||||
|
||||
static NSString* const kCategoryDismissable = @"DISMISSIBLE";
|
||||
static NSString* const kActionOpenBackground = @"OPEN_IN_BACKGROUND";
|
||||
static NSString* const kActionMarkRead = @"MARK_READ_DONT_OPEN";
|
||||
static NSString* const kActionOpenOnly = @"OPEN_ONLY_DONT_MARK_READ";
|
||||
|
||||
|
||||
@implementation NotifyEndpoint
|
||||
|
||||
@@ -18,20 +24,27 @@ static NotificationType notifyType;
|
||||
/// Ask user for permission to send notifications @b AND register delegate to respond to alert banner clicks.
|
||||
/// @note Called every time user changes notification settings
|
||||
+ (void)activate {
|
||||
UNUserNotificationCenter *center = UNUserNotificationCenter.currentNotificationCenter;
|
||||
notifyType = UserPrefsNotificationType();
|
||||
|
||||
// even if disabled, register delegate. This allows to open previously sent notifications
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
singleton = [NotifyEndpoint new];
|
||||
UNUserNotificationCenter.currentNotificationCenter.delegate = singleton;
|
||||
center.delegate = singleton;
|
||||
});
|
||||
|
||||
if (notifyType == NotificationTypeDisabled) {
|
||||
return;
|
||||
}
|
||||
// register action types (allow mark read without opening notification)
|
||||
UNNotificationAction *openBackgroundAction = [UNNotificationAction actionWithIdentifier:kActionOpenBackground title:NSLocalizedString(@"Open in background", nil) options:UNNotificationActionOptionNone];
|
||||
UNNotificationAction *dontOpenAction = [UNNotificationAction actionWithIdentifier:kActionMarkRead title:NSLocalizedString(@"Mark read & dismiss", nil) options:UNNotificationActionOptionNone];
|
||||
UNNotificationAction *dontReadAction = [UNNotificationAction actionWithIdentifier:kActionOpenOnly title:NSLocalizedString(@"Open but keep unread", nil) options:UNNotificationActionOptionNone];
|
||||
UNNotificationCategory *category = [UNNotificationCategory categoryWithIdentifier:kCategoryDismissable actions:@[openBackgroundAction, dontOpenAction, dontReadAction] intentIdentifiers:@[] options:UNNotificationCategoryOptionNone];
|
||||
[center setNotificationCategories:[NSSet setWithObject:category]];
|
||||
|
||||
[UNUserNotificationCenter.currentNotificationCenter requestAuthorizationWithOptions:UNAuthorizationOptionAlert | UNAuthorizationOptionSound completionHandler:^(BOOL granted, NSError * _Nullable error) {
|
||||
[center requestAuthorizationWithOptions:UNAuthorizationOptionAlert | UNAuthorizationOptionSound completionHandler:^(BOOL granted, NSError * _Nullable error) {
|
||||
if (error) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSAlert *alert = [[NSAlert alloc] init];
|
||||
@@ -72,7 +85,7 @@ static NotificationType notifyType;
|
||||
if (count > 0) {
|
||||
[feed.managedObjectContext obtainPermanentIDsForObjects:@[feed] error:nil];
|
||||
[self send:feed.notificationID
|
||||
title:feed.title
|
||||
title:feed.group.anyName
|
||||
body:[NSString stringWithFormat:NSLocalizedString(@"%ld unread articles", nil), count]];
|
||||
}
|
||||
}
|
||||
@@ -84,7 +97,7 @@ static NotificationType notifyType;
|
||||
}
|
||||
[article.managedObjectContext obtainPermanentIDsForObjects:@[article] error:nil];
|
||||
[self send:article.notificationID
|
||||
title:article.feed.title
|
||||
title:article.feed.group.anyName
|
||||
body:article.title];
|
||||
}
|
||||
|
||||
@@ -105,6 +118,7 @@ static NotificationType notifyType;
|
||||
if (title != nil) msg.title = title;
|
||||
if (body != nil) msg.body = body;
|
||||
// common settings:
|
||||
msg.categoryIdentifier = kCategoryDismissable;
|
||||
// TODO: make sound configurable?
|
||||
msg.sound = [UNNotificationSound defaultSound];
|
||||
[self send:identifier content: msg];
|
||||
@@ -167,7 +181,12 @@ static NotificationType notifyType;
|
||||
return;
|
||||
}
|
||||
}
|
||||
[StoreCoordinator updateArticles:articles markRead:YES andOpen:YES inContext:moc];
|
||||
|
||||
// open-in-background performs the same operation as a normal click
|
||||
// the "background" part is triggered by _NOT_ having the UNNotificationActionOptionForeground option
|
||||
BOOL dontOpen = [response.actionIdentifier isEqualToString:kActionMarkRead];
|
||||
BOOL dontMarkRead = [response.actionIdentifier isEqualToString:kActionOpenOnly];
|
||||
[StoreCoordinator updateArticles:articles markRead:!dontMarkRead andOpen:!dontOpen inContext:moc];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -23,18 +23,45 @@
|
||||
ColumnIcon(self, __X, RSSImageSettingsFeed, NSLocalizedString(@"Show in feed menu", nil));
|
||||
// Generate checkbox matrix
|
||||
self.y = PAD_WIN + IconSize + PAD_S;
|
||||
[self entry:NSLocalizedString(@"Tint menu bar icon on unread", nil) c1:Pref_globalTintMenuIcon c2:nil c3:nil];
|
||||
[self entry:NSLocalizedString(@"Update all feeds", nil) c1:Pref_globalUpdateAll c2:nil c3:nil];
|
||||
[self entry:NSLocalizedString(@"Open all unread", nil) c1:Pref_globalOpenUnread c2:Pref_groupOpenUnread c3:Pref_feedOpenUnread];
|
||||
[self entry:NSLocalizedString(@"Mark all read", nil) c1:Pref_globalMarkRead c2:Pref_groupMarkRead c3:Pref_feedMarkRead];
|
||||
[self entry:NSLocalizedString(@"Mark all unread", nil) c1:Pref_globalMarkUnread c2:Pref_groupMarkUnread c3:Pref_feedMarkUnread];
|
||||
[self entry:NSLocalizedString(@"Number of unread articles", nil) c1:Pref_globalUnreadCount c2:Pref_groupUnreadCount c3:Pref_feedUnreadCount];
|
||||
[self entry:NSLocalizedString(@"Indicator for unread articles", nil) c1:nil c2:Pref_groupUnreadIndicator c3:Pref_feedUnreadIndicator];
|
||||
[self entry:NSLocalizedString(@"Show only unread / hide read", nil) c1:nil c2:Pref_groupUnreadOnly c3:Pref_feedUnreadOnly];
|
||||
[[self entry:NSLocalizedString(@"Truncate article title", nil) c1:nil c2:nil c3:Pref_feedTruncateTitle]
|
||||
tooltip:NSLocalizedString(@"Truncate article title after 60 characters", nil)];
|
||||
[[self entry:NSLocalizedString(@"Limit number of articles", nil) c1:nil c2:nil c3:Pref_feedLimitArticles]
|
||||
tooltip:NSLocalizedString(@"Display at most 40 articles in feed menu", nil)];
|
||||
[[self entry:NSLocalizedString(@"Tint menu bar icon on unread", nil)
|
||||
c1:Pref_globalTintMenuIcon c2:nil c3:nil]
|
||||
tooltip:NSLocalizedString(@"If active, a color will indicate if there are unread articles.", nil)];
|
||||
|
||||
[[self entry:NSLocalizedString(@"Update all feeds", nil)
|
||||
c1:Pref_globalUpdateAll c2:nil c3:nil]
|
||||
tooltip:NSLocalizedString(@"Show a button in status bar menu to reload all feeds. This will force fetch new online content regardless of next-update timer.", nil)];
|
||||
|
||||
[[self entry:NSLocalizedString(@"Open all unread", nil)
|
||||
c1:Pref_globalOpenUnread c2:Pref_groupOpenUnread c3:Pref_feedOpenUnread]
|
||||
tooltip:NSLocalizedString(@"Show a button to open unread articles. (globally / per group / per feed)\n\nIf you hold down option key, this will become an “open a few” unread articles button.", nil)];
|
||||
|
||||
[[self entry:NSLocalizedString(@"Mark all read", nil)
|
||||
c1:Pref_globalMarkRead c2:Pref_groupMarkRead c3:Pref_feedMarkRead]
|
||||
tooltip:NSLocalizedString(@"Show a button to mark articles read. (globally / per group / per feed)", nil)];
|
||||
|
||||
[[self entry:NSLocalizedString(@"Mark all unread", nil)
|
||||
c1:Pref_globalMarkUnread c2:Pref_groupMarkUnread c3:Pref_feedMarkUnread]
|
||||
tooltip:NSLocalizedString(@"Show a button to mark articles unread. (globally / per group / per feed)\n\nYou can hold down option key and click on an article to toggle that item (un-)read.", nil)];
|
||||
|
||||
[[self entry:NSLocalizedString(@"Number of unread articles", nil)
|
||||
c1:Pref_globalUnreadCount c2:Pref_groupUnreadCount c3:Pref_feedUnreadCount]
|
||||
tooltip:NSLocalizedString(@"Show count of unread articles in parenthesis. (on menu bar icon / on group folder / on feed folder)", nil)];
|
||||
|
||||
[[self entry:NSLocalizedString(@"Indicator for unread articles", nil)
|
||||
c1:nil c2:Pref_groupUnreadIndicator c3:Pref_feedUnreadIndicator]
|
||||
tooltip:NSLocalizedString(@"Show blue dot on menu items with unread articles. (on group & feed folder / on article entry)", nil)];
|
||||
|
||||
[[self entry:NSLocalizedString(@"Show only unread", nil)
|
||||
c1:nil c2:Pref_groupUnreadOnly c3:Pref_feedUnreadOnly]
|
||||
tooltip:NSLocalizedString(@"Hide articles which have been read. (hide group & feed folders / hide articles inside of feed folder)", nil)];
|
||||
|
||||
[[self entry:NSLocalizedString(@"Truncate article title", nil)
|
||||
c1:nil c2:nil c3:Pref_feedTruncateTitle]
|
||||
tooltip:NSLocalizedString(@"Truncate article title after 60 characters. If a title is longer than that, show an ellipsis character “…” instead.", nil)];
|
||||
|
||||
[[self entry:NSLocalizedString(@"Limit number of articles", nil)
|
||||
c1:nil c2:nil c3:Pref_feedLimitArticles]
|
||||
tooltip:NSLocalizedString(@"Display at most 40 articles in feed menu. Remaining articles will be hidden from view but are still there. Unread count may be confusing as it will also count unread and hidden articles.", nil)];
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@
|
||||
NSUserInterfaceItemIdentifier const CustomCellName = @"NameColumnCell";
|
||||
|
||||
- (instancetype)initWithFrame:(NSRect)frameRect {
|
||||
self = [super initWithFrame:frameRect];
|
||||
self = [super initWithFrame:NSMakeRect(0, 0, 100, 0)];
|
||||
self.identifier = CustomCellName;
|
||||
self.imageView = [[NSView imageView:nil size:16] placeIn:self x:1 yTop:1];
|
||||
self.imageView.accessibilityLabel = NSLocalizedString(@"Feed icon", nil);
|
||||
@@ -195,7 +195,7 @@ NSUserInterfaceItemIdentifier const CustomCellName = @"NameColumnCell";
|
||||
NSUserInterfaceItemIdentifier const CustomCellRefresh = @"RefreshColumnCell";
|
||||
|
||||
- (instancetype)initWithFrame:(NSRect)frameRect {
|
||||
self = [super initWithFrame:frameRect];
|
||||
self = [super initWithFrame:NSMakeRect(0, 0, 100, 0)];
|
||||
self.identifier = CustomCellRefresh;
|
||||
self.textField = [[[[NSView label:@""] textRight] placeIn:self x:0 yTop:0] sizeToRight:0];
|
||||
self.textField.accessibilityTitle = @" "; // otherwise groups and separators will say 'text'
|
||||
@@ -224,7 +224,7 @@ NSUserInterfaceItemIdentifier const CustomCellRefresh = @"RefreshColumnCell";
|
||||
NSUserInterfaceItemIdentifier const CustomCellSeparator = @"SeparatorColumnCell";
|
||||
|
||||
- (instancetype)initWithFrame:(NSRect)frameRect {
|
||||
self = [super initWithFrame:frameRect];
|
||||
self = [super initWithFrame:NSMakeRect(0, 0, 100, 0)];
|
||||
self.identifier = CustomCellSeparator;
|
||||
[[[[DrawSeparator alloc] initWithFrame:self.frame] placeIn:self x:0 y:0] sizableWidthAndHeight];
|
||||
return self;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface BarMenu : NSObject <NSMenuDelegate>
|
||||
@property (assign) BOOL showHidden;
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
- (instancetype)initWithStatusItem:(BarStatusItem*)statusItem NS_DESIGNATED_INITIALIZER;
|
||||
@end
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
- (void)setFeedGroups:(NSArray<FeedGroup*>*)sortedList forMenu:(NSMenu*)menu {
|
||||
[menu insertDefaultHeader];
|
||||
for (FeedGroup *fg in sortedList) {
|
||||
[menu insertFeedGroupItem:fg withUnread:self.unreadMap].submenu.delegate = self;
|
||||
[menu insertFeedGroupItem:fg withUnread:self.unreadMap showHidden:_showHidden].submenu.delegate = self;
|
||||
}
|
||||
[menu setHeaderHasUnread:self.unreadMap[menu.titleIndexPath]];
|
||||
}
|
||||
@@ -78,7 +78,7 @@
|
||||
BOOL onlyUnread = UserPrefsBool(Pref_feedUnreadOnly);
|
||||
|
||||
for (FeedArticle *fa in sortedList) {
|
||||
if (onlyUnread && !fa.unread)
|
||||
if (onlyUnread && !fa.unread && !_showHidden)
|
||||
continue;
|
||||
if (--mc < 0) // mc == 0 will first decrement to -1, then evaluate
|
||||
break;
|
||||
|
||||
@@ -152,6 +152,7 @@
|
||||
-(void)menuWillOpen:(NSMenu *)menu {
|
||||
_mainMenu = menu; // autoreleased once closed
|
||||
self.barMenu = [[BarMenu alloc] initWithStatusItem:self];
|
||||
self.barMenu.showHidden = NSEvent.modifierFlags & NSEventModifierFlagOption;
|
||||
|
||||
[self insertMainMenuHeader:menu];
|
||||
[self.barMenu menuNeedsUpdate:menu];
|
||||
|
||||
@@ -9,7 +9,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property (readonly) BOOL isFeedMenu;
|
||||
|
||||
// Generator
|
||||
- (nullable NSMenuItem*)insertFeedGroupItem:(FeedGroup*)fg withUnread:(MapUnreadTotal*)unreadMap;
|
||||
- (nullable NSMenuItem*)insertFeedGroupItem:(FeedGroup*)fg withUnread:(MapUnreadTotal*)unreadMap showHidden:(BOOL)showHidden;
|
||||
- (void)insertDefaultHeader;
|
||||
// Update menu
|
||||
- (void)setHeaderHasUnread:(UnreadTotal*)count;
|
||||
|
||||
@@ -44,7 +44,7 @@ typedef NS_ENUM(NSInteger, MenuItemTag) {
|
||||
#pragma mark - Generator -
|
||||
|
||||
/// Create new @c NSMenuItem with empty submenu and append it to the menu. @return Inserted item.
|
||||
- (nullable NSMenuItem*)insertFeedGroupItem:(FeedGroup*)fg withUnread:(MapUnreadTotal*)unreadMap {
|
||||
- (nullable NSMenuItem*)insertFeedGroupItem:(FeedGroup*)fg withUnread:(MapUnreadTotal*)unreadMap showHidden:(BOOL)showHidden {
|
||||
unichar chr = '-';
|
||||
NSMenuItem *item = nil;
|
||||
switch (fg.type) {
|
||||
@@ -57,7 +57,7 @@ typedef NS_ENUM(NSInteger, MenuItemTag) {
|
||||
NSUInteger unread = unreadMap[[t substringFromIndex:2]].unread;
|
||||
|
||||
// Check user preferences to show only unread entries
|
||||
if (unread == 0
|
||||
if (unread == 0 && !showHidden
|
||||
&& (fg.type == FEED || fg.type == GROUP)
|
||||
&& UserPrefsBool(Pref_groupUnreadOnly)) {
|
||||
item.hidden = YES;
|
||||
|
||||
Reference in New Issue
Block a user