Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0dec3adf9 | ||
|
|
f7eb63bed9 | ||
|
|
23f4f125db | ||
|
|
b3940f103a | ||
|
|
239527908f | ||
|
|
7df70a7936 | ||
|
|
e8c4c06d33 | ||
|
|
a9c0e64689 |
39
CHANGELOG.md
39
CHANGELOG.md
@@ -8,7 +8,23 @@ and this project does adhere to [Semantic Versioning](https://semver.org/spec/v2
|
||||
## [Unreleased]
|
||||
|
||||
|
||||
## [1.1.0] - 2020-01-17
|
||||
## [1.1.3] – 2020-12-18
|
||||
### Fixed
|
||||
- Recognize YouTube channel URLs in the format `/c/channel-name`
|
||||
|
||||
|
||||
## [1.1.2] – 2020-11-27
|
||||
### Fixed
|
||||
- Fixes hidden color option for marking unread entries. Unread menu entries did use `colorStatusIconTint` instead of `colorUnreadIndicator` (thanks @tchek)
|
||||
- Workaround for not displaying status bar highlight color in macOS 11.0 (issue #7)
|
||||
|
||||
|
||||
## [1.1.1] – 2020-08-31
|
||||
### Fixed
|
||||
- Feed indices weren't updated properly which resulted in empty feed menus (issue: #6)
|
||||
|
||||
|
||||
## [1.1.0] – 2020-01-17
|
||||
### Added
|
||||
- *QuickLook*: Thumbnail previews for OPML files (QLOPML v1.3)
|
||||
- *Status Bar Menu*: Tint menu bar icon with Accent color (macOS 10.14+)
|
||||
@@ -17,19 +33,19 @@ and this project does adhere to [Semantic Versioning](https://semver.org/spec/v2
|
||||
- Resolved Xcode warnings in Xcode 11
|
||||
|
||||
|
||||
## [1.0.2] - 2019-10-25
|
||||
## [1.0.2] – 2019-10-25
|
||||
### Fixed
|
||||
- *Status Bar Menu*: Preferences could not be opened on macOS 10.15
|
||||
- *Status Bar Menu*: Menu flickering resulting in a hang on macOS 10.15
|
||||
- *UI*: Text color in `About` tab
|
||||
|
||||
|
||||
## [1.0.1] - 2019-10-04
|
||||
## [1.0.1] – 2019-10-04
|
||||
### Fixed
|
||||
- Crash on macOS 10.14 due to a `CGColorRef` null pointer
|
||||
|
||||
|
||||
## [1.0.0] - 2019-10-03
|
||||
## [1.0.0] – 2019-10-03
|
||||
### Added
|
||||
- App Signing
|
||||
- Sandboxing & hardened runtime environment
|
||||
@@ -86,7 +102,7 @@ and this project does adhere to [Semantic Versioning](https://semver.org/spec/v2
|
||||
- *UI:* Mark unread articles with blue dot, instead of tick mark
|
||||
|
||||
|
||||
## [0.9.4] - 2019-04-02
|
||||
## [0.9.4] – 2019-04-02
|
||||
### Fixed
|
||||
- Article order got mixed up for some feeds (issue: #4)
|
||||
- If multiple consecutive items reappear in the middle of the feed mark them read
|
||||
@@ -95,7 +111,7 @@ and this project does adhere to [Semantic Versioning](https://semver.org/spec/v2
|
||||
- *UI:* Removed checkbox `Start on login`. Use Preferences > Users > Login Items instead.
|
||||
|
||||
|
||||
## [0.9.3] - 2019-03-14
|
||||
## [0.9.3] – 2019-03-14
|
||||
### Added
|
||||
- Changelog
|
||||
- *UI:* Show body tag in article tooltip if abstract tag is empty
|
||||
@@ -105,7 +121,7 @@ and this project does adhere to [Semantic Versioning](https://semver.org/spec/v2
|
||||
- Fixed update for feeds where all article URLs point to the same resource (issue: #3)
|
||||
|
||||
|
||||
## [0.9.2] - 2019-03-07
|
||||
## [0.9.2] – 2019-03-07
|
||||
### Added
|
||||
- Limit number of articles that are displayed in feed menu (issue: #2)
|
||||
|
||||
@@ -115,7 +131,7 @@ and this project does adhere to [Semantic Versioning](https://semver.org/spec/v2
|
||||
- libxml2 will ignore lower ascii characters (`0x00`–`0x1F`)
|
||||
|
||||
|
||||
## [0.9.1] - 2019-02-14
|
||||
## [0.9.1] – 2019-02-14
|
||||
### Added
|
||||
- Mark single article as un/read (hold down option key and click on article)
|
||||
|
||||
@@ -133,11 +149,14 @@ and this project does adhere to [Semantic Versioning](https://semver.org/spec/v2
|
||||
- Remove html tags from abstract on save (not on display)
|
||||
|
||||
|
||||
## [0.9] - 2019-02-11
|
||||
## [0.9] – 2019-02-11
|
||||
Initial release
|
||||
|
||||
|
||||
[Unreleased]: https://github.com/relikd/baRSS/compare/v1.1.0...HEAD
|
||||
[Unreleased]: https://github.com/relikd/baRSS/compare/v1.1.3...HEAD
|
||||
[1.1.3]: https://github.com/relikd/baRSS/compare/v1.1.2...v1.1.3
|
||||
[1.1.2]: https://github.com/relikd/baRSS/compare/v1.1.1...v1.1.2
|
||||
[1.1.1]: https://github.com/relikd/baRSS/compare/v1.1.0...v1.1.1
|
||||
[1.1.0]: https://github.com/relikd/baRSS/compare/v1.0.2...v1.1.0
|
||||
[1.0.2]: https://github.com/relikd/baRSS/compare/v1.0.1...v1.0.2
|
||||
[1.0.1]: https://github.com/relikd/baRSS/compare/v1.0.0...v1.0.1
|
||||
|
||||
@@ -108,7 +108,7 @@ ToDo
|
||||
The following list is not exhaustive but rather a collection of nice things that will be added eventually.
|
||||
I may postpone some until demand increases …
|
||||
|
||||
- [ ] Localizations
|
||||
- [ ] Localizations
|
||||
- [ ] Feed generator for websites without feeds
|
||||
- [ ] Automatically choose best update interval (e.g., avg)
|
||||
- [ ] Sync with online services
|
||||
@@ -180,7 +180,7 @@ This project uses a modified version of Brent Simmons' [RSXML](https://github.co
|
||||
##### Trivia
|
||||
|
||||
- Start of project: __July 19, 2018__
|
||||
- Estimated development time: __1953h+__
|
||||
- Estimated development time: __1965h+__
|
||||
- First prototype used __feedparser python__ library
|
||||
|
||||
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
54ACC27421061B3B0020715F /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0940;
|
||||
LastUpgradeCheck = 1200;
|
||||
ORGANIZATIONNAME = relikd;
|
||||
TargetAttributes = {
|
||||
54ACC27B21061B3B0020715F = {
|
||||
@@ -647,6 +647,7 @@
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
@@ -702,6 +703,7 @@
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1000"
|
||||
LastUpgradeVersion = "1200"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@@ -196,7 +196,8 @@
|
||||
if (self.articles.count == 0) {
|
||||
img = [NSImage imageNamed:NSImageNameCaution];
|
||||
} else if (self.hasIcon) {
|
||||
img = [[NSImage alloc] initByReferencingURL:[self iconPath]];
|
||||
NSData* data = [[NSData alloc] initWithContentsOfURL:[self iconPath]];
|
||||
img = [[NSImage alloc] initWithData:data];
|
||||
} else {
|
||||
img = [NSImage imageNamed:RSSImageDefaultRSSIcon];
|
||||
}
|
||||
|
||||
@@ -89,11 +89,12 @@
|
||||
- (void)setSortIndexIfChanged:(int32_t)sortIndex {
|
||||
if (self.sortIndex != sortIndex) {
|
||||
self.sortIndex = sortIndex;
|
||||
}
|
||||
// Otherwise move from 0.0 -> 0 will not trigger index path update
|
||||
[self iterateSorted:NO overDescendantFeeds:^(Feed *feed, BOOL *cancel) {
|
||||
[feed calculateAndSetIndexPathString];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
/// Set @c name attribute but only if value differs.
|
||||
- (void)setNameIfChanged:(nullable NSString*)name {
|
||||
|
||||
@@ -28,7 +28,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
// TODO: Make plugins extensible? community extensions.
|
||||
@interface YouTubePlugin : NSObject
|
||||
+ (NSString*)feedURL:(NSURL*)url;
|
||||
+ (NSString*)feedURL:(NSURL*)url data:(NSData*)html;
|
||||
+ (NSString*)videoImage:(NSString*)videoid;
|
||||
+ (NSString*)videoImageHQ:(NSString*)videoid;
|
||||
@end
|
||||
|
||||
@@ -33,12 +33,13 @@
|
||||
|
||||
@return @c nil if @c url is not properly formatted, YouTube feed URL otherwise.
|
||||
*/
|
||||
+ (NSString*)feedURL:(NSURL*)url {
|
||||
+ (NSString*)feedURL:(NSURL*)url data:(NSData*)html {
|
||||
if (![url.host hasSuffix:@"youtube.com"]) // 'youtu.be' & 'youtube-nocookie.com' will redirect
|
||||
return nil;
|
||||
// https://www.youtube.com/channel/[channel-id]
|
||||
// https://www.youtube.com/user/[user-name]
|
||||
// https://www.youtube.com/playlist?list=[playlist-id]
|
||||
// https://www.youtube.com/c/[channel-name]
|
||||
#if DEBUG && ENV_LOG_YOUTUBE
|
||||
printf("resolving YouTube url:\n");
|
||||
printf(" ↳ %s\n", url.absoluteString.UTF8String);
|
||||
@@ -62,6 +63,23 @@
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if ([type isEqualToString:@"c"]) {
|
||||
NSData *m_head = [@"<meta itemprop=\"channelId\" content=\"" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSRange tmp = [html rangeOfData:m_head options:0 range:NSMakeRange(0, html.length)];
|
||||
if (tmp.location == NSNotFound) {
|
||||
NSData *m_json = [@"\"channelId\":\"" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
tmp = [html rangeOfData:m_json options:0 range:NSMakeRange(0, html.length)];
|
||||
}
|
||||
NSUInteger start = tmp.location + tmp.length;
|
||||
NSUInteger end = html.length - start;
|
||||
if (end > 50) end = 50; // no need to search till the end
|
||||
NSString *substr = [[NSString alloc] initWithData:[html subdataWithRange:NSMakeRange(start, end)] encoding:NSUTF8StringEncoding];
|
||||
if (substr) {
|
||||
NSUInteger to = [substr rangeOfString:@"\""].location;
|
||||
if (to != NSNotFound) {
|
||||
found = [ytBase stringByAppendingFormat:@"?channel_id=%@", [substr substringToIndex:to]];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#if DEBUG && ENV_LOG_YOUTUBE
|
||||
|
||||
@@ -147,7 +147,13 @@
|
||||
return;
|
||||
self.currentDownload = [[NSURLRequest requestWithURL:self.remoteURL] downloadTask:^(NSURL * _Nullable path, NSError * _Nullable error) {
|
||||
if (error) path = nil; // will also nullify img
|
||||
NSImage *img = path ? [[NSImage alloc] initByReferencingURL:path] : nil;
|
||||
NSImage *img;
|
||||
if (path) {
|
||||
NSData* data = [[NSData alloc] initWithContentsOfURL:path];
|
||||
img = [[NSImage alloc] initWithData:data];
|
||||
} else {
|
||||
img = nil;
|
||||
}
|
||||
if (img.valid) {
|
||||
// move image to temporary destination, otherwise dataTask: will delete it.
|
||||
NSString *tmpFile = NSProcessInfo.processInfo.globallyUniqueString;
|
||||
@@ -166,7 +172,8 @@
|
||||
if (self.canceled)
|
||||
return;
|
||||
NSURL *path = self.fileURL;
|
||||
NSImage *img = [[NSImage alloc] initByReferencingURL:path];
|
||||
NSData* data = [[NSData alloc] initWithContentsOfURL:path];
|
||||
NSImage* img = [[NSImage alloc] initWithData:data];
|
||||
if (!img.valid) { path = nil; img = nil; }
|
||||
#if DEBUG && ENV_LOG_DOWNLOAD
|
||||
printf("ICON %1.0fx%1.0f %s\n", img.size.width, img.size.height, self.remoteURL.absoluteString.UTF8String);
|
||||
|
||||
@@ -178,7 +178,7 @@
|
||||
}
|
||||
else if (!meta || meta.feedLinks.count == 0) {
|
||||
if ([xml.url.host hasSuffix:@"youtube.com"])
|
||||
feedURL = [YouTubePlugin feedURL:xml.url];
|
||||
feedURL = [YouTubePlugin feedURL:xml.url data:xml.data];
|
||||
if (feedURL.length == 0)
|
||||
self.error = [NSError feedURLNotFound:xml.url];
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.1.0</string>
|
||||
<string>1.1.3</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
@@ -70,7 +70,7 @@
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>14603</string>
|
||||
<string>14644</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.news</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
@@ -83,7 +83,7 @@
|
||||
<true/>
|
||||
</dict>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2019 relikd. Public Domain.</string>
|
||||
<string>Copyright © 2020 relikd.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>AppHook</string>
|
||||
<key>UTImportedTypeDeclarations</key>
|
||||
|
||||
@@ -52,9 +52,9 @@
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
if (@available(macOS 10.14, *)) {
|
||||
color = UserPrefsColor(Pref_colorStatusIconTint, [NSColor controlAccentColor]);
|
||||
color = UserPrefsColor(Pref_colorUnreadIndicator, [NSColor controlAccentColor]);
|
||||
} else {
|
||||
color = UserPrefsColor(Pref_colorStatusIconTint, [NSColor systemBlueColor]);
|
||||
color = UserPrefsColor(Pref_colorUnreadIndicator, [NSColor systemBlueColor]);
|
||||
}
|
||||
});
|
||||
return color;
|
||||
|
||||
@@ -216,7 +216,13 @@
|
||||
*/
|
||||
- (void)faviconDownload:(FaviconDownload*)sender didFinish:(nullable NSURL*)path {
|
||||
// Create image from favicon temporary file location or default icon if no favicon exists.
|
||||
NSImage *img = path ? [[NSImage alloc] initByReferencingURL:path] : [NSImage imageNamed:RSSImageDefaultRSSIcon];
|
||||
NSImage *img;
|
||||
if (path) {
|
||||
NSData* data = [[NSData alloc] initWithContentsOfURL:path];
|
||||
img = [[NSImage alloc] initWithData:data];
|
||||
} else {
|
||||
img = [NSImage imageNamed:RSSImageDefaultRSSIcon];
|
||||
}
|
||||
self.view.favicon.image = img;
|
||||
self.faviconFile = path;
|
||||
[self downloadComplete];
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
[content setFrameSize: NSMakeSize(w, h)];
|
||||
|
||||
// after content size, increase to window size
|
||||
w += 2 * PAD_WIN;
|
||||
w += 2 * (NSInteger)PAD_WIN;
|
||||
h += PAD_WIN + contentOffsetY; // the second PAD_WIN is already in contentOffsetY
|
||||
|
||||
NSWindowStyleMask style = NSWindowStyleMaskTitled | NSWindowStyleMaskResizable | NSWindowStyleMaskFullSizeContentView;
|
||||
|
||||
@@ -121,7 +121,9 @@
|
||||
BOOL tint = (self.unreadCountTotal > 0 && hasNet && UserPrefsBool(Pref_globalTintMenuIcon));
|
||||
self.statusItem.button.image = [NSImage imageNamed:(hasNet ? RSSImageMenuBarIconActive : RSSImageMenuBarIconPaused)];
|
||||
|
||||
if (@available(macOS 10.14, *)) {
|
||||
if (@available(macOS 11, *)) {
|
||||
self.statusItem.button.image.template = !tint;
|
||||
} else if (@available(macOS 10.14, *)) {
|
||||
// There is no proper way to display tinted icon WITHOUT tinted text!
|
||||
// - using alternate image instead of tint:
|
||||
// icon & text stays black on highlight (but only in light mode)
|
||||
|
||||
Reference in New Issue
Block a user