Skip to content

Commit 8950682

Browse files
authored
Merge pull request #2126 from Eitot/feature/unread-counts
Add setting to hide unread counts in the main window, menu bar and Dock
2 parents 92652fb + bd2f820 commit 8950682

9 files changed

Lines changed: 120 additions & 35 deletions

File tree

Vienna/Interfaces/Base.lproj/Preferences.storyboard

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="23504" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="VBW-RM-gbf">
2+
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="24765" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="VBW-RM-gbf">
33
<dependencies>
44
<deployment identifier="macosx"/>
5-
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23504"/>
5+
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="24765"/>
66
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
77
</dependencies>
88
<scenes>
@@ -245,7 +245,7 @@
245245
</viewController>
246246
<customObject id="NAv-gX-ZtR" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
247247
</objects>
248-
<point key="canvasLocation" x="127" y="1302"/>
248+
<point key="canvasLocation" x="127" y="1329"/>
249249
</scene>
250250
<!--Advanced-->
251251
<scene sceneID="99N-ez-bLO">
@@ -383,9 +383,19 @@
383383
<objects>
384384
<viewController title="Appearance" id="U0J-a5-0Pj" customClass="AppearancePreferencesViewController" sceneMemberID="viewController">
385385
<view key="view" id="57X-N5-EiK">
386-
<rect key="frame" x="0.0" y="0.0" width="450" height="231"/>
386+
<rect key="frame" x="0.0" y="0.0" width="450" height="274"/>
387387
<autoresizingMask key="autoresizingMask"/>
388388
<subviews>
389+
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="mEc-X5-kBE">
390+
<rect key="frame" x="20" y="238" width="390" height="16"/>
391+
<buttonCell key="cell" type="check" title="Show unread counts in the main window, menu bar and Dock" bezelStyle="regularSquare" imagePosition="leading" state="on" inset="2" id="LdO-1H-O0m">
392+
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
393+
<font key="font" metaFont="system"/>
394+
</buttonCell>
395+
<connections>
396+
<binding destination="ecW-rA-cY4" name="value" keyPath="values.ShowUnreadCounts" id="s1Z-F2-Xde"/>
397+
</connections>
398+
</button>
389399
<stackView distribution="fill" orientation="horizontal" alignment="firstBaseline" horizontalStackHuggingPriority="750" verticalStackHuggingPriority="750" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tK3-5S-9SP">
390400
<rect key="frame" x="20" y="191" width="200" height="20"/>
391401
<subviews>
@@ -532,7 +542,8 @@
532542
<constraint firstItem="1RP-By-TQq" firstAttribute="leading" secondItem="WR8-u8-Vm9" secondAttribute="trailing" constant="8" symbolic="YES" id="8hd-ut-fEe"/>
533543
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="tK3-5S-9SP" secondAttribute="trailing" constant="20" symbolic="YES" id="DTQ-VU-XI1"/>
534544
<constraint firstAttribute="trailing" secondItem="5dd-GM-M3b" secondAttribute="trailing" constant="20" symbolic="YES" id="Ie4-lM-6py"/>
535-
<constraint firstItem="tK3-5S-9SP" firstAttribute="top" secondItem="57X-N5-EiK" secondAttribute="top" constant="20" symbolic="YES" id="M3e-sb-L21"/>
545+
<constraint firstItem="tK3-5S-9SP" firstAttribute="top" secondItem="mEc-X5-kBE" secondAttribute="bottom" constant="20" id="M3e-sb-L21"/>
546+
<constraint firstItem="mEc-X5-kBE" firstAttribute="top" secondItem="57X-N5-EiK" secondAttribute="top" constant="20" symbolic="YES" id="Qsi-Ug-QPD"/>
536547
<constraint firstItem="tK3-5S-9SP" firstAttribute="leading" secondItem="57X-N5-EiK" secondAttribute="leading" constant="20" symbolic="YES" id="UEc-SH-rwC"/>
537548
<constraint firstItem="WR8-u8-Vm9" firstAttribute="firstBaseline" secondItem="1RP-By-TQq" secondAttribute="firstBaseline" id="UfI-Uc-u7Y"/>
538549
<constraint firstItem="1fA-eb-j2c" firstAttribute="top" secondItem="rx3-gW-nqt" secondAttribute="bottom" constant="20" id="VxU-RL-tKN"/>
@@ -541,6 +552,7 @@
541552
<constraint firstItem="rx3-gW-nqt" firstAttribute="leading" secondItem="57X-N5-EiK" secondAttribute="leading" constant="20" symbolic="YES" id="ZGJ-2b-LbP"/>
542553
<constraint firstItem="wIv-zK-n6S" firstAttribute="top" secondItem="1fA-eb-j2c" secondAttribute="bottom" constant="8" symbolic="YES" id="ZJq-Px-ZUw"/>
543554
<constraint firstItem="MkS-Vx-yr4" firstAttribute="leading" secondItem="wIv-zK-n6S" secondAttribute="trailing" constant="8" symbolic="YES" id="ZdQ-Bp-1xM"/>
555+
<constraint firstItem="mEc-X5-kBE" firstAttribute="leading" secondItem="57X-N5-EiK" secondAttribute="leading" constant="20" symbolic="YES" id="ayz-jC-lwm"/>
544556
<constraint firstItem="1RP-By-TQq" firstAttribute="firstBaseline" secondItem="5dd-GM-M3b" secondAttribute="firstBaseline" id="bKL-p5-Op5"/>
545557
<constraint firstAttribute="trailing" secondItem="1fA-eb-j2c" secondAttribute="trailing" constant="20" symbolic="YES" id="bYP-Li-ls9"/>
546558
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="XVw-n1-nWq" secondAttribute="trailing" constant="20" symbolic="YES" id="cEn-PU-HFm"/>
@@ -549,6 +561,7 @@
549561
<constraint firstItem="rx3-gW-nqt" firstAttribute="top" secondItem="1RP-By-TQq" secondAttribute="bottom" constant="8" symbolic="YES" id="qwr-SO-aPv"/>
550562
<constraint firstAttribute="bottom" secondItem="wIv-zK-n6S" secondAttribute="bottom" constant="20" symbolic="YES" id="rKP-F0-CLI"/>
551563
<constraint firstItem="XVw-n1-nWq" firstAttribute="leading" secondItem="57X-N5-EiK" secondAttribute="leading" constant="20" symbolic="YES" id="ufD-CP-sC9"/>
564+
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="mEc-X5-kBE" secondAttribute="trailing" constant="20" symbolic="YES" id="vXn-xU-mY0"/>
552565
</constraints>
553566
</view>
554567
<connections>
@@ -566,7 +579,7 @@
566579
</numberFormatter>
567580
<userDefaultsController representsSharedInstance="YES" id="ecW-rA-cY4"/>
568581
</objects>
569-
<point key="canvasLocation" x="127" y="1004"/>
582+
<point key="canvasLocation" x="127" y="997"/>
570583
</scene>
571584
<!--General-->
572585
<scene sceneID="Bqg-9r-5LY">

Vienna/Interfaces/mul.lproj/Preferences.xcstrings

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4128,6 +4128,18 @@
41284128
}
41294129
}
41304130
},
4131+
"LdO-1H-O0m.title" : {
4132+
"comment" : "Class = \"NSButtonCell\"; title = \"Show unread counts in the main window, menu bar and Dock\"; ObjectID = \"LdO-1H-O0m\";",
4133+
"extractionState" : "extracted_with_value",
4134+
"localizations" : {
4135+
"en" : {
4136+
"stringUnit" : {
4137+
"state" : "new",
4138+
"value" : "Show unread counts in the main window, menu bar and Dock"
4139+
}
4140+
}
4141+
}
4142+
},
41314143
"Lfh-L2-KcO.title" : {
41324144
"comment" : "Class = \"NSViewController\"; title = \"Syncing\"; ObjectID = \"Lfh-L2-KcO\";",
41334145
"extractionState" : "extracted_with_value",

Vienna/Sources/Application/AppController.m

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ @implementation AppController {
108108

109109
NSMutableDictionary *scriptPathMappings;
110110
NSStatusItem *appStatusItem;
111-
NSInteger lastCountOfUnread;
112111
NSMenuItem *scriptsMenuItem;
113112
BOOL didCompleteInitialisation;
114113
NSString *searchString;
@@ -121,7 +120,6 @@ -(instancetype)init
121120
{
122121
if ((self = [super init]) != nil) {
123122
scriptPathMappings = [[NSMutableDictionary alloc] init];
124-
lastCountOfUnread = 0;
125123
appStatusItem = nil;
126124
scriptsMenuItem = nil;
127125
didCompleteInitialisation = NO;
@@ -282,6 +280,12 @@ -(void)applicationDidFinishLaunching:(NSNotification *)aNot
282280
options:0
283281
context:VNAAppControllerObserverContext];
284282

283+
NSUserDefaults *userDefaults = NSUserDefaults.standardUserDefaults;
284+
[userDefaults addObserver:self
285+
forKeyPath:MAPref_ShowUnreadCounts
286+
options:0
287+
context:VNAAppControllerObserverContext];
288+
285289
// Load the styles into the main menu.
286290
[self populateStyleMenu];
287291

@@ -1024,7 +1028,7 @@ - (void)observeValueForKeyPath:(NSString *)keyPath
10241028
return;
10251029
}
10261030

1027-
if ([keyPath isEqualToString:NSStringFromSelector(@selector(numberOfPlugins))]) {
1031+
if ([object isEqual:self.pluginManager]) {
10281032
NSMenu *menu = ((ViennaApp *)NSApp).articleMenu;
10291033

10301034
// Remove any previously added plug-in menu items.
@@ -1042,6 +1046,13 @@ - (void)observeValueForKeyPath:(NSString *)keyPath
10421046

10431047
// Repopulate the menu.
10441048
[self populatePluginsMenu];
1049+
return;
1050+
}
1051+
1052+
if ([object isEqual:NSUserDefaults.standardUserDefaults]) {
1053+
if ([keyPath isEqualToString:MAPref_ShowUnreadCounts]) {
1054+
[self showUnreadCountOnApplicationIconAndWindowTitle];
1055+
}
10451056
}
10461057
}
10471058

@@ -1157,10 +1168,6 @@ - (void)handleUpdateUnreadCount:(NSNotification *)nc
11571168
- (void)showUnreadCountOnApplicationIconAndWindowTitle {
11581169
@synchronized(NSApp.dockTile) {
11591170
NSInteger currentCountOfUnread = db.countOfUnread;
1160-
if (currentCountOfUnread == lastCountOfUnread) {
1161-
return;
1162-
}
1163-
lastCountOfUnread = currentCountOfUnread;
11641171

11651172
// Always update the app status icon first
11661173
[self setAppStatusBarIcon];
@@ -1170,8 +1177,11 @@ - (void)showUnreadCountOnApplicationIconAndWindowTitle {
11701177
NSApp.dockTile.badgeLabel = nil;
11711178
self.mainWindowController.unreadCount = 0;
11721179
} else {
1173-
NSString *countdown = [NSString stringWithFormat:@"%li", (long)currentCountOfUnread];
1174-
NSApp.dockTile.badgeLabel = countdown;
1180+
if ([NSUserDefaults.standardUserDefaults boolForKey:MAPref_ShowUnreadCounts]) {
1181+
NSApp.dockTile.badgeLabel = [NSString stringWithFormat:@"%li", currentCountOfUnread];
1182+
} else {
1183+
NSApp.dockTile.badgeLabel = nil;
1184+
}
11751185
self.mainWindowController.unreadCount = currentCountOfUnread;
11761186
}
11771187
}
@@ -1330,21 +1340,34 @@ -(void)showAppInStatusBar
13301340
*/
13311341
-(void)setAppStatusBarIcon
13321342
{
1333-
if (appStatusItem != nil) {
1334-
if (lastCountOfUnread == 0) {
1335-
NSImage *statusBarImage = [NSImage imageNamed:ACImageNameStatusBarIcon];
1336-
statusBarImage.template = YES;
1343+
if (!appStatusItem) {
1344+
return;
1345+
}
1346+
1347+
NSInteger unreadCount = db.countOfUnread;
1348+
if (unreadCount == 0) {
1349+
NSImage *statusBarImage = [NSImage imageNamed:ACImageNameStatusBarIcon];
1350+
statusBarImage.template = YES;
1351+
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
1352+
context.allowsImplicitAnimation = YES;
13371353
appStatusItem.button.image = statusBarImage;
13381354
appStatusItem.button.title = @"";
13391355
appStatusItem.button.imagePosition = NSImageOnly;
1340-
} else {
1341-
NSImage *statusBarImage = [NSImage imageNamed:ACImageNameStatusBarIconUnread];
1342-
statusBarImage.template = YES;
1356+
}];
1357+
} else {
1358+
NSImage *statusBarImage = [NSImage imageNamed:ACImageNameStatusBarIconUnread];
1359+
statusBarImage.template = YES;
1360+
[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
1361+
context.allowsImplicitAnimation = YES;
13431362
appStatusItem.button.image = statusBarImage;
1344-
appStatusItem.button.title = [NSString stringWithFormat:@"%ld", (long)lastCountOfUnread];
1363+
if ([NSUserDefaults.standardUserDefaults boolForKey:MAPref_ShowUnreadCounts]) {
1364+
appStatusItem.button.title = [NSString stringWithFormat:@"%ld", unreadCount];
1365+
} else {
1366+
appStatusItem.button.title = @"";
1367+
}
13451368
appStatusItem.button.imagePosition = NSImageLeading;
1346-
}
1347-
}
1369+
}];
1370+
}
13481371
}
13491372

13501373
/* handleRSSLink
@@ -2922,6 +2945,11 @@ -(void)dealloc
29222945
forKeyPath:NSStringFromSelector(@selector(numberOfPlugins))
29232946
context:VNAAppControllerObserverContext];
29242947

2948+
NSUserDefaults *userDefaults = NSUserDefaults.standardUserDefaults;
2949+
[userDefaults removeObserver:self
2950+
forKeyPath:MAPref_ShowUnreadCounts
2951+
context:VNAAppControllerObserverContext];
2952+
29252953
[[NSNotificationCenter defaultCenter] removeObserver:self];
29262954
}
29272955

Vienna/Sources/Database/Database.m

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,7 +1056,7 @@ -(BOOL)wrappedDeleteFolder:(NSInteger)folderId
10561056

10571057
// Delete all articles in this folder then delete ourselves.
10581058
folder = [self folderFromID:folderId];
1059-
_countOfUnread -= folder.unreadCount;
1059+
self.countOfUnread -= folder.unreadCount;
10601060
if (folder.type == VNAFolderTypeSmart) {
10611061
[queue inDatabase:^(FMDatabase *db) {
10621062
[db executeUpdate:@"DELETE FROM smart_folders WHERE folder_id=?", @(folderId)];
@@ -1976,7 +1976,7 @@ -(void)initFolderArray
19761976
NSAssert(self.databaseQueue, @"Database not assigned for this item");
19771977

19781978
// Keep running count of total unread articles
1979-
_countOfUnread = 0;
1979+
__block NSInteger countOfUnread = 0;
19801980

19811981
FMDatabaseQueue *queue = self.databaseQueue;
19821982

@@ -2013,7 +2013,7 @@ -(void)initFolderArray
20132013
folder.lastUpdate = lastUpdate;
20142014
[folder setFlag:flags];
20152015
if (unreadCount > 0) {
2016-
self->_countOfUnread += unreadCount;
2016+
countOfUnread += unreadCount;
20172017
}
20182018
self.foldersDict[@(newItemId)] = folder;
20192019

@@ -2050,6 +2050,9 @@ -(void)initFolderArray
20502050
}
20512051
[results close];
20522052
}];
2053+
2054+
_countOfUnread = countOfUnread;
2055+
20532056
// Fix the childUnreadCount for every parent
20542057
for (Folder * folder in [self.foldersDict objectEnumerator]) {
20552058
if (folder.unreadCount > 0 && folder.parentId != VNAFolderTypeRoot) {
@@ -2508,7 +2511,7 @@ -(void)markStarredArticlesFromFolder:(Folder *)folder guidArray:(NSArray *)guidA
25082511
*/
25092512
-(void)setFolderUnreadCount:(Folder *)folder adjustment:(NSUInteger)adjustment
25102513
{
2511-
_countOfUnread += adjustment;
2514+
self.countOfUnread += adjustment;
25122515
NSInteger newCount = folder.unreadCount + adjustment;
25132516
folder.unreadCount = newCount;
25142517
FMDatabaseQueue *queue = self.databaseQueue;
@@ -2667,7 +2670,7 @@ -(void)close
26672670
self.trashFolder = nil;
26682671
self.searchFolder = nil;
26692672
self.initializedfoldersDict = NO;
2670-
_countOfUnread = 0;
2673+
self.countOfUnread = 0;
26712674
[self.databaseQueue close];
26722675
}
26732676

Vienna/Sources/Main window/FoldersTree.m

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,19 +127,26 @@ -(void)initialiseFoldersTree
127127
forKeyPath:MAPref_ShowFeedsWithUnreadItemsInBold
128128
options:0
129129
context:VNAFoldersTreeObserverContext];
130+
[userDefaults addObserver:self
131+
forKeyPath:MAPref_ShowUnreadCounts
132+
options:0
133+
context:VNAFoldersTreeObserverContext];
130134
}
131135

132136
- (void)dealloc
133137
{
134138
[NSNotificationCenter.defaultCenter removeObserver:self];
135139

136-
NSUserDefaults *userDefaults;
140+
NSUserDefaults *userDefaults = NSUserDefaults.standardUserDefaults;
137141
[userDefaults removeObserver:self
138142
forKeyPath:MAPref_FeedListSizeMode
139143
context:VNAFoldersTreeObserverContext];
140144
[userDefaults removeObserver:self
141145
forKeyPath:MAPref_ShowFeedsWithUnreadItemsInBold
142146
context:VNAFoldersTreeObserverContext];
147+
[userDefaults removeObserver:self
148+
forKeyPath:MAPref_ShowUnreadCounts
149+
context:VNAFoldersTreeObserverContext];
143150
}
144151

145152
-(void)handleOpenReaderFolderChange:(NSNotification *)nc
@@ -1082,9 +1089,15 @@ - (void)observeValueForKeyPath:(NSString *)keyPath
10821089

10831090
if ([keyPath isEqualToString:MAPref_FeedListSizeMode]) {
10841091
[self updateCellSize:[userDefaults integerForKey:MAPref_FeedListSizeMode]];
1092+
return;
10851093
}
10861094
if ([keyPath isEqualToString:MAPref_ShowFeedsWithUnreadItemsInBold]) {
10871095
[self.outlineView reloadDataWhilePreservingSelection];
1096+
return;
1097+
}
1098+
if ([keyPath isEqualToString:MAPref_ShowUnreadCounts]) {
1099+
[self.outlineView reloadDataWhilePreservingSelection];
1100+
return;
10881101
}
10891102
}
10901103

@@ -1388,7 +1401,7 @@ - (NSView *)outlineView:(NSOutlineView *)outlineView
13881401
}
13891402

13901403
BOOL useEmphasis = [prefs boolForKey:MAPref_ShowFeedsWithUnreadItemsInBold];
1391-
1404+
BOOL showUnreadCounts = [prefs boolForKey:MAPref_ShowUnreadCounts];
13921405
switch (folder.type) {
13931406
case VNAFolderTypeSmart:
13941407
case VNAFolderTypeTrash:
@@ -1398,12 +1411,12 @@ - (NSView *)outlineView:(NSOutlineView *)outlineView
13981411
break;
13991412
case VNAFolderTypeGroup:
14001413
cellView.emphasized = useEmphasis && folder.childUnreadCount > 0 && ![outlineView isItemExpanded:item];
1401-
cellView.canShowUnreadCount = ![outlineView isItemExpanded:item];
1414+
cellView.canShowUnreadCount = showUnreadCounts ? ![outlineView isItemExpanded:item] : NO;
14021415
cellView.unreadCount = folder.childUnreadCount;
14031416
break;
14041417
default:
14051418
cellView.emphasized = useEmphasis && folder.unreadCount > 0;
1406-
cellView.canShowUnreadCount = YES;
1419+
cellView.canShowUnreadCount = showUnreadCounts;
14071420
cellView.unreadCount = folder.unreadCount;
14081421
}
14091422

Vienna/Sources/Main window/MainWindowController.swift

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ final class MainWindowController: NSWindowController {
9090
private func updateSubtitle() {
9191
// Unread counter
9292
var countString = String()
93-
if unreadCount > 0 {
93+
if unreadCount > 0 && UserDefaults.standard.bool(forKey: MAPref_ShowUnreadCounts) {
9494
let number = NSNumber(value: unreadCount)
9595
let formattedNumber = NumberFormatter.localizedString(from: number, number: .decimal)
9696
countString = String(format: NSLocalizedString("%@ unread", comment: ""), formattedNumber)
@@ -378,6 +378,9 @@ extension MainWindowController: NSWindowDelegate {
378378
if change.newValue is String {
379379
self?.statusLabel.stringValue = manager.statusMessage
380380
}
381+
},
382+
UserDefaults.standard.observe(\.ShowUnreadCounts) { [weak self] _, _ in
383+
self?.updateSubtitle()
381384
}
382385
]
383386
}
@@ -741,6 +744,16 @@ extension MainWindowController: RSSSubscriber {
741744
}
742745
}
743746

747+
// MARK: - UserDefaults
748+
749+
extension UserDefaults {
750+
751+
// swiftlint:disable:next identifier_name
752+
@objc fileprivate dynamic var ShowUnreadCounts: Bool {
753+
return bool(forKey: MAPref_ShowUnreadCounts)
754+
}
755+
}
756+
744757
// MARK: - Constants
745758

746759
private extension NSToolbarItem.Identifier {

Vienna/Sources/Preferences window/Preferences.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ -(NSDictionary *)allocFactoryDefaults
243243
defaultValues[MAPref_ShowDetailsOnFeedCredentialsDialog] = boolNo;
244244
defaultValues[MAPref_ShowEnclosureBar] = boolYes;
245245
defaultValues[MAPref_MenuEnableActionImages] = boolNo;
246+
defaultValues[MAPref_ShowUnreadCounts] = @YES;
246247

247248
// Archives
248249
defaultValues[MAPref_ArticleListFont] = [NSKeyedArchiver archivedDataWithRootObject:defaultFont

0 commit comments

Comments
 (0)