Merge branch 'dejal'

* dejal: (43 commits)
  Load offline story images even when online to speed up image display.
  #1875 (borders between panes not using theme colors)
  #1247 (Mac Catalyst edition)
  #1874 (Crash on opening widget story)
  #1247 (Mac Catalyst edition)
  #1247 (Mac Catalyst edition)
  #1247 (Mac Catalyst edition)
  #1247 (Mac Catalyst edition)
  #1247 (Mac Catalyst edition)
  #1247 (Mac Catalyst edition)
  Bumped version number
  #1247 (Mac Catalyst edition)
  #1851 (mark as read button at bottom of story list)
  #1247 (Mac Catalyst edition)
  #1247 (Mac Catalyst edition)
  Added privacy manifest
  #1247 (Mac Catalyst edition)
  #1247 (Mac Catalyst edition)
  #1247 (Mac Catalyst edition)
  #1247 (Mac Catalyst edition)
  ...
This commit is contained in:
Samuel Clay 2024-08-08 17:44:29 -04:00
commit de448e5415
117 changed files with 4845 additions and 948 deletions

View file

@ -154,7 +154,7 @@
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger activitiesCount = [appDelegate.userActivitiesArray count];
int minimumHeight;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!appDelegate.isPhone) {
minimumHeight = MINIMUM_ACTIVITY_HEIGHT_IPAD;
} else {
minimumHeight = MINIMUM_ACTIVITY_HEIGHT_IPHONE;
@ -165,7 +165,7 @@
}
id activityCell;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!appDelegate.isPhone) {
activityCell = [[ActivityCell alloc] init];
} else {
activityCell = [[SmallActivityCell alloc] init];
@ -185,7 +185,7 @@
ActivityCell *cell = [tableView
dequeueReusableCellWithIdentifier:@"ActivityCell"];
if (cell == nil) {
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!appDelegate.isPhone) {
cell = [[ActivityCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:@"ActivityCell"];
@ -304,7 +304,7 @@
UIImage *img = [UIImage imageNamed:@"fleuron.png"];
UIImageView *fleuron = [[UIImageView alloc] initWithImage:img];
int height;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!appDelegate.isPhone) {
height = MINIMUM_ACTIVITY_HEIGHT_IPAD;
} else {
height = MINIMUM_ACTIVITY_HEIGHT_IPHONE;

View file

@ -7,15 +7,10 @@
//
#import <UIKit/UIKit.h>
#import "NewsBlurAppDelegate.h"
#import "NewsBlur-Swift.h"
@class NewsBlurAppDelegate;
@interface AddSiteViewController : BaseViewController
<UITextFieldDelegate, UITableViewDelegate, UITableViewDataSource> {
NewsBlurAppDelegate *appDelegate;
}
<UITextFieldDelegate, UITableViewDelegate, UITableViewDataSource>
- (void)reload;
- (IBAction)addSite;

View file

@ -8,7 +8,6 @@
#import "AddSiteViewController.h"
#import "AddSiteAutocompleteCell.h"
#import "NewsBlurAppDelegate.h"
#import "MenuViewController.h"
#import "SBJson4.h"
#import "NewsBlur-Swift.h"
@ -93,7 +92,7 @@
//- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// // Return YES for supported orientations
// if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
// if (!self.isPhone) {
// return YES;
// } else if (UIInterfaceOrientationIsPortrait(interfaceOrientation)) {
// return YES;
@ -130,7 +129,7 @@
}
- (IBAction)doCancelButton {
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!self.isPhone) {
[appDelegate hidePopover];
} else {
[appDelegate hidePopoverAnimated:YES];
@ -272,7 +271,7 @@
[self.errorLabel setText:[responseObject valueForKey:@"message"]];
[self.errorLabel setHidden:NO];
} else {
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!self.isPhone) {
[self->appDelegate hidePopover];
} else {
[self->appDelegate hidePopoverAnimated:YES];

View file

@ -53,7 +53,7 @@
[self.webView loadRequest:requestObj];
}];
if (self.fromStory && [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (self.fromStory && !appDelegate.isPhone) {
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc]
initWithTitle: @"Cancel"
style: UIBarButtonItemStylePlain
@ -75,6 +75,7 @@
}
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
BOOL isPhone = appDelegate.isPhone;
NSURLRequest *request = navigationAction.request;
NSString *URLString = [[request URL] absoluteString];
NSLog(@"URL STRING IS %@", URLString);
@ -86,7 +87,7 @@
if (self.fromStory) {
[self.appDelegate refreshUserProfile:^{
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!isPhone) {
[self.appDelegate.shareNavigationController viewWillAppear:YES];
[self.appDelegate.modalNavigationController dismissViewControllerAnimated:YES completion:nil];
} else {

View file

@ -0,0 +1,70 @@
//
// AuxSceneDelegate.swift
// NewsBlur
//
// Created by David Sinclair on 2024-05-30.
// Copyright © 2024 NewsBlur. All rights reserved.
//
import UIKit
/// Scene delegate for auxiliary windows. Currently only used on macOS.
class AuxSceneDelegate: UIResponder, UIWindowSceneDelegate {
let appDelegate: NewsBlurAppDelegate = .shared
var window: UIWindow?
#if targetEnvironment(macCatalyst)
var toolbar = NSToolbar(identifier: "aux")
var toolbarDelegate = ToolbarDelegate()
#endif
/// Open a new window with an `OriginalStoryViewController` for the given URL.
@objc(openWindowForURL:customTitle:) class func openWindow(for url: URL, customTitle: String) {
let activity = NSUserActivity(activityType: "aux")
activity.userInfo = ["url" : url, "title" : customTitle]
if #available(iOS 17.0, *) {
let request = UISceneSessionActivationRequest(userActivity: activity)
UIApplication.shared.activateSceneSession(for: request) { error in
print("Error activating scene: \(error)")
}
} else {
UIApplication.shared.requestSceneSessionActivation(nil, userActivity: activity, options: nil) { error in
print("Error activating scene: \(error)")
}
}
}
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
#if targetEnvironment(macCatalyst)
guard let windowScene = scene as? UIWindowScene, //let titlebar = windowScene.titlebar,
let userInfo = connectionOptions.userActivities.first?.userInfo else {
return
}
let url = userInfo["url"] as? URL
let title = userInfo["title"] as? String
let controller = OriginalStoryViewController()
windowScene.title = "Loading…"
window?.rootViewController = controller
appDelegate.activeOriginalStoryURL = url
controller.customPageTitle = title
_ = controller.view
controller.loadInitialStory()
//TODO: 🚧 perhaps make a toolbar for this window
// toolbar.delegate = toolbarDelegate
// toolbar.displayMode = .iconOnly
//
// titlebar.toolbar = toolbar
// titlebar.toolbarStyle = .automatic
#endif
}
}

View file

@ -1,9 +1,23 @@
#import <UIKit/UIKit.h>
#import "MBProgressHUD.h"
@class NewsBlurAppDelegate;
@interface BaseViewController : UIViewController {
NewsBlurAppDelegate *appDelegate;
}
@property (nonatomic) IBOutlet NewsBlurAppDelegate *appDelegate;
@property (nonatomic, readonly) BOOL isPhone;
@property (nonatomic, readonly) BOOL isMac;
@property (nonatomic, readonly) BOOL isVision;
@property (nonatomic, readonly) BOOL isPortrait;
@property (nonatomic, readonly) BOOL isCompactWidth;
@property (nonatomic, readonly) BOOL isGrid;
@property (nonatomic, readonly) BOOL isFeedShown;
@property (nonatomic, readonly) BOOL isStoryShown;
- (void)informError:(id)error;
- (void)informError:(id)error statusCode:(NSInteger)statusCode;
- (void)informMessage:(NSString *)message;
@ -13,6 +27,7 @@
- (void)addKeyCommandWithInput:(NSString *)input modifierFlags:(UIKeyModifierFlags)modifierFlags action:(SEL)action discoverabilityTitle:(NSString *)discoverabilityTitle wantPriority:(BOOL)wantPriority;
- (void)addCancelKeyCommandWithAction:(SEL)action discoverabilityTitle:(NSString *)discoverabilityTitle;
- (void)systemAppearanceDidChange:(BOOL)isDark;
- (void)updateTheme;
- (void)tableView:(UITableView *)tableView redisplayCellAtIndexPath:(NSIndexPath *)indexPath;
@ -25,5 +40,55 @@
- (void)collectionView:(UICollectionView *)collectionView selectItemAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UICollectionViewScrollPosition)scrollPosition;
- (void)collectionView:(UICollectionView *)collectionView deselectItemAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated;
- (IBAction)newSite:(id)sender;
- (IBAction)reloadFeeds:(id)sender;
- (IBAction)showMuteSites:(id)sender;
- (IBAction)showOrganizeSites:(id)sender;
- (IBAction)showWidgetSites:(id)sender;
- (IBAction)showNotifications:(id)sender;
- (IBAction)showFindFriends:(id)sender;
- (IBAction)showPremium:(id)sender;
- (IBAction)showSupportForum:(id)sender;
- (IBAction)showLogout:(id)sender;
- (IBAction)findInFeeds:(id)sender;
- (IBAction)findInFeedDetail:(id)sender;
- (IBAction)chooseColumns:(id)sender;
- (IBAction)chooseLayout:(id)sender;
- (IBAction)chooseTitle:(id)sender;
- (IBAction)choosePreview:(id)sender;
- (IBAction)chooseGridColumns:(id)sender;
- (IBAction)chooseGridHeight:(id)sender;
- (IBAction)chooseFontSize:(id)sender;
- (IBAction)chooseSpacing:(id)sender;
- (IBAction)chooseTheme:(id)sender;
- (IBAction)moveSite:(id)sender;
- (IBAction)openRenameSite:(id)sender;
- (IBAction)muteSite:(id)sender;
- (IBAction)deleteSite:(id)sender;
- (IBAction)openTrainSite:(id)sender;
- (IBAction)openNotifications:(id)sender;
- (IBAction)openStatistics:(id)sender;
- (IBAction)instaFetchFeed:(id)sender;
- (IBAction)doMarkAllRead:(id)sender;
- (IBAction)openMarkReadMenu:(id)sender;
- (IBAction)openSettingsMenu:(id)sender;
- (IBAction)nextSite:(id)sender;
- (IBAction)previousSite:(id)sender;
- (IBAction)nextFolder:(id)sender;
- (IBAction)previousFolder:(id)sender;
- (IBAction)openAllStories:(id)sender;
- (IBAction)showSendTo:(id)sender;
- (IBAction)showTrain:(id)sender;
- (IBAction)showShare:(id)sender;
- (IBAction)nextUnreadStory:(id)sender;
- (IBAction)nextStory:(id)sender;
- (IBAction)previousStory:(id)sender;
- (IBAction)toggleTextStory:(id)sender;
- (IBAction)openInBrowser:(id)sender;
@end

View file

@ -4,17 +4,33 @@
@implementation BaseViewController
@synthesize appDelegate;
#pragma mark -
#pragma mark HTTP requests
- (instancetype)init {
if (self = [super init]) {
self.appDelegate = [NewsBlurAppDelegate sharedAppDelegate];
}
return self;
}
- (void)awakeFromNib {
[super awakeFromNib];
self.appDelegate = [NewsBlurAppDelegate sharedAppDelegate];
}
- (BOOL)becomeFirstResponder {
BOOL success = [super becomeFirstResponder];
NSLog(@"%@ becomeFirstResponder: %@", self, success ? @"yes" : @"no"); // log
return success;
}
#pragma mark -
#pragma mark View methods
@ -37,7 +53,7 @@
return [self informError:@"The server barfed!"];
} else {
errorMessage = [error localizedDescription];
if ([error code] == 4 &&
if ([error code] == 4 &&
[errorMessage rangeOfString:@"cancelled"].location != NSNotFound) {
return;
}
@ -45,8 +61,8 @@
[MBProgressHUD hideHUDForView:self.view animated:YES];
MBProgressHUD *HUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
[HUD setCustomView:[[UIImageView alloc]
initWithImage:[UIImage imageNamed:@"warning.gif"]]];
[HUD setCustomView:[[UIImageView alloc]
initWithImage:[UIImage imageNamed:@"warning.gif"]]];
[HUD setMode:MBProgressHUDModeCustomView];
if (details) {
[HUD setDetailsLabelText:details];
@ -54,19 +70,19 @@
HUD.labelText = errorMessage;
[HUD hide:YES afterDelay:(details ? 3 : 1)];
// UIAlertView* alertView = [[UIAlertView alloc]
// initWithTitle:@"Error"
// message:localizedDescription delegate:nil
// cancelButtonTitle:@"OK"
// otherButtonTitles:nil];
// [alertView show];
// [alertView release];
// UIAlertView* alertView = [[UIAlertView alloc]
// initWithTitle:@"Error"
// message:localizedDescription delegate:nil
// cancelButtonTitle:@"OK"
// otherButtonTitles:nil];
// [alertView show];
// [alertView release];
}
- (void)informMessage:(NSString *)message {
[MBProgressHUD hideHUDForView:self.view animated:YES];
MBProgressHUD *HUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
HUD.mode = MBProgressHUDModeText;
HUD.mode = MBProgressHUDModeText;
HUD.labelText = message;
[HUD hide:YES afterDelay:.75];
}
@ -78,8 +94,14 @@
[HUD hide:YES afterDelay:2];
}
- (void)systemAppearanceDidChange:(BOOL)isDark {
[[ThemeManager themeManager] systemAppearanceDidChange:isDark];
}
- (void)updateTheme {
// Subclasses should override this, calling super, to update their nav bar, table, etc
appDelegate.splitViewController.view.backgroundColor = UIColorFromLightDarkRGB(0x555555, 0x777777);
}
- (void)tableView:(UITableView *)tableView redisplayCellAtIndexPath:(NSIndexPath *)indexPath {
@ -144,7 +166,7 @@
#pragma mark UIViewController
- (void) viewDidLoad {
[super viewDidLoad];
[super viewDidLoad];
BOOL isDark = [NewsBlurAppDelegate sharedAppDelegate].window.windowScene.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark;
@ -166,7 +188,7 @@
BOOL isDark = [NewsBlurAppDelegate sharedAppDelegate].window.windowScene.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark;
[[ThemeManager themeManager] systemAppearanceDidChange:isDark];
[self systemAppearanceDidChange:isDark];
}
- (UIStatusBarStyle)preferredStatusBarStyle {
@ -177,4 +199,463 @@
return UIStatusBarStyleLightContent;
}
- (BOOL)isPhone {
return [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone;
}
- (BOOL)isMac {
return [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomMac;
}
- (BOOL)isVision {
if (@available(iOS 17.0, *)) {
return [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomVision;
} else {
return NO;
}
}
- (BOOL)isPortrait {
UIWindow *window = [NewsBlurAppDelegate sharedAppDelegate].window;
UIInterfaceOrientation orientation = window.windowScene.interfaceOrientation;
if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown) {
return YES;
} else {
return NO;
}
}
- (BOOL)isCompactWidth {
UIWindow *window = [NewsBlurAppDelegate sharedAppDelegate].window;
UITraitCollection *traits = window.windowScene.traitCollection;
return traits.horizontalSizeClass == UIUserInterfaceSizeClassCompact;
//return self.compactWidth > 0.0;
}
- (BOOL)isGrid {
return self.appDelegate.detailViewController.storyTitlesInGrid;
}
- (BOOL)isFeedShown {
return appDelegate.storiesCollection.activeFeed != nil || appDelegate.storiesCollection.activeFolder != nil;
}
- (BOOL)isStoryShown {
return !appDelegate.storyPagesViewController.currentPage.view.isHidden && appDelegate.storyPagesViewController.currentPage.noStoryMessage.isHidden;
}
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
if (action == @selector(chooseLayout:) || action == @selector(findInFeedDetail:)) {
return self.isFeedShown;
} else if (action == @selector(chooseTitle:) || action == @selector(choosePreview:)) {
return self.isFeedShown && !self.isGrid;
} else if (action == @selector(chooseGridColumns:) || action == @selector(chooseGridHeight:)) {
return self.isFeedShown && self.isGrid;
} else if (action == @selector(openTrainSite) ||
action == @selector(openTrainSite:) ||
action == @selector(openNotifications:) ||
action == @selector(openStatistics:) ||
action == @selector(moveSite:) ||
action == @selector(openRenameSite:) ||
action == @selector(deleteSite:)) {
return self.isFeedShown && appDelegate.storiesCollection.isCustomFolderOrFeed;
} else if (action == @selector(muteSite) ||
action == @selector(muteSite:)) {
return self.isFeedShown && !appDelegate.storiesCollection.isRiverView;
} else if (action == @selector(instaFetchFeed:) ||
action == @selector(doMarkAllRead:)) {
return self.isFeedShown;
} else if (action == @selector(showSendTo:) ||
action == @selector(showTrain:) ||
action == @selector(showShare:) ||
action == @selector(nextUnreadStory:) ||
action == @selector(nextStory:) ||
action == @selector(previousStory:) ||
action == @selector(toggleTextStory:) ||
action == @selector(openInBrowser:)) {
return self.isStoryShown;
} else {
return [super canPerformAction:action withSender:sender];
}
}
- (void)validateCommand:(UICommand *)command {
[super validateCommand:command];
if (command.action == @selector(chooseColumns:)) {
command.state = [command.propertyList isEqualToString:appDelegate.detailViewController.behaviorString];
} else if (command.action == @selector(chooseLayout:)) {
NSString *value = self.appDelegate.storiesCollection.activeStoryTitlesPosition;
command.state = [command.propertyList isEqualToString:value];
} else if (command.action == @selector(chooseIntelligence:)) {
NSInteger intelligence = [[NSUserDefaults standardUserDefaults] integerForKey:@"selectedIntelligence"];
NSString *value = [NSString stringWithFormat:@"%@", @(intelligence + 1)];
command.state = [command.propertyList isEqualToString:value];
} else if (command.action == @selector(toggleSidebar:)) {
UISplitViewController *splitViewController = self.appDelegate.splitViewController;
if (splitViewController.preferredDisplayMode != UISplitViewControllerDisplayModeTwoBesideSecondary) {
command.title = @"Show Sidebar";
} else {
command.title = @"Hide Sidebar";
}
} else if (command.action == @selector(chooseTitle:)) {
NSString *value = [[NSUserDefaults standardUserDefaults] objectForKey:@"story_list_preview_text_size"];
command.state = [command.propertyList isEqualToString:value];
} else if (command.action == @selector(choosePreview:)) {
NSString *value = [[NSUserDefaults standardUserDefaults] objectForKey:@"story_list_preview_images_size"];
command.state = [command.propertyList isEqualToString:value];
} else if (command.action == @selector(chooseGridColumns:)) {
NSString *value = [[NSUserDefaults standardUserDefaults] objectForKey:@"grid_columns"];
if (value == nil) {
value = @"auto";
}
command.state = [command.propertyList isEqualToString:value];
} else if (command.action == @selector(chooseGridHeight:)) {
NSString *value = [[NSUserDefaults standardUserDefaults] objectForKey:@"grid_height"];
if (value == nil) {
value = @"medium";
}
command.state = [command.propertyList isEqualToString:value];
} else if (command.action == @selector(chooseFontSize:)) {
NSString *value = [[NSUserDefaults standardUserDefaults] objectForKey:@"feed_list_font_size"];
command.state = [command.propertyList isEqualToString:value];
} else if (command.action == @selector(chooseSpacing:)) {
NSString *value = [[NSUserDefaults standardUserDefaults] objectForKey:@"feed_list_spacing"];
command.state = [command.propertyList isEqualToString:value];
} else if (command.action == @selector(chooseTheme:)) {
command.state = [command.propertyList isEqualToString:ThemeManager.themeManager.theme];
} else if (command.action == @selector(openRenameSite:)) {
if (appDelegate.storiesCollection.isRiverOrSocial) {
command.title = @"Rename Folder…";
} else {
command.title = @"Rename Site…";
}
} else if (command.action == @selector(deleteSite:)) {
if (appDelegate.storiesCollection.isRiverOrSocial) {
command.title = @"Delete Folder…";
} else {
command.title = @"Delete Site…";
}
} else if (command.action == @selector(toggleStorySaved:)) {
BOOL isRead = [[self.appDelegate.activeStory objectForKey:@"starred"] boolValue];
if (isRead) {
command.title = @"Unsave This Story";
} else {
command.title = @"Save This Story";
}
} else if (command.action == @selector(toggleStoryUnread:)) {
BOOL isRead = [[self.appDelegate.activeStory objectForKey:@"read_status"] boolValue];
if (isRead) {
command.title = @"Mark as Unread";
} else {
command.title = @"Mark as Read";
}
}
}
#pragma mark -
#pragma mark File menu
- (IBAction)newSite:(id)sender {
[appDelegate.feedsViewController tapAddSite:nil];
}
- (IBAction)reloadFeeds:(id)sender {
[appDelegate reloadFeedsView:NO];
}
- (IBAction)showMuteSites:(id)sender {
[self.appDelegate showMuteSites];
}
- (IBAction)showOrganizeSites:(id)sender {
[self.appDelegate showOrganizeSites];
}
- (IBAction)showWidgetSites:(id)sender {
[self.appDelegate showWidgetSites];
}
- (IBAction)showNotifications:(id)sender {
[self.appDelegate openNotificationsWithFeed:nil];
}
- (IBAction)showFindFriends:(id)sender {
[self.appDelegate showFindFriends];
}
- (IBAction)showPremium:(id)sender {
[self.appDelegate showPremiumDialog];
}
- (IBAction)showSupportForum:(id)sender {
NSURL *url = [NSURL URLWithString:@"https://forum.newsblur.com"];
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
}
- (IBAction)showLogout:(id)sender {
[self.appDelegate confirmLogout];
}
#pragma mark -
#pragma mark Edit menu
- (IBAction)findInFeeds:(id)sender {
[self.appDelegate showColumn:UISplitViewControllerColumnPrimary debugInfo:@"findInFeeds"];
[self.appDelegate.feedsViewController.searchBar becomeFirstResponder];
}
- (IBAction)findInFeedDetail:(id)sender {
[self.appDelegate showColumn:UISplitViewControllerColumnSupplementary debugInfo:@"findInFeedDetail"];
[self.appDelegate.feedDetailViewController.searchBar becomeFirstResponder];
}
#pragma mark -
#pragma mark View menu
- (IBAction)chooseColumns:(id)sender {
UICommand *command = sender;
NSString *string = command.propertyList;
[[NSUserDefaults standardUserDefaults] setObject:string forKey:@"split_behavior"];
[UIView animateWithDuration:0.5 animations:^{
[self.appDelegate updateSplitBehavior:YES];
}];
[self.appDelegate.detailViewController updateLayoutWithReload:NO fetchFeeds:YES];
}
- (IBAction)chooseLayout:(id)sender {
UICommand *command = sender;
NSString *string = command.propertyList;
NSString *key = self.appDelegate.storiesCollection.storyTitlesPositionKey;
[[NSUserDefaults standardUserDefaults] setObject:string forKey:key];
[self.appDelegate.detailViewController updateLayoutWithReload:YES fetchFeeds:YES];
}
- (IBAction)chooseIntelligence:(id)sender {
UICommand *command = sender;
NSInteger index = [command.propertyList integerValue];
[self.appDelegate.feedsViewController.intelligenceControl setSelectedSegmentIndex:index];
[self.appDelegate.feedsViewController selectIntelligence];
}
- (IBAction)chooseTitle:(id)sender {
UICommand *command = sender;
NSString *string = command.propertyList;
[[NSUserDefaults standardUserDefaults] setObject:string forKey:@"story_list_preview_text_size"];
[self.appDelegate resizePreviewSize];
}
- (IBAction)choosePreview:(id)sender {
UICommand *command = sender;
NSString *string = command.propertyList;
[[NSUserDefaults standardUserDefaults] setObject:string forKey:@"story_list_preview_images_size"];
[self.appDelegate resizePreviewSize];
}
- (IBAction)chooseGridColumns:(id)sender {
UICommand *command = sender;
NSString *string = command.propertyList;
[[NSUserDefaults standardUserDefaults] setObject:string forKey:@"grid_columns"];
[self.appDelegate.detailViewController updateLayoutWithReload:YES fetchFeeds:YES];
}
- (IBAction)chooseGridHeight:(id)sender {
UICommand *command = sender;
NSString *string = command.propertyList;
[[NSUserDefaults standardUserDefaults] setObject:string forKey:@"grid_height"];
[self.appDelegate.detailViewController updateLayoutWithReload:YES fetchFeeds:YES];
}
- (IBAction)chooseFontSize:(id)sender {
UICommand *command = sender;
NSString *string = command.propertyList;
[[NSUserDefaults standardUserDefaults] setObject:string forKey:@"feed_list_font_size"];
[self.appDelegate resizeFontSize];
}
- (IBAction)chooseSpacing:(id)sender {
UICommand *command = sender;
NSString *string = command.propertyList;
[[NSUserDefaults standardUserDefaults] setObject:string forKey:@"feed_list_spacing"];
[self.appDelegate.feedsViewController reloadFeedTitlesTable];
[self.appDelegate.feedDetailViewController reloadWithSizing];
}
- (IBAction)chooseTheme:(id)sender {
UICommand *command = sender;
NSString *string = command.propertyList;
[ThemeManager themeManager].theme = string;
}
- (IBAction)toggleSidebar:(id)sender{
UISplitViewController *splitViewController = self.appDelegate.splitViewController;
[UIView animateWithDuration:0.2 animations:^{
NSLog(@"toggleSidebar: displayMode: %@; preferredDisplayMode: %@; UISplitViewControllerDisplayModeSecondaryOnly: %@; UISplitViewControllerDisplayModeTwoBesideSecondary: %@, UISplitViewControllerDisplayModeOneBesideSecondary: %@; ", @(splitViewController.displayMode), @(splitViewController.preferredDisplayMode), @(UISplitViewControllerDisplayModeSecondaryOnly), @(UISplitViewControllerDisplayModeTwoBesideSecondary), @(UISplitViewControllerDisplayModeOneBesideSecondary)); // log
if (splitViewController.splitBehavior == UISplitViewControllerSplitBehaviorOverlay) {
splitViewController.preferredDisplayMode = (splitViewController.displayMode != UISplitViewControllerDisplayModeTwoOverSecondary ? UISplitViewControllerDisplayModeTwoOverSecondary : UISplitViewControllerDisplayModeOneOverSecondary);
} else if (splitViewController.splitBehavior == UISplitViewControllerSplitBehaviorDisplace) {
if (splitViewController.preferredDisplayMode == UISplitViewControllerDisplayModeTwoDisplaceSecondary &&
splitViewController.displayMode == UISplitViewControllerDisplayModeSecondaryOnly) {
splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModeOneBesideSecondary;
dispatch_async(dispatch_get_main_queue(), ^(void) {
splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModeTwoDisplaceSecondary;
});
} else {
splitViewController.preferredDisplayMode = (splitViewController.displayMode != UISplitViewControllerDisplayModeTwoDisplaceSecondary ? UISplitViewControllerDisplayModeTwoDisplaceSecondary : UISplitViewControllerDisplayModeOneBesideSecondary);
}
} else {
if (splitViewController.preferredDisplayMode == UISplitViewControllerDisplayModeTwoBesideSecondary &&
splitViewController.displayMode == UISplitViewControllerDisplayModeSecondaryOnly) {
splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModeOneBesideSecondary;
dispatch_async(dispatch_get_main_queue(), ^(void) {
splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModeTwoBesideSecondary;
});
} else {
splitViewController.preferredDisplayMode = (splitViewController.displayMode != UISplitViewControllerDisplayModeTwoBesideSecondary ? UISplitViewControllerDisplayModeTwoBesideSecondary : UISplitViewControllerDisplayModeOneBesideSecondary);
}
}
}];
}
#pragma mark -
#pragma mark Site menu
- (IBAction)moveSite:(id)sender {
[self.appDelegate.feedDetailViewController openMoveView:self.appDelegate.navigationController];
}
- (IBAction)openRenameSite:(id)sender {
[self.appDelegate.feedDetailViewController openRenameSite];
}
- (IBAction)muteSite:(id)sender {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"Are you sure you wish to mute %@?", self.appDelegate.storiesCollection.activeTitle] message:nil preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:[UIAlertAction actionWithTitle: @"Mute Site" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * action) {
[alertController dismissViewControllerAnimated:YES completion:nil];
[self.appDelegate.feedDetailViewController muteSite];
}]];
[alertController addAction:[UIAlertAction actionWithTitle:@"Cancel"
style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:alertController animated:YES completion:nil];
}
- (IBAction)deleteSite:(id)sender {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"Are you sure you wish to delete %@?", self.appDelegate.storiesCollection.activeTitle] message:nil preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:[UIAlertAction actionWithTitle: @"Delete Site" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * action) {
[alertController dismissViewControllerAnimated:YES completion:nil];
[self.appDelegate.feedDetailViewController deleteSite];
}]];
[alertController addAction:[UIAlertAction actionWithTitle:@"Cancel"
style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:alertController animated:YES completion:nil];
}
- (IBAction)openTrainSite:(id)sender {
[self.appDelegate.feedDetailViewController openTrainSite];
}
- (IBAction)openNotifications:(id)sender {
[self.appDelegate.feedDetailViewController openNotifications:sender];
}
- (IBAction)openStatistics:(id)sender {
[self.appDelegate.feedDetailViewController openStatistics:sender];
}
- (IBAction)instaFetchFeed:(id)sender {
[self.appDelegate.feedDetailViewController instafetchFeed];
}
- (IBAction)doMarkAllRead:(id)sender {
[self.appDelegate.feedDetailViewController doMarkAllRead:sender];
}
// These two are needed for the toolbar in Grid view.
- (IBAction)openMarkReadMenu:(id)sender {
[self.appDelegate.feedDetailViewController doOpenMarkReadMenu:sender];
}
- (IBAction)openSettingsMenu:(id)sender {
[self.appDelegate.feedDetailViewController doOpenSettingsMenu:sender];
}
- (IBAction)nextSite:(id)sender {
[self.appDelegate.feedsViewController selectNextFeed:sender];
}
- (IBAction)previousSite:(id)sender {
[self.appDelegate.feedsViewController selectPreviousFeed:sender];
}
- (IBAction)nextFolder:(id)sender {
[self.appDelegate.feedsViewController selectNextFolder:sender];
}
- (IBAction)previousFolder:(id)sender {
[self.appDelegate.feedsViewController selectPreviousFolder:sender];
}
- (IBAction)openAllStories:(id)sender {
[self.appDelegate.feedsViewController selectEverything:sender];
}
#pragma mark -
#pragma mark Story menu
- (IBAction)showSendTo:(id)sender {
[appDelegate showSendTo:self sender:sender];
}
- (IBAction)showTrain:(id)sender {
[self.appDelegate openTrainStory:self.appDelegate.storyPagesViewController.fontSettingsButton];
}
- (IBAction)showShare:(id)sender {
[self.appDelegate.storyPagesViewController.currentPage openShareDialog];
}
- (IBAction)nextUnreadStory:(id)sender {
[self.appDelegate.storyPagesViewController doNextUnreadStory:sender];
}
- (IBAction)nextStory:(id)sender {
[self.appDelegate.storyPagesViewController changeToNextPage:sender];
}
- (IBAction)previousStory:(id)sender {
[self.appDelegate.storyPagesViewController changeToPreviousPage:sender];
}
- (IBAction)toggleTextStory:(id)sender {
[self.appDelegate.storyPagesViewController toggleTextView:sender];
}
- (IBAction)openInBrowser:(id)sender {
[self.appDelegate.storyPagesViewController showOriginalSubview:sender];
}
@end

View file

@ -10,11 +10,6 @@ import UIKit
/// Manages the detail column of the split view, with the feed detail and/or the story pages.
class DetailViewController: BaseViewController {
/// Returns the shared app delegate.
var appDelegate: NewsBlurAppDelegate {
return NewsBlurAppDelegate.shared()
}
/// Preference keys.
enum Key {
/// Style of the feed detail list layout.
@ -28,6 +23,9 @@ class DetailViewController: BaseViewController {
/// Position of the divider between the views when in vertical orientation. Only used for `.top` and `.bottom` layouts.
static let verticalPosition = "story_titles_divider_vertical"
/// Width of the feeds view, i.e. the primary split column.
static let feedsWidth = "split_primary_width"
}
/// Preference values.
@ -173,7 +171,7 @@ class DetailViewController: BaseViewController {
/// How the split controller behaves.
var behavior: Behavior {
switch UserDefaults.standard.string(forKey: Key.behavior) {
switch behaviorString {
case BehaviorValue.tile:
return .tile
case BehaviorValue.displace:
@ -185,20 +183,15 @@ class DetailViewController: BaseViewController {
}
}
/// Returns `true` if the device is an iPhone, otherwise `false`.
@objc var isPhone: Bool {
return UIDevice.current.userInterfaceIdiom == .phone
}
/// Returns `true` if the window is in portrait orientation, otherwise `false`.
@objc var isPortraitOrientation: Bool {
return view.window?.windowScene?.interfaceOrientation.isPortrait ?? false
/// The split controller behavior as a raw string.
@objc var behaviorString: String {
return UserDefaults.standard.string(forKey: Key.behavior) ?? BehaviorValue.auto
}
/// Position of the divider between the views.
var dividerPosition: CGFloat {
get {
let key = isPortraitOrientation ? Key.verticalPosition : Key.horizontalPosition
let key = isPortrait ? Key.verticalPosition : Key.horizontalPosition
let value = CGFloat(UserDefaults.standard.float(forKey: key))
if value == 0 {
@ -212,12 +205,32 @@ class DetailViewController: BaseViewController {
return
}
let key = isPortraitOrientation ? Key.verticalPosition : Key.horizontalPosition
let key = isPortrait ? Key.verticalPosition : Key.horizontalPosition
UserDefaults.standard.set(Float(newValue), forKey: key)
}
}
/// Width of the feeds view, i.e. the primary split column.
var feedsWidth: CGFloat {
get {
let value = CGFloat(UserDefaults.standard.float(forKey: Key.feedsWidth))
if value == 0 {
return 320
} else {
return value
}
}
set {
guard newValue != feedsWidth else {
return
}
UserDefaults.standard.set(Float(newValue), forKey: Key.feedsWidth)
}
}
/// Top container view.
@IBOutlet weak var topContainerView: UIView!
@ -395,6 +408,16 @@ class DetailViewController: BaseViewController {
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let width = splitViewController?.primaryColumnWidth ?? 320
if width != feedsWidth {
feedsWidth = width
}
}
private func adjustTopConstraint() {
guard let scene = view.window?.windowScene else {
return
@ -453,6 +476,13 @@ private extension DetailViewController {
func checkViewControllers() {
let isTop = layout == .top
#if targetEnvironment(macCatalyst)
splitViewController?.primaryBackgroundStyle = .sidebar
splitViewController?.minimumPrimaryColumnWidth = 250
splitViewController?.maximumPrimaryColumnWidth = 700
splitViewController?.preferredPrimaryColumnWidth = feedsWidth
#endif
if layout != .grid || isPhone {
storyPagesViewController = listStoryPagesViewController
_ = storyPagesViewController?.view

View file

@ -0,0 +1,212 @@
//
// Feed.swift
// NewsBlur
//
// Created by David Sinclair on 2024-04-04.
// Copyright © 2024 NewsBlur. All rights reserved.
//
import Foundation
// The Feed, Story, and StoryCache classes could be quite useful going forward; Rather than calling getStory() to get the dictionary, could have a variation that returns a Story instance. Could fetch from the cache if available, or make and cache one from the dictionary. Would need to remove it from the cache when changing anything about a story. Could perhaps make the cache part of StoriesCollection.
/// A dictionary with the most broad key and value types, common in ObjC code.
typealias AnyDictionary = [AnyHashable : Any]
/// A feed, wrapping the dictionary representation.
class Feed: Identifiable {
let id: String
var name = "<deleted>"
var subscribers = 0
var dictionary = AnyDictionary()
var isRiverOrSocial = false
var colorBarLeft: UIColor?
var colorBarRight: UIColor?
lazy var image: UIImage? = {
guard let appDelegate = NewsBlurAppDelegate.shared else {
return nil
}
if let image = appDelegate.getFavicon(id) {
return Utilities.roundCorneredImage(image, radius: 4, convertTo: CGSizeMake(16, 16))
} else {
return nil
}
}()
var classifiers: AnyDictionary? {
guard let appDelegate = NewsBlurAppDelegate.shared else {
return nil
}
return appDelegate.storiesCollection.activeClassifiers[id] as? AnyDictionary
}
func classifiers(for kind: String) -> AnyDictionary? {
return classifiers?[kind] as? AnyDictionary
}
enum Score: Int {
case none = 0
case like = 1
case dislike = -1
var imageName: String {
switch self {
case .none:
return "hand.thumbsup"
case .like:
return "hand.thumbsup.fill"
case .dislike:
return "hand.thumbsdown.fill"
}
}
}
struct Training: Identifiable {
let name: String
let count: Int
let score: Score
var id: String {
return name
}
}
lazy var titles: [Training] = {
guard let appDelegate = NewsBlurAppDelegate.shared,
let classifierTitles = self.classifiers(for: "titles") else {
return []
}
let userTitles = classifierTitles.map { Training(name: $0.key as! String, count: 0, score: Score(rawValue: $0.value as? Int ?? 0) ?? .none) }
return userTitles.sorted()
}()
lazy var authors: [Training] = {
guard let appDelegate = NewsBlurAppDelegate.shared,
let classifierAuthors = self.classifiers(for: "authors"),
let activeAuthors = appDelegate.storiesCollection.activePopularAuthors as? [[AnyHashable]] else {
return []
}
var userAuthors = [Training]()
for (someName, someScore) in classifierAuthors {
if let name = someName as? String, let score = someScore as? Int, !activeAuthors.contains(where: { $0[0] == someName }) {
userAuthors.append(Training(name: name, count: 0, score: Score(rawValue: score) ?? .none))
}
}
let otherAuthors: [Training] = activeAuthors.map { Training(name: $0[0] as! String, count: $0[1] as! Int, score: Score(rawValue: classifierAuthors[$0[0] as! String] as? Int ?? 0) ?? .none) }
return userAuthors.sorted() + otherAuthors
}()
lazy var tags: [Training] = {
guard let appDelegate = NewsBlurAppDelegate.shared,
let classifierTags = self.classifiers(for: "tags"),
let activeTags = appDelegate.storiesCollection.activePopularTags as? [[AnyHashable]] else {
return []
}
var userTags = [Training]()
for (someName, someScore) in classifierTags {
if let name = someName as? String, let score = someScore as? Int, !activeTags.contains(where: { $0[0] == someName }) {
userTags.append(Training(name: name, count: 0, score: Score(rawValue: score) ?? .none))
}
}
let otherTags: [Training] = activeTags.map { Training(name: $0[0] as! String, count: $0[1] as! Int, score: Score(rawValue: classifierTags[$0[0] as! String] as? Int ?? 0) ?? .none) }
return userTags.sorted() + otherTags
}()
init(id: String) {
self.id = id
guard let appDelegate = NewsBlurAppDelegate.shared else {
return
}
var feed: [String : Any]? = appDelegate.dictActiveFeeds[id] as? [String : Any]
if feed == nil {
feed = appDelegate.dictFeeds[id] as? [String : Any]
}
guard let feed else {
return
}
dictionary = feed
load()
}
init(dictionary: AnyDictionary) {
id = "\(dictionary["id"] ?? "<invalid>")"
self.dictionary = dictionary
load()
}
private func load() {
guard let appDelegate = NewsBlurAppDelegate.shared, let storiesCollection = appDelegate.storiesCollection else {
return
}
name = dictionary["feed_title"] as? String ?? "<invalid>"
subscribers = dictionary["num_subscribers"] as? Int ?? 0
colorBarLeft = color(for: "favicon_fade", from: dictionary, default: "707070")
colorBarRight = color(for: "favicon_color", from: dictionary, default: "505050")
isRiverOrSocial = storiesCollection.isRiverOrSocial
}
func color(for key: String, from feed: AnyDictionary, default defaultHex: String) -> UIColor {
let hex = feed[key] as? String ?? defaultHex
let scanner = Scanner(string: hex)
var color: Int64 = 0
scanner.scanHexInt64(&color)
let value = Int(color)
return ThemeManager.shared.fixedColor(fromRGB: value) ?? UIColor.gray
}
}
extension Feed: Equatable {
static func == (lhs: Feed, rhs: Feed) -> Bool {
return lhs.id == rhs.id
}
}
extension Feed: CustomDebugStringConvertible {
var debugDescription: String {
return "Feed \"\(name)\" (\(id))"
}
}
extension Feed.Training: Hashable {
static func == (lhs: Feed.Training, rhs: Feed.Training) -> Bool {
return lhs.name == rhs.name
}
func hash(into hasher: inout Hasher) {
hasher.combine(name)
}
}
extension Feed.Training: Comparable {
static func < (lhs: Feed.Training, rhs: Feed.Training) -> Bool {
return lhs.name < rhs.name
}
}

View file

@ -79,7 +79,7 @@
UIImage *folderImage = [UIImage imageNamed:@"folder-open"];
CGFloat folderImageViewX = 10.0;
if ([[UIDevice currentDevice] userInterfaceIdiom] != UIUserInterfaceIdiomPad) {
if (((NewsBlurAppDelegate *)[[UIApplication sharedApplication] delegate]).isPhone) {
folderImageViewX = 7.0;
}

View file

@ -18,9 +18,7 @@ typedef NS_ENUM(NSUInteger, FeedChooserOperation)
};
@interface FeedChooserViewController : BaseViewController {
NewsBlurAppDelegate *appDelegate;
}
@interface FeedChooserViewController : BaseViewController
@property (weak) IBOutlet UITableView *tableView;

View file

@ -30,7 +30,6 @@ static const CGFloat kFolderTitleHeight = 36.0;
@property (nonatomic) FeedChooserSort sort;
@property (nonatomic) BOOL ascending;
@property (nonatomic) BOOL flat;
@property (nonatomic, readonly) NewsBlurAppDelegate *appDelegate;
@property (nonatomic, strong) NSUserDefaults *groupDefaults;
@property (nonatomic, readonly) NSArray *widgetFeeds;
@ -45,8 +44,6 @@ static const CGFloat kFolderTitleHeight = 36.0;
- (void)viewDidLoad {
[super viewDidLoad];
appDelegate = [NewsBlurAppDelegate sharedAppDelegate];
if (self.operation == FeedChooserOperationWidgetSites) {
self.groupDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.newsblur.NewsBlur-Group"];
}
@ -830,6 +827,36 @@ static const CGFloat kFolderTitleHeight = 36.0;
return indexIndex;
}
#if TARGET_OS_MACCATALYST
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSArray<NSIndexPath *> *selectedRows = [tableView indexPathsForSelectedRows];
if ([selectedRows containsObject:indexPath]) {
[tableView deselectRowAtIndexPath:indexPath animated:false];
return nil;
}
return indexPath;
}
- (NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
NSArray<NSIndexPath *> *selectedRows = [tableView indexPathsForSelectedRows];
if ([selectedRows containsObject:indexPath]) {
return nil;
}
return indexPath;
}
- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath {
NSArray<NSIndexPath *> *selectedRows = [tableView indexPathsForSelectedRows];
for (NSIndexPath *index in selectedRows) {
[[tableView cellForRowAtIndexPath:index] setHighlighted:YES];
}
return YES;
}
#endif
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (self.operation == FeedChooserOperationWidgetSites) {
[self deselectRowsOutsideSection:indexPath.section];

View file

@ -75,6 +75,7 @@ struct CardView: View {
}
Button {
cache.appDelegate.activeStory = story.dictionary
cache.appDelegate.feedDetailViewController.markFeedsRead(fromTimestamp: story.timestamp, andOlder: false)
cache.appDelegate.feedDetailViewController.reload()
} label: {
@ -82,12 +83,15 @@ struct CardView: View {
}
Button {
cache.appDelegate.activeStory = story.dictionary
cache.appDelegate.feedDetailViewController.markFeedsRead(fromTimestamp: story.timestamp, andOlder: true)
cache.appDelegate.feedDetailViewController.reload()
} label: {
Label("Mark older stories read", image: "mark-read")
}
Divider()
Button {
cache.appDelegate.storiesCollection.toggleStorySaved(story.dictionary)
cache.appDelegate.feedDetailViewController.reload()
@ -96,13 +100,15 @@ struct CardView: View {
}
Button {
cache.appDelegate.activeStory = story.dictionary
cache.appDelegate.showSend(to: cache.appDelegate.feedDetailViewController, sender: cache.appDelegate.feedDetailViewController.view)
} label: {
Label("Send this story to…", image: "email")
}
Button {
cache.appDelegate.openTrainStory(nil)
cache.appDelegate.activeStory = story.dictionary
cache.appDelegate.openTrainStory(cache.appDelegate.feedDetailViewController.view)
} label: {
Label("Train this story", image: "train")
}
@ -168,14 +174,14 @@ struct CardContentView: View {
var body: some View {
VStack(alignment: .leading) {
if story.isRiverOrSocial, let feedImage {
if let feed = story.feed, feed.isRiverOrSocial, let feedImage = feed.image {
HStack {
Image(uiImage: feedImage)
.resizable()
.frame(width: 16, height: 16)
.padding(.leading, cache.settings.spacing == .compact ? 20 : 24)
Text(story.feedName)
Text(feed.name)
.font(font(named: "WhitneySSm-Medium", size: 12))
.lineLimit(1)
.foregroundColor(feedColor)
@ -239,14 +245,6 @@ struct CardContentView: View {
}
}
var feedImage: UIImage? {
if let image = cache.appDelegate.getFavicon(story.feedID) {
return Utilities.roundCorneredImage(image, radius: 4, convertTo: CGSizeMake(16, 16))
} else {
return nil
}
}
var unreadImage: UIImage? {
guard story.isReadAvailable else {
return nil
@ -304,7 +302,7 @@ struct CardFeedBarView: View {
var body: some View {
GeometryReader { geometry in
if let color = story.feedColorBarLeft {
if let feed = story.feed, let color = feed.colorBarLeft {
Path { path in
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: 0, y: geometry.size.height))
@ -312,7 +310,7 @@ struct CardFeedBarView: View {
.stroke(Color(color), lineWidth: 4)
}
if let color = story.feedColorBarRight {
if let feed = story.feed, let color = feed.colorBarRight {
Path { path in
path.move(to: CGPoint(x: 4, y: 0))
path.addLine(to: CGPoint(x: 4, y: geometry.size.height))

View file

@ -136,6 +136,7 @@ struct FeedDetailGridView: View {
}
}
.modify({ view in
#if !targetEnvironment(macCatalyst)
if #available(iOS 15.0, *) {
view.refreshable {
if cache.canPullToRefresh {
@ -143,6 +144,7 @@ struct FeedDetailGridView: View {
}
}
}
#endif
})
}
.background(Color.themed([0xE0E0E0, 0xFFF8CA, 0x363636, 0x101010]))

View file

@ -14,7 +14,6 @@
#import "MCSwipeTableViewCell.h"
#import "FeedDetailTableCell.h"
@class NewsBlurAppDelegate;
@class MCSwipeTableViewCell;
@interface FeedDetailObjCViewController : BaseViewController
@ -23,8 +22,6 @@
MCSwipeTableViewCellDelegate,
UIGestureRecognizerDelegate, UISearchBarDelegate,
UITableViewDragDelegate> {
NewsBlurAppDelegate *appDelegate;
BOOL pageFetching;
BOOL pageFinished;
BOOL finishedAnimatingIn;
@ -39,7 +36,6 @@
NBNotifier *notifier;
}
@property (nonatomic) IBOutlet NewsBlurAppDelegate *appDelegate;
@property (nonatomic, strong) IBOutlet UITableView *storyTitlesTable;
@property (nonatomic) IBOutlet UIBarButtonItem * feedMarkReadButton;
@property (nonatomic) IBOutlet UIBarButtonItem * feedsBarButton;
@ -50,7 +46,9 @@
@property (nonatomic) IBOutlet UIBarButtonItem * titleImageBarButton;
@property (nonatomic, retain) NBNotifier *notifier;
@property (nonatomic, retain) StoriesCollection *storiesCollection;
#if !TARGET_OS_MACCATALYST
@property (nonatomic) UIRefreshControl *refreshControl;
#endif
@property (nonatomic) UISearchBar *searchBar;
@property (nonatomic) IBOutlet UIView *messageView;
@property (nonatomic) IBOutlet UILabel *messageLabel;
@ -112,16 +110,19 @@
- (void)loadStoryAtRow:(NSInteger)row;
- (void)redrawUnreadStory;
- (IBAction)doOpenMarkReadMenu:(id)sender;
- (IBAction)doMarkAllRead:(id)sender;
- (IBAction)doOpenSettingsMenu:(id)sender;
- (void)deleteSite;
- (void)deleteFolder;
- (void)muteSite;
- (void)openTrainSite;
- (IBAction)muteSite;
- (IBAction)openTrainSite;
- (IBAction)openNotifications:(id)sender;
- (void)openNotificationsWithFeed:(NSString *)feedId;
- (void)openRenameSite;
- (IBAction)openStatistics:(id)sender;
- (IBAction)openRenameSite;
- (void)showUserProfile;
- (void)changeActiveFeedDetailRow;
- (void)instafetchFeed;
- (IBAction)instafetchFeed;
- (void)changeActiveStoryTitleCellLayout;
- (void)didSelectItemAtIndexPath:(NSIndexPath *)indexPath;
- (void)loadFaviconsFromActiveFeed;
@ -132,4 +133,7 @@
- (void)failedMarkAsUnsaved:(NSDictionary *)params;
- (void)failedMarkAsUnread:(NSDictionary *)params;
- (void)confirmDeleteSite:(UINavigationController *)menuNavigationController;
- (void)openMoveView:(UINavigationController *)menuNavigationController;
@end

View file

@ -69,7 +69,6 @@ typedef NS_ENUM(NSUInteger, FeedSection)
@synthesize separatorBarButton;
@synthesize titleImageBarButton;
@synthesize spacerBarButton, spacer2BarButton;
@synthesize appDelegate;
@synthesize pageFetching;
@synthesize pageFinished;
@synthesize finishedAnimatingIn;
@ -92,8 +91,6 @@ typedef NS_ENUM(NSUInteger, FeedSection)
- (void)viewDidLoad {
[super viewDidLoad];
self.appDelegate = [NewsBlurAppDelegate sharedAppDelegate];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(preferredContentSizeChanged:)
name:UIContentSizeCategoryDidChangeNotification
@ -106,7 +103,7 @@ typedef NS_ENUM(NSUInteger, FeedSection)
if (@available(iOS 15.0, *)) {
self.storyTitlesTable.allowsFocus = NO;
}
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!self.isPhone) {
self.storyTitlesTable.dragDelegate = self;
self.storyTitlesTable.dragInteractionEnabled = YES;
}
@ -119,10 +116,12 @@ typedef NS_ENUM(NSUInteger, FeedSection)
initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
spacer2BarButton.width = 0;
#if !TARGET_OS_MACCATALYST
self.refreshControl = [UIRefreshControl new];
self.refreshControl.tintColor = UIColorFromLightDarkRGB(0x0, 0xffffff);
self.refreshControl.backgroundColor = UIColorFromRGB(0xE3E6E0);
[self.refreshControl addTarget:self action:@selector(refresh:) forControlEvents:UIControlEventValueChanged];
#endif
self.searchBar = [[UISearchBar alloc]
initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.storyTitlesTable.frame), 44.)];
@ -150,11 +149,11 @@ typedef NS_ENUM(NSUInteger, FeedSection)
self.feedsBarButton = [[UIBarButtonItem alloc] initWithTitle:@"Sites" style:UIBarButtonItemStylePlain target:self action:@selector(doShowFeeds:)];
self.feedsBarButton.accessibilityLabel = @"Show Sites";
UIImage *settingsImage = [Utilities imageNamed:@"settings" sized:30];
UIImage *settingsImage = [Utilities imageNamed:@"settings" sized:self.isMac ? 24 : 30];
settingsBarButton = [UIBarButtonItem barItemWithImage:settingsImage target:self action:@selector(doOpenSettingsMenu:)];
settingsBarButton.accessibilityLabel = @"Settings";
UIImage *markreadImage = [Utilities imageNamed:@"mark-read" sized:30];
UIImage *markreadImage = [Utilities imageNamed:@"mark-read" sized:self.isMac ? 24 : 30];
feedMarkReadButton = [UIBarButtonItem barItemWithImage:markreadImage target:self action:@selector(doOpenMarkReadMenu:)];
feedMarkReadButton.accessibilityLabel = @"Mark all as read";
@ -166,16 +165,19 @@ typedef NS_ENUM(NSUInteger, FeedSection)
[view addGestureRecognizer:markReadLongPress];
titleImageBarButton = [UIBarButtonItem alloc];
#if TARGET_OS_MACCATALYST
if (@available(macCatalyst 16.0, *)) {
settingsBarButton.hidden = YES;
feedMarkReadButton.hidden = YES;
}
#else
UILongPressGestureRecognizer *tableLongPress = [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:@selector(handleTableLongPress:)];
tableLongPress.minimumPressDuration = 1.0;
tableLongPress.delegate = self;
[self.storyTitlesTable addGestureRecognizer:tableLongPress];
#if TARGET_OS_MACCATALYST
// CATALYST: support double-click; doing the following breaks clicking on rows in Catalyst.
#else
UITapGestureRecognizer *doubleTapGesture = [[UITapGestureRecognizer alloc]
initWithTarget:self action:nil];
doubleTapGesture.numberOfTapsRequired = 2;
@ -406,6 +408,11 @@ typedef NS_ENUM(NSUInteger, FeedSection)
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
#if TARGET_OS_MACCATALYST
[self.navigationController setNavigationBarHidden:YES animated:animated];
[self.navigationController setToolbarHidden:YES animated:animated];
#endif
self.appDelegate = (NewsBlurAppDelegate *)[[UIApplication sharedApplication] delegate];
if (self.standardInteractivePopGestureDelegate == nil) {
@ -434,7 +441,7 @@ typedef NS_ENUM(NSUInteger, FeedSection)
if (storiesCollection == nil) {
NSString *appOpening = [userPreferences stringForKey:@"app_opening"];
if ([appOpening isEqualToString:@"feeds"] && [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if ([appOpening isEqualToString:@"feeds"] && !self.isPhone) {
self.messageLabel.text = @"Select a feed to read";
self.messageView.hidden = NO;
}
@ -510,11 +517,13 @@ typedef NS_ENUM(NSUInteger, FeedSection)
[self.searchBar setShowsCancelButton:NO animated:YES];
}
#if !TARGET_OS_MACCATALYST
if (self.canPullToRefresh) {
self.storyTitlesTable.refreshControl = self.refreshControl;
} else {
self.storyTitlesTable.refreshControl = nil;
}
#endif
[self updateTheme];
@ -751,7 +760,7 @@ typedef NS_ENUM(NSUInteger, FeedSection)
}
- (void)beginOfflineTimer {
if ([self.storiesCollection.activeFolder isEqualToString:@"infrequent"]) {
if (self.storiesCollection.isInfrequent) {
return;
}
@ -844,6 +853,11 @@ typedef NS_ENUM(NSUInteger, FeedSection)
continue;
}
if (indexPath.row > storiesCollection.storyLocationsCount) {
NSLog(@"⚠️ row %@ is greater than the story locations count: %@", @(indexPath.row), @(storiesCollection.storyLocationsCount)); // log
continue;
}
[self reloadIndexPath:indexPath withRowAnimation:UITableViewRowAnimationNone];
break;
}
@ -1300,6 +1314,13 @@ typedef NS_ENUM(NSUInteger, FeedSection)
NSLog(@"finishedLoadingFeed: %@", receivedFeedId); // log
#if TARGET_OS_MACCATALYST
if (@available(macCatalyst 16.0, *)) {
settingsBarButton.hidden = NO;
feedMarkReadButton.hidden = NO;
}
#endif
self.pageFinished = NO;
[self renderStories:confirmedNewStories];
@ -1440,7 +1461,7 @@ typedef NS_ENUM(NSUInteger, FeedSection)
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
NSString *feedOpening = [preferences stringForKey:@"feed_opening"];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad && feedOpening == nil) {
if (!self.isPhone && feedOpening == nil) {
feedOpening = @"story";
}
@ -1493,7 +1514,7 @@ typedef NS_ENUM(NSUInteger, FeedSection)
// NSIndexPath *indexPath = [NSIndexPath indexPathForRow:locationOfStoryId inSection:0];
NSIndexPath *indexPath = [self indexPathForStoryLocation:locationOfStoryId];
if (self.isLegacyTable && self.storyTitlesTable.window != nil) {
if (self.isLegacyTable && self.storyTitlesTable.window != nil && indexPath.row < [self.storyTitlesTable numberOfRowsInSection:0]) {
[self tableView:self.storyTitlesTable selectRowAtIndexPath:indexPath
animated:NO
scrollPosition:UITableViewScrollPositionMiddle];
@ -1621,6 +1642,35 @@ typedef NS_ENUM(NSUInteger, FeedSection)
toItem:fleuron
attribute:NSLayoutAttributeBottom
multiplier:1.0 constant:height/2]];
} else if (!self.isMarkReadOnScroll) {
UIButton *markReadButton = [UIButton buttonWithType:UIButtonTypeCustom];
markReadButton.titleLabel.font = [UIFont systemFontOfSize:14];
[markReadButton setTitle:@" Mark All Stories as Read " forState:UIControlStateNormal];
[markReadButton addTarget:self action:@selector(doMarkAllRead:) forControlEvents:UIControlEventTouchUpInside];
markReadButton.tintColor = UIColor.whiteColor;
markReadButton.backgroundColor = UIColorFromFixedRGB(0x939EAF);
markReadButton.layer.cornerRadius = 10;
[markReadButton sizeToFit];
markReadButton.translatesAutoresizingMaskIntoConstraints = NO;
[cell.contentView addSubview:markReadButton];
[cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:markReadButton
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:cell.contentView
attribute:NSLayoutAttributeCenterX
multiplier:1.0 constant:0]];
[cell.contentView addConstraint:[NSLayoutConstraint constraintWithItem:markReadButton
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:fleuron
attribute:NSLayoutAttributeBottom
multiplier:1.0 constant:height/2]];
}
return cell;
@ -2380,6 +2430,21 @@ didEndSwipingSwipingWithState:(MCSwipeTableViewCellState)state
visibleUnreadCount = 0;
}
#if TARGET_OS_MACCATALYST
UINavigationController *feedDetailNavController = appDelegate.feedDetailViewController.navigationController;
UIView *sourceView = feedDetailNavController.view;
CGRect sourceRect = CGRectMake(120, 0, 20, 20);
if (appDelegate.splitViewController.isFeedListHidden) {
sourceRect = CGRectMake(192, 0, 20, 20);
}
[self.appDelegate showMarkReadMenuWithFeedIds:feedIds collectionTitle:collectionTitle visibleUnreadCount:visibleUnreadCount sourceView:sourceView sourceRect:sourceRect completionHandler:^(BOOL marked){
if (marked) {
pop();
}
}];
#else
UIBarButtonItem *barButton = self.feedMarkReadButton;
if (sender && [sender isKindOfClass:[UIBarButtonItem class]]) barButton = sender;
@ -2388,6 +2453,7 @@ didEndSwipingSwipingWithState:(MCSwipeTableViewCellState)state
pop();
}
}];
#endif
}
- (IBAction)doOpenMarkReadMenu:(id)sender {
@ -2406,11 +2472,6 @@ didEndSwipingSwipingWithState:(MCSwipeTableViewCellState)state
appDelegate.storiesCollection.isReadView;
}
- (BOOL)isInfrequent {
return appDelegate.storiesCollection.isRiverView &&
[appDelegate.storiesCollection.activeFolder isEqualToString:@"infrequent"];
}
- (IBAction)doShowFeeds:(id)sender {
[self.appDelegate showColumn:UISplitViewControllerColumnPrimary debugInfo:@"showFeeds"];
}
@ -2425,8 +2486,8 @@ didEndSwipingSwipingWithState:(MCSwipeTableViewCellState)state
MenuViewController *viewController = [MenuViewController new];
__weak MenuViewController *weakViewController = viewController;
BOOL everything = [appDelegate.storiesCollection.activeFolder isEqualToString:@"everything"];
BOOL infrequent = [self isInfrequent];
BOOL everything = appDelegate.storiesCollection.isEverything;
BOOL infrequent = appDelegate.storiesCollection.isInfrequent;
BOOL river = [self isRiver];
BOOL read = appDelegate.storiesCollection.isReadView;
BOOL widget = appDelegate.storiesCollection.isWidgetView;
@ -2608,7 +2669,19 @@ didEndSwipingSwipingWithState:(MCSwipeTableViewCellState)state
UINavigationController *navController = self.navigationController ?: appDelegate.storyPagesViewController.navigationController;
#if TARGET_OS_MACCATALYST
UINavigationController *feedDetailNavController = appDelegate.feedDetailViewController.navigationController;
UIView *sourceView = feedDetailNavController.view;
CGRect sourceRect = CGRectMake(152, 0, 20, 20);
if (appDelegate.splitViewController.isFeedListHidden) {
sourceRect = CGRectMake(224, 0, 20, 20);
}
[viewController showFromNavigationController:navController barButtonItem:nil sourceView:sourceView sourceRect:sourceRect permittedArrowDirections:UIPopoverArrowDirectionDown];
#else
[viewController showFromNavigationController:navController barButtonItem:self.settingsBarButton];
#endif
}
- (NSString *)feedIdForSearch {
@ -2806,7 +2879,7 @@ didEndSwipingSwipingWithState:(MCSwipeTableViewCellState)state
}];
}
- (void)muteSite {
- (IBAction)muteSite {
[MBProgressHUD hideHUDForView:self.view animated:YES];
MBProgressHUD *HUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
HUD.labelText = @"Muting...";
@ -2959,7 +3032,7 @@ didEndSwipingSwipingWithState:(MCSwipeTableViewCellState)state
[menuNavigationController showViewController:viewController sender:self];
}
- (void)openTrainSite {
- (IBAction)openTrainSite {
[appDelegate openTrainSite];
}
@ -2969,15 +3042,27 @@ didEndSwipingSwipingWithState:(MCSwipeTableViewCellState)state
[self reload];
}
- (IBAction)openNotifications:(id)sender {
NSString *feedIdStr = storiesCollection.activeFeedIdStr;
[appDelegate openNotificationsWithFeed:feedIdStr];
}
- (void)openNotificationsWithFeed:(NSString *)feedId {
[appDelegate openNotificationsWithFeed:feedId];
}
- (IBAction)openStatistics:(id)sender {
NSString *feedIdStr = storiesCollection.activeFeedIdStr;
[appDelegate openStatisticsWithFeed:feedIdStr sender:settingsBarButton];
}
- (void)openStatisticsWithFeed:(NSString *)feedId {
[appDelegate openStatisticsWithFeed:feedId sender:settingsBarButton];
}
- (void)openRenameSite {
- (IBAction)openRenameSite {
NSString *title = [NSString stringWithFormat:@"Rename \"%@\"", appDelegate.storiesCollection.isRiverView ?
[appDelegate extractFolderName:appDelegate.storiesCollection.activeFolder] : [appDelegate.storiesCollection.activeFeed objectForKey:@"feed_title"]];
NSString *subtitle = (appDelegate.storiesCollection.isRiverView ?
@ -3068,8 +3153,10 @@ didEndSwipingSwipingWithState:(MCSwipeTableViewCellState)state
self.navigationItem.titleView = [appDelegate makeFeedTitle:storiesCollection.activeFeed];
}
#if !TARGET_OS_MACCATALYST
self.refreshControl.tintColor = UIColorFromLightDarkRGB(0x0, 0xffffff);
self.refreshControl.backgroundColor = UIColorFromRGB(0xE3E6E0);
#endif
self.searchBar.backgroundColor = UIColorFromRGB(0xE3E6E0);
self.searchBar.tintColor = UIColorFromRGB(0xffffff);
@ -3093,6 +3180,16 @@ didEndSwipingSwipingWithState:(MCSwipeTableViewCellState)state
[self reload];
}
//- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
// NSLog(@"canPerformAction: %@ withSender: %@", NSStringFromSelector(action), sender); // log
//
// if (action == @selector(deleteSite:)) {
// return NO;
// }
//
// return YES;
//}
#pragma mark -
#pragma mark Story Actions - save
@ -3127,7 +3224,7 @@ didEndSwipingSwipingWithState:(MCSwipeTableViewCellState)state
// called when the user taps refresh button
- (void)instafetchFeed {
- (IBAction)instafetchFeed {
NSString *urlString = [NSString
stringWithFormat:@"%@/reader/refresh_feed/%@",
self.appDelegate.url,
@ -3152,12 +3249,16 @@ didEndSwipingSwipingWithState:(MCSwipeTableViewCellState)state
}
}
- (IBAction)deleteSite:(id)sender {
//TODO
}
#pragma mark -
#pragma mark PullToRefresh
- (BOOL)canPullToRefresh {
BOOL river = appDelegate.storiesCollection.isRiverView;
BOOL infrequent = [self isInfrequent];
BOOL infrequent = appDelegate.storiesCollection.isInfrequent;
BOOL read = appDelegate.storiesCollection.isReadView;
BOOL widget = appDelegate.storiesCollection.isWidgetView;
BOOL saved = appDelegate.storiesCollection.isSavedView;
@ -3165,6 +3266,7 @@ didEndSwipingSwipingWithState:(MCSwipeTableViewCellState)state
return appDelegate.storiesCollection.activeFeed != nil && !river && !infrequent && !saved && !read && !widget;
}
#if !TARGET_OS_MACCATALYST
- (void)refresh:(UIRefreshControl *)refreshControl {
if (self.canPullToRefresh) {
self.inPullToRefresh_ = YES;
@ -3173,10 +3275,13 @@ didEndSwipingSwipingWithState:(MCSwipeTableViewCellState)state
[self finishRefresh];
}
}
#endif
- (void)finishRefresh {
self.inPullToRefresh_ = NO;
#if !TARGET_OS_MACCATALYST
[self.refreshControl endRefreshing];
#endif
}
#pragma mark -

View file

@ -29,10 +29,6 @@ class FeedDetailViewController: FeedDetailObjCViewController {
case loading
}
var isGrid: Bool {
return appDelegate.detailViewController.layout == .grid
}
var wasGrid: Bool {
return appDelegate.detailViewController.wasGrid
}
@ -138,7 +134,15 @@ class FeedDetailViewController: FeedDetailObjCViewController {
@objc var suppressMarkAsRead = false
var scrollingDate = Date.distantPast
func deferredReload(story: Story? = nil) {
if let story {
print("🪿 queuing deferred reload for \(story)")
} else {
print("🪿 queuing deferred reload")
}
reloadWorkItem?.cancel()
if let story {
@ -153,6 +157,16 @@ class FeedDetailViewController: FeedDetailObjCViewController {
}
if pendingStories.isEmpty {
print("🪿 starting deferred reload")
let secondsSinceScroll = -scrollingDate.timeIntervalSinceNow
if secondsSinceScroll < 0.5 {
print("🪿 too soon to reload; \(secondsSinceScroll) seconds since scroll")
deferredReload(story: story)
return
}
configureDataSource()
} else {
for story in pendingStories.values {
@ -202,6 +216,55 @@ extension FeedDetailViewController {
reloadTable()
}
}
#if targetEnvironment(macCatalyst)
override func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
let location = storyLocation(for: indexPath)
guard location < storiesCollection.storyLocationsCount else {
return nil
}
let storyIndex = storiesCollection.index(fromLocation: location)
let story = Story(index: storyIndex)
appDelegate.activeStory = story.dictionary
return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { suggestedActions in
let read = UIAction(title: story.isRead ? "Mark as unread" : "Mark as read", image: UIImage(named: "mark-read")) { action in
self.appDelegate.storiesCollection.toggleStoryUnread(story.dictionary)
self.reload()
}
let newer = UIAction(title: "Mark newer stories read", image: UIImage(named: "mark-read")) { action in
self.markFeedsRead(fromTimestamp: story.timestamp, andOlder: false)
self.reload()
}
let older = UIAction(title: "Mark older stories read", image: UIImage(named: "mark-read")) { action in
self.markFeedsRead(fromTimestamp: story.timestamp, andOlder: true)
self.reload()
}
let saved = UIAction(title: story.isSaved ? "Unsave this story" : "Save this story", image: UIImage(named: "saved-stories")) { action in
self.appDelegate.storiesCollection.toggleStorySaved(story.dictionary)
self.reload()
}
let send = UIAction(title: "Send this story to…", image: UIImage(named: "email")) { action in
self.appDelegate.showSend(to: self, sender: self.view)
}
let train = UIAction(title: "Train this story", image: UIImage(named: "train")) { action in
self.appDelegate.openTrainStory(self.view)
}
let submenu = UIMenu(title: "", options: .displayInline, children: [saved, send, train])
return UIMenu(title: "", children: [read, newer, older, submenu])
}
}
#endif
}
extension FeedDetailViewController: FeedDetailInteraction {
@ -232,12 +295,18 @@ extension FeedDetailViewController: FeedDetailInteraction {
let cacheCount = storyCache.before.count + storyCache.after.count
if cacheCount > 0, story.index >= cacheCount - 5 {
let debug = Date()
if storiesCollection.isRiverView, storiesCollection.activeFolder != nil {
fetchRiverPage(storiesCollection.feedPage + 1, withCallback: nil)
} else {
fetchFeedDetail(storiesCollection.feedPage + 1, withCallback: nil)
}
print("🐓 Fetching next page took \(-debug.timeIntervalSinceNow) seconds")
}
scrollingDate = Date()
}
func tapped(story: Story) {

View file

@ -174,11 +174,18 @@ static UIFont *textFont = nil;
BOOL isHighlighted = cell.highlighted || cell.selected;
UIColor *backgroundColor;
#if TARGET_OS_MACCATALYST
backgroundColor = cell.isSocial ? UIColorFromRGB(0xD8E3DB) :
cell.isSearch ? UIColorFromRGB(0xDBDFE6) :
cell.isSaved ? UIColorFromRGB(0xDFDCD6) :
UIColor.clearColor;
#else
backgroundColor = cell.isSocial ? UIColorFromRGB(0xD8E3DB) :
cell.isSearch ? UIColorFromRGB(0xDBDFE6) :
cell.isSaved ? UIColorFromRGB(0xDFDCD6) :
UIColorFromRGB(0xF7F8F5);
#endif
// [backgroundColor set];
self.backgroundColor = backgroundColor;
cell.backgroundColor = backgroundColor;
@ -219,7 +226,7 @@ static UIFont *textFont = nil;
paragraphStyle.alignment = NSTextAlignmentLeft;
CGSize faviconSize;
if (cell.isSocial) {
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!cell.appDelegate.isPhone) {
faviconSize = CGSizeMake(28, 28);
UIImage *feedIcon = [Utilities roundCorneredImage:cell.feedFavicon radius:4 convertToSize:faviconSize];
[feedIcon drawInRect:CGRectMake(9.0, CGRectGetMidY(r)-faviconSize.height/2, faviconSize.width, faviconSize.height)];
@ -239,7 +246,7 @@ static UIFont *textFont = nil;
} else {
faviconSize = CGSizeMake(16, 16);
UIImage *feedIcon = [Utilities roundCorneredImage:cell.feedFavicon radius:4 convertToSize:faviconSize];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!cell.appDelegate.isPhone) {
[feedIcon drawInRect:CGRectMake(12.0, CGRectGetMidY(r)-faviconSize.height/2, faviconSize.width, faviconSize.height)];
[cell.feedTitle drawInRect:CGRectMake(36.0, titleOffsetY, r.size.width - ([cell.unreadCount offsetWidth] + 36) - 10, font.pointSize*1.4)
withAttributes:@{NSFontAttributeName: font,

View file

@ -21,8 +21,6 @@ static enum {
NewsBlurTopSectionAllStories = 1
} NewsBlurTopSection;
@class NewsBlurAppDelegate;
@interface FeedsObjCViewController : BaseViewController
<UITableViewDelegate, UITableViewDataSource,
NSCacheDelegate,
@ -30,8 +28,6 @@ UIPopoverControllerDelegate,
IASKSettingsDelegate,
MCSwipeTableViewCellDelegate,
UIGestureRecognizerDelegate, UISearchBarDelegate> {
NewsBlurAppDelegate *appDelegate;
NSMutableDictionary * activeFeedLocations;
NSMutableDictionary *stillVisibleFeeds;
NSMutableDictionary *visibleFolders;
@ -53,15 +49,23 @@ UIGestureRecognizerDelegate, UISearchBarDelegate> {
NBNotifier *notifier;
}
@property (nonatomic) IBOutlet NewsBlurAppDelegate *appDelegate;
@property (nonatomic) IBOutlet UIView *innerView;
@property (nonatomic) IBOutlet UITableView *feedTitlesTable;
@property (nonatomic) IBOutlet NSLayoutConstraint *feedTitlesTopConstraint;
@property (nonatomic) IBOutlet NSLayoutConstraint *feedTitlesLeadingConstraint;
@property (nonatomic) IBOutlet NSLayoutConstraint *feedTitlesTrailingConstraint;
@property (nonatomic) IBOutlet UIToolbar *feedViewToolbar;
@property (nonatomic) IBOutlet UISlider * feedScoreSlider;
@property (nonatomic) IBOutlet UIBarButtonItem * homeButton;
@property (nonatomic) IBOutlet UIBarButtonItem * addBarButton;
@property (nonatomic) IBOutlet UIBarButtonItem * settingsBarButton;
@property (nonatomic) UIButton *activityButton;
@property (nonatomic) IBOutlet UIBarButtonItem * activitiesButton;
#if TARGET_OS_MACCATALYST
@property (nonatomic) IBOutlet UIBarButtonItem * spacerBarButton;
@property (nonatomic) IBOutlet UIBarButtonItem * userBarButton;
#endif
@property (nonatomic) IBOutlet UIView *userInfoView;
@property (nonatomic) IBOutlet UIButton *userAvatarButton;
@property (nonatomic) IBOutlet UILabel *neutralCount;
@property (nonatomic) IBOutlet UILabel *positiveCount;
@ -74,7 +78,9 @@ UIGestureRecognizerDelegate, UISearchBarDelegate> {
@property (nonatomic, readwrite) BOOL viewShowingAllFeeds;
@property (nonatomic, readwrite) BOOL interactiveFeedDetailTransition;
@property (nonatomic, readwrite) BOOL isOffline;
#if !TARGET_OS_MACCATALYST
@property (nonatomic) UIRefreshControl *refreshControl;
#endif
@property (nonatomic) UISearchBar *searchBar;
@property (nonatomic, strong) NSArray<NSString *> *searchFeedIds;
@property (nonatomic) NSCache *imageCache;
@ -94,7 +100,14 @@ UIGestureRecognizerDelegate, UISearchBarDelegate> {
- (void)didSelectSectionHeader:(UIButton *)button;
- (void)didSelectSectionHeaderWithTag:(NSInteger)tag;
- (void)selectNextFolderOrFeed;
- (IBAction)selectIntelligence;
- (void)selectEverything:(id)sender;
- (void)selectNextFeed:(id)sender;
- (void)selectPreviousFeed:(id)sender;
- (void)selectNextFolder:(id)sender;
- (void)selectPreviousFolder:(id)sender;
- (void)markFeedRead:(NSString *)feedId cutoffDays:(NSInteger)days;
- (void)markFeedsRead:(NSArray *)feedIds cutoffDays:(NSInteger)days;
- (void)markEverythingReadWithDays:(NSInteger)days;

View file

@ -57,7 +57,6 @@ static NSArray<NSString *> *NewsBlurTopSectionNames;
@implementation FeedsObjCViewController
@synthesize appDelegate;
@synthesize feedTitlesTable;
@synthesize feedViewToolbar;
@synthesize feedScoreSlider;
@ -113,12 +112,14 @@ static NSArray<NSString *> *NewsBlurTopSectionNames;
self.rowHeights = [NSMutableDictionary dictionary];
self.folderTitleViews = [NSMutableDictionary dictionary];
#if !TARGET_OS_MACCATALYST
self.refreshControl = [UIRefreshControl new];
self.refreshControl.tintColor = UIColorFromLightDarkRGB(0x0, 0xffffff);
self.refreshControl.backgroundColor = UIColorFromRGB(0xE3E6E0);
[self.refreshControl addTarget:self action:@selector(refresh:) forControlEvents:UIControlEventValueChanged];
self.feedTitlesTable.refreshControl = self.refreshControl;
self.feedViewToolbar.translatesAutoresizingMaskIntoConstraints = NO;
#endif
self.searchBar = [[UISearchBar alloc]
initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.feedTitlesTable.frame), 44.)];
@ -129,7 +130,16 @@ static NSArray<NSString *> *NewsBlurTopSectionNames;
self.searchBar.nb_searchField.textColor = UIColorFromRGB(0x0);
[self.searchBar setSearchBarStyle:UISearchBarStyleMinimal];
[self.searchBar setAutocapitalizationType:UITextAutocapitalizationTypeNone];
#if TARGET_OS_MACCATALYST
// Workaround for Catalyst bug.
self.searchBar.frame = CGRectMake(10, 0, CGRectGetWidth(self.feedTitlesTable.frame) - 20, 44.);
self.searchBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
UIView *searchContainerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.feedTitlesTable.frame), 44.)];
[searchContainerView addSubview:self.searchBar];
self.feedTitlesTable.tableHeaderView = searchContainerView;
#else
self.feedTitlesTable.tableHeaderView = self.searchBar;
#endif
userLabelFont = [UIFont fontWithName:@"WhitneySSm-Medium" size:15.0];
@ -169,7 +179,17 @@ static NSArray<NSString *> *NewsBlurTopSectionNames;
[[UIBarButtonItem appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName:
UIColorFromFixedRGB(0x4C4D4A)}
forState:UIControlStateHighlighted];
#if TARGET_OS_MACCATALYST
self.innerView.backgroundColor = UIColor.clearColor;
if (ThemeManager.themeManager.isLikeSystem) {
self.view.backgroundColor = UIColor.clearColor;
} else {
self.view.backgroundColor = UIColorFromRGB(0xf4f4f4);
}
#else
self.view.backgroundColor = UIColorFromRGB(0xf4f4f4);
#endif
self.navigationController.navigationBar.tintColor = UIColorFromRGB(0x8F918B);
self.navigationController.navigationBar.translucent = NO;
UIInterfaceOrientation orientation = self.view.window.windowScene.interfaceOrientation;
@ -199,6 +219,12 @@ static NSArray<NSString *> *NewsBlurTopSectionNames;
self.feedTitlesTable.translatesAutoresizingMaskIntoConstraints = NO;
self.feedTitlesTable.estimatedRowHeight = 0;
#if TARGET_OS_MACCATALYST
// Workaround for Catalyst bug.
self.feedTitlesLeadingConstraint.constant = -10;
self.feedTitlesTrailingConstraint.constant = -10;
#endif
if (@available(iOS 15.0, *)) {
self.feedTitlesTable.sectionHeaderTopPadding = 0;
}
@ -228,7 +254,7 @@ static NSArray<NSString *> *NewsBlurTopSectionNames;
[self resetRowHeights];
// if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad &&
// if (!self.isPhone &&
// !self.interactiveFeedDetailTransition) {
//
// [appDelegate.masterContainerViewController transitionFromFeedDetail];
@ -236,6 +262,16 @@ static NSArray<NSString *> *NewsBlurTopSectionNames;
// NSLog(@"Feed List timing 0: %f", [NSDate timeIntervalSinceReferenceDate] - start);
[super viewWillAppear:animated];
#if TARGET_OS_MACCATALYST
UINavigationController *navController = self.navigationController;
UITitlebar *titlebar = navController.navigationBar.window.windowScene.titlebar;
titlebar.titleVisibility = UITitlebarTitleVisibilityHidden;
[self.navigationController setNavigationBarHidden:YES animated:animated];
[self.navigationController setToolbarHidden:YES animated:animated];
#endif
NSUserDefaults *userPreferences = [NSUserDefaults standardUserDefaults];
NSInteger intelligenceLevel = [userPreferences integerForKey:@"selectedIntelligence"];
@ -425,7 +461,7 @@ static NSArray<NSString *> *NewsBlurTopSectionNames;
- (void)layoutForInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// CGSize toolbarSize = [self.feedViewToolbar sizeThatFits:self.view.frame.size];
// if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
// if (!self.isPhone) {
// self.feedViewToolbar.frame = CGRectMake(-10.0f,
// CGRectGetHeight(self.view.frame) - toolbarSize.height,
// toolbarSize.width + 20, toolbarSize.height);
@ -434,7 +470,7 @@ static NSArray<NSString *> *NewsBlurTopSectionNames;
// }
// self.innerView.frame = (CGRect){CGPointZero, CGSizeMake(CGRectGetWidth(self.view.frame), CGRectGetMinY(self.feedViewToolbar.frame))};
// if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad && !appDelegate.isCompactWidth) {
// if (!self.isPhone && !appDelegate.isCompactWidth) {
// CGRect navFrame = appDelegate.navigationController.view.frame;
// CGFloat limit = appDelegate.masterContainerViewController.rightBorder.frame.origin.x + 1;
//
@ -456,7 +492,7 @@ static NSArray<NSString *> *NewsBlurTopSectionNames;
orientation = self.view.window.windowScene.interfaceOrientation;
}
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad && !UIInterfaceOrientationIsLandscape(orientation)) {
if (!self.isPhone && !UIInterfaceOrientationIsLandscape(orientation)) {
[self.intelligenceControl setImage:[UIImage imageNamed:@"unread_yellow_icn.png"] forSegmentAtIndex:1];
[self.intelligenceControl setImage:[Utilities imageNamed:@"indicator-focus" sized:14] forSegmentAtIndex:2];
[self.intelligenceControl setImage:[Utilities imageNamed:@"unread_blue_icn.png" sized:14] forSegmentAtIndex:3];
@ -494,6 +530,10 @@ static NSArray<NSString *> *NewsBlurTopSectionNames;
return YES;
}
- (void)buildMenuWithBuilder:(id<UIMenuBuilder>)builder {
}
#pragma mark -
#pragma mark State Restoration
@ -686,18 +726,19 @@ static NSArray<NSString *> *NewsBlurTopSectionNames;
// [settingsBarButton setCustomView:settingsButton];
UIImage *activityImage = [Utilities templateImageNamed:@"dialog-notifications" sized:32];
NBBarButtonItem *activityButton = [NBBarButtonItem buttonWithType:UIButtonTypeCustom];
activityButton.accessibilityLabel = @"Activities";
[activityButton setImage:activityImage forState:UIControlStateNormal];
activityButton.tintColor = UIColorFromRGB(0x8F918B);
[activityButton setImageEdgeInsets:UIEdgeInsetsMake(4, 0, 4, 0)];
[activityButton addTarget:self
[self.activityButton removeFromSuperview];
self.activityButton = [NBBarButtonItem buttonWithType:UIButtonTypeCustom];
self.activityButton.accessibilityLabel = @"Activities";
[self.activityButton setImage:activityImage forState:UIControlStateNormal];
self.activityButton.tintColor = UIColorFromRGB(0x8F918B);
[self.activityButton setImageEdgeInsets:UIEdgeInsetsMake(4, 0, 4, 0)];
[self.activityButton addTarget:self
action:@selector(showInteractionsPopover:)
forControlEvents:UIControlEventTouchUpInside];
activitiesButton = [[UIBarButtonItem alloc]
initWithCustomView:activityButton];
initWithCustomView:self.activityButton];
activitiesButton.width = 32;
// activityButton.backgroundColor = UIColor.redColor;
// self.activityButton.backgroundColor = UIColor.redColor;
self.navigationItem.rightBarButtonItem = activitiesButton;
NSMutableDictionary *sortedFolders = [[NSMutableDictionary alloc] init];
@ -901,7 +942,7 @@ static NSArray<NSString *> *NewsBlurTopSectionNames;
[self refreshHeaderCounts];
[appDelegate checkForFeedNotifications];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad && finished) {
if (!self.isPhone && finished) {
[self cacheFeedRowLocations];
}
@ -1035,7 +1076,11 @@ static NSArray<NSString *> *NewsBlurTopSectionNames;
appDelegate.activeUserProfileId = [NSString stringWithFormat:@"%@", [appDelegate.dictSocialProfile objectForKey:@"user_id"]];
appDelegate.activeUserProfileName = [NSString stringWithFormat:@"%@", [appDelegate.dictSocialProfile objectForKey:@"username"]];
// appDelegate.activeUserProfileName = @"You";
[appDelegate showUserProfileModal:self.navigationItem.titleView];
#if TARGET_OS_MACCATALYST
[appDelegate showUserProfileModal:self.userAvatarButton];
#else
[appDelegate showUserProfileModal:self.navigationItem.titleView];
#endif
}
- (IBAction)tapAddSite:(id)sender {
@ -1056,9 +1101,11 @@ static NSArray<NSString *> *NewsBlurTopSectionNames;
MenuViewController *viewController = [MenuViewController new];
[viewController addTitle:@"Preferences" iconName:@"dialog-preferences" iconColor:UIColorFromRGB(0xDF8566) selectionShouldDismiss:YES handler:^{
[self.appDelegate showPreferences];
}];
if (!self.isMac) {
[viewController addTitle:@"Preferences" iconName:@"dialog-preferences" iconColor:UIColorFromRGB(0xDF8566) selectionShouldDismiss:YES handler:^{
[self.appDelegate showPreferences];
}];
}
[viewController addTitle:@"Mute Sites" iconName:@"menu_icn_mute.png" selectionShouldDismiss:YES handler:^{
[self.appDelegate showMuteSites];
@ -1275,14 +1322,21 @@ static NSArray<NSString *> *NewsBlurTopSectionNames;
[appDelegate.feedDetailViewController reloadWithSizing];
}
- (void)systemAppearanceDidChange:(BOOL)isDark {
[super systemAppearanceDidChange:isDark];
#if TARGET_OS_MACCATALYST
if (ThemeManager.themeManager.isLikeSystem) {
self.view.backgroundColor = UIColor.clearColor;
} else {
self.view.backgroundColor = UIColorFromRGB(0xf4f4f4);
}
#endif
}
- (void)updateTheme {
[super updateTheme];
// CATALYST: This prematurely dismisses the login view controller; is it really appropriate?
// if (![self.presentedViewController isKindOfClass:[UINavigationController class]] || (((UINavigationController *)self.presentedViewController).topViewController != (UIViewController *)self.appDelegate.fontSettingsViewController && ![((UINavigationController *)self.presentedViewController).topViewController conformsToProtocol:@protocol(IASKViewController)])) {
// [self.presentedViewController dismissViewControllerAnimated:YES completion:nil];
// }
[self.appDelegate hidePopoverAnimated:YES];
UINavigationBarAppearance *appearance = [[UINavigationBarAppearance alloc] initWithIdiom:[[UIDevice currentDevice] userInterfaceIdiom]];
@ -1299,16 +1353,24 @@ static NSArray<NSString *> *NewsBlurTopSectionNames;
self.feedViewToolbar.barTintColor = [UINavigationBar appearance].barTintColor;
self.addBarButton.tintColor = UIColorFromRGB(0x8F918B);
self.settingsBarButton.tintColor = UIColorFromRGB(0x8F918B);
#if TARGET_OS_MACCATALYST
if (ThemeManager.themeManager.isLikeSystem) {
self.view.backgroundColor = UIColor.clearColor;
} else {
self.view.backgroundColor = UIColorFromRGB(0xf4f4f4);
}
#else
self.refreshControl.tintColor = UIColorFromLightDarkRGB(0x0, 0xffffff);
self.refreshControl.backgroundColor = UIColorFromRGB(0xE3E6E0);
self.view.backgroundColor = UIColorFromRGB(0xf4f4f4);
#endif
[[ThemeManager themeManager] updateSegmentedControl:self.intelligenceControl];
NBBarButtonItem *barButton = self.addBarButton.customView;
[barButton setImage:[[ThemeManager themeManager] themedImage:[UIImage imageNamed:@"nav_icn_add.png"]] forState:UIControlStateNormal];
self.settingsBarButton.image = [Utilities imageNamed:@"settings" sized:30];
self.settingsBarButton.image = [Utilities imageNamed:@"settings" sized:self.isMac ? 24 : 30];
[self layoutHeaderCounts:0];
[self refreshHeaderCounts];
@ -1327,6 +1389,7 @@ static NSArray<NSString *> *NewsBlurTopSectionNames;
}
self.feedTitlesTable.backgroundColor = UIColorFromRGB(0xf4f4f4);
[self reloadFeedTitlesTable];
[self resetupGestures];
@ -1682,7 +1745,7 @@ static NSArray<NSString *> *NewsBlurTopSectionNames;
- (CGFloat)calculateHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (appDelegate.hasNoSites) {
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!self.isPhone) {
return kBlurblogTableViewRowHeight;
} else {
return kPhoneBlurblogTableViewRowHeight;
@ -1726,13 +1789,13 @@ static NSArray<NSString *> *NewsBlurTopSectionNames;
if ([folderName isEqualToString:@"river_blurblogs"] ||
[folderName isEqualToString:@"river_global"]) { // blurblogs
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!self.isPhone) {
height = kBlurblogTableViewRowHeight;
} else {
height = kPhoneBlurblogTableViewRowHeight;
}
} else {
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!self.isPhone) {
height = kTableViewRowHeight;
} else {
height = kPhoneTableViewRowHeight;
@ -2386,6 +2449,10 @@ heightForHeaderInSection:(NSInteger)section {
hud.mode = MBProgressHUDModeText;
hud.removeFromSuperViewOnHide = YES;
if (!self.appDelegate.isPhone) {
hud.xOffset = 50;
}
NSIndexPath *topRow;
if ([[self.feedTitlesTable indexPathsForVisibleRows] count]) {
topRow = [[self.feedTitlesTable indexPathsForVisibleRows] objectAtIndex:0];
@ -2452,7 +2519,7 @@ heightForHeaderInSection:(NSInteger)section {
[hud hide:YES afterDelay:0.5];
[self showExplainerOnEmptyFeedlist];
// if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
// if (!self.isPhone) {
// FeedDetailViewController *storiesModule = self.appDelegate.dashboardViewController.storiesModule;
//
// storiesModule.storiesCollection.feedPage = 0;
@ -2681,15 +2748,19 @@ heightForHeaderInSection:(NSInteger)section {
#pragma mark -
#pragma mark PullToRefresh
#if !TARGET_OS_MACCATALYST
- (void)refresh:(UIRefreshControl *)refreshControl {
self.inPullToRefresh_ = YES;
[appDelegate reloadFeedsView:NO];
[appDelegate donateRefresh];
}
#endif
- (void)finishRefresh {
self.inPullToRefresh_ = NO;
#if !TARGET_OS_MACCATALYST
[self.refreshControl endRefreshing];
#endif
}
- (void)refreshFeedList {
@ -2814,6 +2885,11 @@ heightForHeaderInSection:(NSInteger)section {
});
}
//- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
// NSLog(@"canPerformAction: %@ withSender: %@", NSStringFromSelector(action, sender); // log
// return YES;
//}
- (void)resetToolbar {
// self.navigationItem.leftBarButtonItem = nil;
self.navigationItem.titleView = nil;
@ -2821,6 +2897,16 @@ heightForHeaderInSection:(NSInteger)section {
}
- (void)layoutHeaderCounts:(UIInterfaceOrientation)orientation {
#if TARGET_OS_MACCATALYST
int xOffset = 60;
int yOffset = 10;
[self.userInfoView removeFromSuperview];
self.userInfoView = [[UIView alloc]
initWithFrame:CGRectMake(0, 0, self.innerView.bounds.size.width, 50)];
self.userInfoView.backgroundColor = UIColorFromLightSepiaMediumDarkRGB(0xE0E0E0, 0xFFF8CA, 0x4F4F4F, 0x292B2C);
#else
if (!orientation) {
orientation = self.view.window.windowScene.interfaceOrientation;
}
@ -2831,22 +2917,33 @@ heightForHeaderInSection:(NSInteger)section {
isShort = YES;
}
int xOffset = 50;
int yOffset = isShort ? 0 : 6;
UIView *userInfoView = [[UIView alloc]
initWithFrame:CGRectMake(0, 0,
self.navigationController.navigationBar.frame.size.width,
self.navigationController.navigationBar.frame.size.height)];
self.userInfoView = [[UIView alloc]
initWithFrame:CGRectMake(0, 0,
self.navigationController.navigationBar.frame.size.width,
self.navigationController.navigationBar.frame.size.height)];
#endif
// adding user avatar to left
NSURL *imageURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@",
[appDelegate.dictSocialProfile
objectForKey:@"large_photo_url"]]];
userAvatarButton = [UIButton systemButtonWithImage:[UIImage imageNamed:@"user"]
target:self action:@selector((showUserProfile))];
target:self action:@selector(showUserProfile)];
userAvatarButton.pointerInteractionEnabled = YES;
userAvatarButton.accessibilityLabel = @"User info";
#if TARGET_OS_MACCATALYST
userAvatarButton.accessibilityHint = @"Double-click for information about your account.";
CGRect frame = userAvatarButton.frame;
userAvatarButton.frame = frame;
#else
userAvatarButton.accessibilityHint = @"Double-tap for information about your account.";
UIEdgeInsets insets = UIEdgeInsetsMake(0, -10, 10, 0);
userAvatarButton.contentEdgeInsets = insets;
#endif
// userAvatarButton.backgroundColor = UIColor.blueColor;
NSMutableURLRequest *avatarRequest = [NSMutableURLRequest requestWithURL:imageURL];
[avatarRequest addValue:@"image/*" forHTTPHeaderField:@"Accept"];
@ -2857,49 +2954,61 @@ heightForHeaderInSection:(NSInteger)section {
typeof(weakSelf) __strong strongSelf = weakSelf;
image = [Utilities roundCorneredImage:image radius:6 convertToSize:CGSizeMake(38, 38)];
image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
[(UIButton *)strongSelf.userAvatarButton setImage:image forState:UIControlStateNormal];
UIButton *button = strongSelf.userAvatarButton;
[button setImage:image forState:UIControlStateNormal];
} failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nonnull response, NSError * _Nonnull error) {
NSLog(@"Could not fetch user avatar: %@", error);
}];
[userInfoView addSubview:userAvatarButton];
[self.userInfoView addSubview:userAvatarButton];
userLabel = [[UILabel alloc] initWithFrame:CGRectMake(50, yOffset, userInfoView.frame.size.width, 16)];
userLabel = [[UILabel alloc] initWithFrame:CGRectMake(xOffset, yOffset, self.userInfoView.frame.size.width, 16)];
userLabel.text = appDelegate.activeUsername;
userLabel.font = userLabelFont;
userLabel.textColor = UIColorFromRGB(0x404040);
userLabel.backgroundColor = [UIColor clearColor];
userLabel.accessibilityLabel = [NSString stringWithFormat:@"Logged in as %@", appDelegate.activeUsername];
[userLabel sizeToFit];
[userInfoView addSubview:userLabel];
[self.userInfoView addSubview:userLabel];
[appDelegate.folderCountCache removeObjectForKey:@"everything"];
yellowIcon = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"g_icn_unread"]];
[userInfoView addSubview:yellowIcon];
[self.userInfoView addSubview:yellowIcon];
yellowIcon.hidden = YES;
neutralCount = [[UILabel alloc] init];
neutralCount.font = [UIFont fontWithName:@"WhitneySSm-Book" size:12];
neutralCount.textColor = UIColorFromRGB(0x707070);
neutralCount.backgroundColor = [UIColor clearColor];
[userInfoView addSubview:neutralCount];
[self.userInfoView addSubview:neutralCount];
greenIcon = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"g_icn_focus"]];
[userInfoView addSubview:greenIcon];
[self.userInfoView addSubview:greenIcon];
greenIcon.hidden = YES;
positiveCount = [[UILabel alloc] init];
positiveCount.font = [UIFont fontWithName:@"WhitneySSm-Book" size:12];
positiveCount.textColor = UIColorFromRGB(0x707070);
positiveCount.backgroundColor = [UIColor clearColor];
[userInfoView addSubview:positiveCount];
[self.userInfoView addSubview:positiveCount];
[userInfoView sizeToFit];
// self.userInfoView.backgroundColor = UIColor.blueColor;
// userInfoView.backgroundColor = UIColor.blueColor;
#if TARGET_OS_MACCATALYST
self.activityButton.frame = CGRectMake(self.innerView.bounds.size.width - 36, 10, 32, 32);
self.navigationItem.titleView = userInfoView;
[self.userInfoView addSubview:self.activityButton];
[self.innerView addSubview:self.userInfoView];
self.activityButton.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
self.userInfoView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
self.feedTitlesTopConstraint.constant = 50;
#else
[self.userInfoView sizeToFit];
self.navigationItem.titleView = self.userInfoView;
#endif
}
- (void)refreshHeaderCounts {
@ -2908,6 +3017,12 @@ heightForHeaderInSection:(NSInteger)section {
return;
}
#if TARGET_OS_MACCATALYST
int yOffset = 2;
#else
int yOffset = 0;
#endif
userAvatarButton.hidden = NO;
[appDelegate.folderCountCache removeObjectForKey:@"everything"];
@ -2924,13 +3039,13 @@ heightForHeaderInSection:(NSInteger)section {
yellowIcon.frame = CGRectMake(CGRectGetMinX(userLabel.frame), CGRectGetMaxY(userLabel.frame) + 4, 8, 8);
neutralCount.frame = CGRectMake(CGRectGetMaxX(yellowIcon.frame) + 2,
CGRectGetMinY(yellowIcon.frame) - 2, 100, 16);
CGRectGetMinY(yellowIcon.frame) - 2 - yOffset, 100, 16);
[neutralCount sizeToFit];
greenIcon.frame = CGRectMake(CGRectGetMaxX(neutralCount.frame) + 8,
CGRectGetMinY(yellowIcon.frame), 8, 8);
positiveCount.frame = CGRectMake(CGRectGetMaxX(greenIcon.frame) + 2,
CGRectGetMinY(greenIcon.frame) - 2, 100, 16);
CGRectGetMinY(greenIcon.frame) - 2 - yOffset, 100, 16);
[positiveCount sizeToFit];
yellowIcon.hidden = NO;

View file

@ -11,11 +11,8 @@
#import "NewsBlurAppDelegate.h"
#import "NewsBlur-Swift.h"
@interface FirstTimeUserAddFriendsViewController : BaseViewController {
NewsBlurAppDelegate *appDelegate;
}
@interface FirstTimeUserAddFriendsViewController : BaseViewController
@property (nonatomic) IBOutlet NewsBlurAppDelegate *appDelegate;
@property (nonatomic) IBOutlet UIBarButtonItem *nextButton;
@property (weak, nonatomic) IBOutlet UIButton *facebookButton;
@property (weak, nonatomic) IBOutlet UIButton *twitterButton;

View file

@ -16,7 +16,6 @@
@implementation FirstTimeUserAddFriendsViewController
@synthesize appDelegate;
@synthesize nextButton;
@synthesize facebookButton;
@synthesize twitterButton;
@ -36,8 +35,6 @@
- (void)viewDidLoad {
[super viewDidLoad];
self.appDelegate = [NewsBlurAppDelegate sharedAppDelegate];
UIBarButtonItem *next = [[UIBarButtonItem alloc] initWithTitle:@"Skip this step" style:UIBarButtonItemStyleDone target:self action:@selector(tapNextButton)];
self.nextButton = next;
self.navigationItem.rightBarButtonItem = next;
@ -53,7 +50,7 @@
//- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// // Return YES for supported orientations
// if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
// if (!self.isPhone) {
// return YES;
// } else if (UIInterfaceOrientationIsPortrait(interfaceOrientation)) {
// return YES;

View file

@ -9,11 +9,8 @@
#import <UIKit/UIKit.h>
#import "NewsBlurAppDelegate.h"
@interface FirstTimeUserAddNewsBlurViewController : BaseViewController {
NewsBlurAppDelegate *appDelegate;
}
@interface FirstTimeUserAddNewsBlurViewController : BaseViewController
@property (nonatomic) IBOutlet NewsBlurAppDelegate *appDelegate;
@property (nonatomic) IBOutlet UIBarButtonItem *nextButton;
@property (strong, nonatomic) IBOutlet UILabel *instructionsLabel;

View file

@ -11,7 +11,6 @@
@implementation FirstTimeUserAddNewsBlurViewController
@synthesize appDelegate;
@synthesize nextButton;
@synthesize instructionsLabel;
@ -27,8 +26,6 @@
- (void)viewDidLoad {
[super viewDidLoad];
self.appDelegate = [NewsBlurAppDelegate sharedAppDelegate];
UIBarButtonItem *next = [[UIBarButtonItem alloc] initWithTitle:@"Start reading" style:UIBarButtonItemStyleDone target:self action:@selector(tapNextButton)];
self.nextButton = next;
self.navigationItem.rightBarButtonItem = next;
@ -51,7 +48,7 @@
//- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// // Return YES for supported orientations
// if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
// if (!self.isPhone) {
// return YES;
// } else if (UIInterfaceOrientationIsPortrait(interfaceOrientation)) {
// return YES;

View file

@ -10,11 +10,8 @@
#import "NewsBlurAppDelegate.h"
@interface FirstTimeUserAddSitesViewController : BaseViewController
<UITableViewDataSource, UITableViewDelegate> {
NewsBlurAppDelegate *appDelegate;
}
<UITableViewDataSource, UITableViewDelegate>
@property (nonatomic) IBOutlet NewsBlurAppDelegate *appDelegate;
@property (nonatomic) IBOutlet UIButton *googleReaderButton;
@property (nonatomic) IBOutlet UIView *googleReaderButtonWrapper;
@property (nonatomic) IBOutlet UIBarButtonItem *nextButton;

View file

@ -24,7 +24,6 @@
@implementation FirstTimeUserAddSitesViewController
@synthesize appDelegate;
@synthesize googleReaderButton;
@synthesize nextButton;
@synthesize activityIndicator;
@ -50,8 +49,6 @@
[super viewDidLoad];
self.appDelegate = [NewsBlurAppDelegate sharedAppDelegate];
UIBarButtonItem *next = [[UIBarButtonItem alloc] initWithTitle:@"Next step" style:UIBarButtonItemStyleDone target:self action:@selector(tapNextButton)];
self.nextButton = next;
self.nextButton.enabled = YES;
@ -89,7 +86,7 @@
//- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// // Return YES for supported orientations
// if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
// if (!self.isPhone) {
// return YES;
// } else if (UIInterfaceOrientationIsPortrait(interfaceOrientation)) {
// return YES;

View file

@ -98,7 +98,7 @@
//- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// // Return YES for supported orientations
// if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
// if (!self.isPhone) {
// return YES;
// } else if (UIInterfaceOrientationIsPortrait(interfaceOrientation)) {
// return YES;

View file

@ -213,7 +213,7 @@
if (section == NewsBlurTopSectionInfrequentSiteStories) {
folderImage = [UIImage imageNamed:@"ak-icon-infrequent.png"];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!appDelegate.isPhone) {
folderImageViewX = 10;
} else {
folderImageViewX = 7;
@ -221,7 +221,7 @@
allowLongPress = YES;
} else if (section == NewsBlurTopSectionAllStories) {
folderImage = [UIImage imageNamed:@"all-stories"];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!appDelegate.isPhone) {
folderImageViewX = 10;
} else {
folderImageViewX = 7;
@ -229,42 +229,42 @@
allowLongPress = NO;
} else if ([folderName isEqual:@"river_global"]) {
folderImage = [UIImage imageNamed:@"global-shares"];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!appDelegate.isPhone) {
folderImageViewX = 10;
} else {
folderImageViewX = 8;
}
} else if ([folderName isEqual:@"river_blurblogs"]) {
folderImage = [UIImage imageNamed:@"all-shares"];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!appDelegate.isPhone) {
folderImageViewX = 10;
} else {
folderImageViewX = 8;
}
} else if ([folderName isEqual:@"saved_searches"]) {
folderImage = [UIImage imageNamed:@"search"];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!appDelegate.isPhone) {
folderImageViewX = 10;
} else {
folderImageViewX = 7;
}
} else if ([folderName isEqual:@"saved_stories"]) {
folderImage = [UIImage imageNamed:@"saved-stories"];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!appDelegate.isPhone) {
folderImageViewX = 10;
} else {
folderImageViewX = 7;
}
} else if ([folderName isEqual:@"read_stories"]) {
folderImage = [UIImage imageNamed:@"indicator-unread"];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!appDelegate.isPhone) {
folderImageViewX = 10;
} else {
folderImageViewX = 7;
}
} else if ([folderName isEqual:@"widget_stories"]) {
folderImage = [UIImage imageNamed:@"g_icn_folder_widget.png"];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!appDelegate.isPhone) {
folderImageViewX = 10;
} else {
folderImageViewX = 7;
@ -275,7 +275,7 @@
} else {
folderImage = [UIImage imageNamed:@"folder-open"];
}
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!appDelegate.isPhone) {
} else {
folderImageViewX = 7;
}

View file

@ -439,7 +439,9 @@
[self.fontSizeSegment setTitle:@"M" forSegmentAtIndex:2];
[self.fontSizeSegment setTitle:@"L" forSegmentAtIndex:3];
[self.fontSizeSegment setTitle:@"XL" forSegmentAtIndex:4];
#if !TARGET_OS_MACCATALYST
self.fontSizeSegment.backgroundColor = UIColorFromRGB(0xeeeeee);
#endif
[self.fontSizeSegment setTitleTextAttributes:@{NSFontAttributeName:[UIFont fontWithName:@"WhitneySSm-Medium" size:12.0f]} forState:UIControlStateNormal];
[self.fontSizeSegment setContentOffset:CGSizeMake(0, 1) forSegmentAtIndex:0];
[self.fontSizeSegment setContentOffset:CGSizeMake(0, 1) forSegmentAtIndex:1];
@ -467,7 +469,9 @@
[self.lineSpacingSegment setImage:[UIImage imageNamed:@"line_spacing_m"] forSegmentAtIndex:2];
[self.lineSpacingSegment setImage:[UIImage imageNamed:@"line_spacing_l"] forSegmentAtIndex:3];
[self.lineSpacingSegment setImage:[UIImage imageNamed:@"line_spacing_xl"] forSegmentAtIndex:4];
#if !TARGET_OS_MACCATALYST
self.lineSpacingSegment.backgroundColor = UIColorFromRGB(0xeeeeee);
#endif
[[ThemeManager themeManager] updateSegmentedControl:self.lineSpacingSegment];
@ -486,7 +490,9 @@
self.fullscreenSegment.frame = CGRectMake(8, 7, cell.frame.size.width - 8*2, kMenuOptionHeight - 7*2);
[self.fullscreenSegment setTitle:@"Full Screen" forSegmentAtIndex:0];
[self.fullscreenSegment setTitle:@"Toolbar" forSegmentAtIndex:1];
#if !TARGET_OS_MACCATALYST
self.fullscreenSegment.backgroundColor = UIColorFromRGB(0xeeeeee);
#endif
[self.fullscreenSegment setTitleTextAttributes:@{NSFontAttributeName:[UIFont fontWithName:@"WhitneySSm-Medium" size:12.0f]} forState:UIControlStateNormal];
[self.fullscreenSegment setContentOffset:CGSizeMake(0, 1) forSegmentAtIndex:0];
[self.fullscreenSegment setContentOffset:CGSizeMake(0, 1) forSegmentAtIndex:1];
@ -508,7 +514,9 @@
self.autoscrollSegment.frame = CGRectMake(8, 7, cell.frame.size.width - 8*2, kMenuOptionHeight - 7*2);
[self.autoscrollSegment setTitle:@"Manual scroll" forSegmentAtIndex:0];
[self.autoscrollSegment setTitle:@"Auto scroll" forSegmentAtIndex:1];
#if !TARGET_OS_MACCATALYST
self.autoscrollSegment.backgroundColor = UIColorFromRGB(0xeeeeee);
#endif
[self.autoscrollSegment setTitleTextAttributes:@{NSFontAttributeName:[UIFont fontWithName:@"WhitneySSm-Medium" size:12.0f]} forState:UIControlStateNormal];
[self.autoscrollSegment setContentOffset:CGSizeMake(0, 1) forSegmentAtIndex:0];
[self.autoscrollSegment setContentOffset:CGSizeMake(0, 1) forSegmentAtIndex:1];
@ -530,7 +538,9 @@
self.scrollOrientationSegment.frame = CGRectMake(8, 7, cell.frame.size.width - 8*2, kMenuOptionHeight - 7*2);
[self.scrollOrientationSegment setTitle:@"⏩ Horizontal" forSegmentAtIndex:0];
[self.scrollOrientationSegment setTitle:@"⏬ Vertical" forSegmentAtIndex:1];
#if !TARGET_OS_MACCATALYST
self.scrollOrientationSegment.backgroundColor = UIColorFromRGB(0xeeeeee);
#endif
[self.scrollOrientationSegment setTitleTextAttributes:@{NSFontAttributeName:[UIFont fontWithName:@"WhitneySSm-Medium" size:12.0f]} forState:UIControlStateNormal];
[self.scrollOrientationSegment setContentOffset:CGSizeMake(0, 1) forSegmentAtIndex:0];
[self.scrollOrientationSegment setContentOffset:CGSizeMake(0, 1) forSegmentAtIndex:1];
@ -566,7 +576,9 @@
[self.themeSegment setDividerImage:blankImage forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
self.themeSegment.tintColor = [UIColor clearColor];
#if !TARGET_OS_MACCATALYST
self.themeSegment.backgroundColor = [UIColor clearColor];
#endif
[[ThemeManager themeManager] updateThemeSegmentedControl:self.themeSegment];
@ -580,7 +592,14 @@
name = [name stringByAppendingString:@"-sel"];
}
return [[UIImage imageNamed:name] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
UIImage *image = [[UIImage imageNamed:name] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomMac) {
image = [Utilities imageWithImage:image convertToSize:CGSizeMake(20.0, 20.0)];
image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
}
return image;
}
@end

View file

@ -13,7 +13,6 @@
@class NewsBlurAppDelegate;
@interface FriendsListViewController : BaseViewController <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate> {
NewsBlurAppDelegate *appDelegate;
UISearchBar *friendSearchBar;
UITableView *friendsTable;
NSArray *suggestedUserProfiles;
@ -21,7 +20,6 @@
NSArray *userProfileIds;
}
@property (nonatomic) IBOutlet NewsBlurAppDelegate *appDelegate;
@property (nonatomic) IBOutlet UISearchBar *friendSearchBar;
@property (nonatomic) IBOutlet UITableView *friendsTable;

View file

@ -27,7 +27,6 @@
@implementation FriendsListViewController
@synthesize appDelegate;
@synthesize friendSearchBar;
@synthesize friendsTable;
@synthesize suggestedUserProfiles;
@ -45,8 +44,6 @@
{
[super viewDidLoad];
self.appDelegate = [NewsBlurAppDelegate sharedAppDelegate];
self.navigationItem.title = @"Find Friends";
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithTitle: @"Done"
style: UIBarButtonItemStylePlain
@ -156,7 +153,7 @@
// if (self.inSearch_){
// return 0;
// } else {
// if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad){
// if (!self.isPhone){
// return 28;
// }else{
// return 21;
@ -168,7 +165,7 @@
viewForHeaderInSection:(NSInteger)section {
int headerLabelHeight, folderImageViewY;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!self.isPhone) {
headerLabelHeight = 28;
folderImageViewY = 3;
} else {
@ -280,7 +277,7 @@ viewForHeaderInSection:(NSInteger)section {
// add a NO FRIENDS TO SUGGEST message on either the first or second row depending on iphone/ipad
int row = 0;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!self.isPhone) {
row = 1;
}

View file

@ -154,7 +154,7 @@
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger userInteractions = [appDelegate.userInteractionsArray count];
int minimumHeight;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!appDelegate.isPhone) {
minimumHeight = MINIMUM_INTERACTION_HEIGHT_IPAD;
} else {
minimumHeight = MINIMUM_INTERACTION_HEIGHT_IPHONE;
@ -165,7 +165,7 @@
}
InteractionCell *interactionCell;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!appDelegate.isPhone) {
interactionCell = [[InteractionCell alloc] init];
} else {
interactionCell = [[SmallInteractionCell alloc] init];
@ -190,7 +190,7 @@
InteractionCell *cell = [tableView
dequeueReusableCellWithIdentifier:@"InteractionCell"];
if (cell == nil) {
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!appDelegate.isPhone) {
cell = [[InteractionCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"InteractionCell"];
} else {
cell = [[SmallInteractionCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"InteractionCell"];
@ -276,7 +276,7 @@
UIImageView *fleuron = [[UIImageView alloc] initWithImage:img];
int height;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!appDelegate.isPhone) {
height = MINIMUM_INTERACTION_HEIGHT_IPAD;
} else {
height = MINIMUM_INTERACTION_HEIGHT_IPHONE;

View file

@ -12,8 +12,6 @@
#define LANDSCAPE_MARGIN 128
@interface LoginViewController : BaseViewController {
NewsBlurAppDelegate *appDelegate;
BOOL isOnSignUpScreen;
UITextField *usernameInput;
UITextField *passwordInput;
@ -46,8 +44,6 @@
- (void)animateLoop;
@property (nonatomic) IBOutlet NewsBlurAppDelegate *appDelegate;
@property (nonatomic) IBOutlet UITextField *usernameInput;
@property (nonatomic) IBOutlet UITextField *passwordInput;
@property (nonatomic) IBOutlet UITextField *emailInput;

View file

@ -12,7 +12,6 @@
@implementation LoginViewController
@synthesize appDelegate;
@synthesize usernameInput;
@synthesize passwordInput;
@synthesize emailInput;
@ -44,8 +43,6 @@
}
- (void)viewDidLoad {
self.appDelegate = NewsBlurAppDelegate.sharedAppDelegate;
self.usernameInput.borderStyle = UITextBorderStyleRoundedRect;
self.passwordInput.borderStyle = UITextBorderStyleRoundedRect;
self.emailInput.borderStyle = UITextBorderStyleRoundedRect;
@ -71,7 +68,7 @@
}
- (void)rearrangeViews {
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!self.isPhone) {
CGSize viewSize = self.view.bounds.size;
CGFloat viewWidth = viewSize.width;
CGFloat yOffset = 0;
@ -98,7 +95,7 @@
//- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// // Return YES for supported orientations
// if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
// if (!self.isPhone) {
// return YES;
// }
// return NO;
@ -108,7 +105,7 @@
[MBProgressHUD hideHUDForView:self.view animated:YES];
[super viewDidAppear:animated];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!self.isPhone) {
[self updateControls];
[self rearrangeViews];
}
@ -141,7 +138,7 @@
self.errorLabel.hidden = !hasError;
self.forgotPasswordButton.hidden = !hasError;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!self.isPhone) {
self.loginOptionalLabel.hidden = hasError;
}
}
@ -166,7 +163,7 @@
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!self.isPhone) {
if(textField == usernameInput) {
[passwordInput becomeFirstResponder];
} else if (textField == passwordInput) {
@ -244,7 +241,7 @@
setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways];
NSMutableDictionary *params = [NSMutableDictionary dictionary];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!self.isPhone) {
[params setObject:[signUpUsernameInput text] forKey:@"username"];
[params setObject:[signUpPasswordInput text] forKey:@"password"];
} else {

View file

@ -7,11 +7,12 @@
//
#import <UIKit/UIKit.h>
#import "BaseViewController.h"
typedef void (^MenuItemHandler)(void);
typedef void (^MenuItemSegmentedHandler)(NSUInteger selectedIndex);
@interface MenuViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
@interface MenuViewController : BaseViewController <UITableViewDataSource, UITableViewDelegate>
@property (weak) IBOutlet UITableView *menuTableView;
@ -29,5 +30,6 @@ typedef void (^MenuItemSegmentedHandler)(NSUInteger selectedIndex);
- (void)showFromNavigationController:(UINavigationController *)navigationController barButtonItem:(UIBarButtonItem *)barButtonItem;
- (void)showFromNavigationController:(UINavigationController *)navigationController barButtonItem:(UIBarButtonItem *)barButtonItem permittedArrowDirections:(UIPopoverArrowDirection)permittedArrowDirections;
- (void)showFromNavigationController:(UINavigationController *)navigationController barButtonItem:(UIBarButtonItem *)barButtonItem sourceView:(UIView *)sourceView sourceRect:(CGRect)sourceRect permittedArrowDirections:(UIPopoverArrowDirection)permittedArrowDirections;
@end

View file

@ -157,7 +157,14 @@ NSString * const MenuHandler = @"handler";
name = [name stringByAppendingString:@"-sel"];
}
return [[UIImage imageNamed:name] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
UIImage *image = [[UIImage imageNamed:name] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomMac) {
image = [Utilities imageWithImage:image convertToSize:CGSizeMake(20.0, 20.0)];
image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
}
return image;
}
- (UITableViewCell *)makeThemeSegmentedTableCell {
@ -197,7 +204,9 @@ NSString * const MenuHandler = @"handler";
[segmentedControl setDividerImage:blankImage forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
segmentedControl.tintColor = [UIColor clearColor];
#if !TARGET_OS_MACCATALYST
segmentedControl.backgroundColor = [UIColor clearColor];
#endif
segmentedControl.selectedSegmentIndex = valueIndex;
@ -243,7 +252,9 @@ NSString * const MenuHandler = @"handler";
segmentedControl.apportionsSegmentWidthsByContent = YES;
segmentedControl.selectedSegmentIndex = [item[MenuSegmentIndex] integerValue];
segmentedControl.tag = row;
#if !TARGET_OS_MACCATALYST
segmentedControl.backgroundColor = UIColorFromRGB(0xeeeeee);
#endif
[segmentedControl setTitleTextAttributes:@{NSFontAttributeName : [UIFont fontWithName:@"WhitneySSm-Medium" size:12.0]} forState:UIControlStateNormal];
[segmentedControl addTarget:self action:@selector(segmentedValueChanged:) forControlEvents:UIControlEventValueChanged];
@ -277,6 +288,10 @@ NSString * const MenuHandler = @"handler";
}
- (void)showFromNavigationController:(UINavigationController *)navigationController barButtonItem:(UIBarButtonItem *)barButtonItem permittedArrowDirections:(UIPopoverArrowDirection)permittedArrowDirections {
[self showFromNavigationController:navigationController barButtonItem:barButtonItem sourceView:nil sourceRect:CGRectZero permittedArrowDirections:permittedArrowDirections];
}
- (void)showFromNavigationController:(UINavigationController *)navigationController barButtonItem:(UIBarButtonItem *)barButtonItem sourceView:(UIView *)sourceView sourceRect:(CGRect)sourceRect permittedArrowDirections:(UIPopoverArrowDirection)permittedArrowDirections {
UIViewController *presentedViewController = navigationController.presentedViewController;
if (presentedViewController && presentedViewController.presentationController.presentationStyle == UIModalPresentationPopover) {
[presentedViewController dismissViewControllerAnimated:YES completion:nil];
@ -293,6 +308,8 @@ NSString * const MenuHandler = @"handler";
popoverPresentationController.backgroundColor = UIColorFromRGB(NEWSBLUR_WHITE_COLOR);
popoverPresentationController.permittedArrowDirections = permittedArrowDirections;
popoverPresentationController.barButtonItem = barButtonItem;
popoverPresentationController.sourceView = sourceView;
popoverPresentationController.sourceRect = sourceRect;
[navigationController presentViewController:embeddedNavController animated:YES completion:nil];
}

View file

@ -9,16 +9,12 @@
#import <UIKit/UIKit.h>
#import "NewsBlurAppDelegate.h"
@class NewsBlurAppDelegate;
@interface FolderTextField : UITextField
@end
@interface MoveSiteViewController : BaseViewController
<UITextFieldDelegate, UIPickerViewDelegate, UIPickerViewDataSource> {
NewsBlurAppDelegate *appDelegate;
}
<UITextFieldDelegate, UIPickerViewDelegate, UIPickerViewDataSource>
- (void)reload;
- (IBAction)moveSite;
@ -27,7 +23,6 @@
- (IBAction)doMoveButton;
- (NSArray *)pickerFolders;
@property (nonatomic) IBOutlet NewsBlurAppDelegate *appDelegate;
@property (nonatomic) IBOutlet UITextField *fromFolderInput;
@property (nonatomic) IBOutlet FolderTextField *toFolderInput;
@property (nonatomic) IBOutlet UILabel *titleLabel;

View file

@ -7,13 +7,11 @@
//
#import "MoveSiteViewController.h"
#import "NewsBlurAppDelegate.h"
#import "StringHelper.h"
#import "StoriesCollection.h"
@implementation MoveSiteViewController
@synthesize appDelegate;
@synthesize toFolderInput;
@synthesize fromFolderInput;
@synthesize titleLabel;
@ -34,8 +32,6 @@
}
- (void)viewDidLoad {
self.appDelegate = [NewsBlurAppDelegate sharedAppDelegate];
UIImageView *folderImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"folder-open"]];
folderImage.frame = CGRectMake(0, 0, 24, 16);
[folderImage setContentMode:UIViewContentModeRight];
@ -54,14 +50,12 @@
frame.size.height += 20;
self.navBar.frame = frame;
appDelegate = [NewsBlurAppDelegate sharedAppDelegate];
[super viewDidLoad];
}
//- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// // Return YES for supported orientations
// if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
// if (!self.isPhone) {
// return YES;
// } else if (UIInterfaceOrientationIsPortrait(interfaceOrientation)) {
// return YES;

View file

@ -287,7 +287,6 @@ SFSafariViewControllerDelegate> {
@property (nonatomic, readwrite) BOOL hasQueuedReadStories;
@property (nonatomic, readwrite) BOOL hasQueuedSavedStories;
@property (nonatomic, readonly) BOOL showingSafariViewController;
@property (nonatomic, readonly) BOOL isCompactWidth;
//@property (nonatomic) CGFloat compactWidth;
@property (nonatomic, strong) BGAppRefreshTask *backgroundAppRefreshTask;
@ -296,6 +295,9 @@ SFSafariViewControllerDelegate> {
- (void)registerDefaultsFromSettingsBundle;
- (void)finishBackground;
- (void)prepareViewControllers;
- (BOOL)openURL:(NSURL *)url;
- (void)showFirstTimeUser;
- (void)showLogin;
@ -395,7 +397,6 @@ SFSafariViewControllerDelegate> {
- (BOOL)isSavedStoriesIntelligenceMode;
- (NSArray *)allFeedIds;
- (NSArray *)feedIdsForFolderTitle:(NSString *)folderTitle;
- (BOOL)isPortrait;
- (void)confirmLogout;
- (void)showConnectToService:(NSString *)serviceName;
- (void)showAlert:(UIAlertController *)alert withViewController:(UIViewController *)vc;
@ -439,6 +440,7 @@ SFSafariViewControllerDelegate> {
- (void)renameFolder:(NSString *)newTitle;
- (void)showMarkReadMenuWithFeedIds:(NSArray *)feedIds collectionTitle:(NSString *)collectionTitle visibleUnreadCount:(NSInteger)visibleUnreadCount barButtonItem:(UIBarButtonItem *)barButtonItem completionHandler:(void (^)(BOOL marked))completionHandler;
- (void)showMarkReadMenuWithFeedIds:(NSArray *)feedIds collectionTitle:(NSString *)collectionTitle visibleUnreadCount:(NSInteger)visibleUnreadCount sourceView:(UIView *)sourceView sourceRect:(CGRect)sourceRect completionHandler:(void (^)(BOOL marked))completionHandler;
- (void)showMarkReadMenuWithFeedIds:(NSArray *)feedIds collectionTitle:(NSString *)collectionTitle sourceView:(UIView *)sourceView sourceRect:(CGRect)sourceRect completionHandler:(void (^)(BOOL marked))completionHandler;
- (void)showMarkOlderNewerReadMenuWithStoriesCollection:(StoriesCollection *)olderNewerCollection story:(NSDictionary *)olderNewerStory sourceView:(UIView *)sourceView sourceRect:(CGRect)sourceRect extraItems:(NSArray *)extraItems completionHandler:(void (^)(BOOL marked))completionHandler;
@ -446,6 +448,7 @@ SFSafariViewControllerDelegate> {
- (void)showPopoverWithViewController:(UIViewController *)viewController contentSize:(CGSize)contentSize barButtonItem:(UIBarButtonItem *)barButtonItem;
- (void)showPopoverWithViewController:(UIViewController *)viewController contentSize:(CGSize)contentSize sourceView:(UIView *)sourceView sourceRect:(CGRect)sourceRect;
- (void)showPopoverWithViewController:(UIViewController *)viewController contentSize:(CGSize)contentSize sourceView:(UIView *)sourceView sourceRect:(CGRect)sourceRect permittedArrowDirections:(UIPopoverArrowDirection)permittedArrowDirections;
- (void)hidePopoverAnimated:(BOOL)animated completion:(void (^)(void))completion;
- (BOOL)hidePopoverAnimated:(BOOL)animated;
- (void)hidePopover;

File diff suppressed because it is too large Load diff

View file

@ -13,11 +13,9 @@
@class NewsBlurAppDelegate;
@interface NotificationsViewController : BaseViewController <UITableViewDelegate, UITableViewDataSource> {
NewsBlurAppDelegate *appDelegate;
NSArray *notificationFeedIds;
}
@property (nonatomic) IBOutlet NewsBlurAppDelegate *appDelegate;
@property (nonatomic) IBOutlet UITableView *notificationsTable;
@property (nonatomic) NSString *feedId;

View file

@ -16,14 +16,11 @@
@implementation NotificationsViewController
@synthesize notificationsTable;
@synthesize appDelegate;
@synthesize feedId;
- (void)viewDidLoad {
[super viewDidLoad];
self.appDelegate = [NewsBlurAppDelegate sharedAppDelegate];
self.navigationItem.title = @"Notifications";
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithTitle: @"Done"
style: UIBarButtonItemStylePlain
@ -31,9 +28,6 @@
action: @selector(doCancelButton)];
[self.navigationItem setRightBarButtonItem:cancelButton];
// Do any additional setup after loading the view from its nib.
self.appDelegate = (NewsBlurAppDelegate *)[[UIApplication sharedApplication] delegate];
notificationsTable = [[UITableView alloc] init];
notificationsTable.delegate = self;
notificationsTable.dataSource = self;
@ -85,7 +79,7 @@
viewForHeaderInSection:(NSInteger)section {
int headerLabelHeight, folderImageViewY;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!self.isPhone) {
headerLabelHeight = 36;
folderImageViewY = 8;
} else {

View file

@ -10,13 +10,10 @@
#import "BaseViewController.h"
#import <WebKit/WebKit.h>
@class NewsBlurAppDelegate;
@interface OriginalStoryViewController : BaseViewController
<UITextFieldDelegate, WKNavigationDelegate, WKUIDelegate,
UIGestureRecognizerDelegate> {
NewsBlurAppDelegate *appDelegate;
NSString *activeUrl;
NSMutableArray *visitedUrls;
WKWebView *webView;
@ -27,7 +24,6 @@ UIGestureRecognizerDelegate> {
BOOL finishedLoading;
}
@property (nonatomic) IBOutlet NewsBlurAppDelegate *appDelegate;
@property (nonatomic) IBOutlet WKWebView *webView;
//@property (strong, nonatomic) SloppySwiper *swiper;
@property (nonatomic) UIProgressView *progressView;

View file

@ -17,7 +17,6 @@
@implementation OriginalStoryViewController
@synthesize appDelegate;
@synthesize webView;
//@synthesize swiper;
@synthesize progressView;
@ -25,14 +24,12 @@
- (void)viewDidLoad {
[super viewDidLoad];
self.appDelegate = [NewsBlurAppDelegate sharedAppDelegate];
self.view.layer.masksToBounds = NO;
self.view.layer.shadowRadius = 5;
self.view.layer.shadowOpacity = 0.5;
self.view.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.view.bounds].CGPath;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!self.isPhone) {
closeButton = [UIBarButtonItem barItemWithImage:[UIImage imageNamed:@"ios7_back_button"]
target:self
action:@selector(closeOriginalView)];
@ -70,7 +67,7 @@
// UIGestureRecognizer *themeGesture = [[ThemeManager themeManager] addThemeGestureRecognizerToView:self.webView];
// [self.webView.scrollView.panGestureRecognizer requireGestureRecognizerToFail:themeGesture];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!self.isPhone) {
UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc]
initWithTarget:self action:@selector(handlePanGesture:)];
gesture.delegate = self;
@ -215,7 +212,7 @@
center.y);
self.view.center = center;
[recognizer setTranslation:CGPointZero inView:self.view];
// if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
// if (!self.isPhone) {
// [appDelegate.masterContainerViewController interactiveTransitionFromOriginalView:percentage];
// } else {
//
@ -231,7 +228,7 @@
[self transitionToFeedDetail:recognizer];
} else {
// NSLog(@"Original velocity: %f (at %.2f%%)", velocity, percentage*100);
// if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
// if (!self.isPhone) {
// [appDelegate.masterContainerViewController transitionToOriginalView:NO];
// } else {
//
@ -241,7 +238,7 @@
}
- (void)transitionToFeedDetail:(UIGestureRecognizer *)recognizer {
// if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
// if (!self.isPhone) {
// [appDelegate.masterContainerViewController transitionFromOriginalView];
// } else {
//
@ -365,7 +362,7 @@
- (void)updateTitle:(WKWebView*)aWebView
{
if (self.customPageTitle != nil) {
if (self.customPageTitle.length > 0) {
titleView.text = self.customPageTitle;
} else {
NSString *pageTitleValue = webView.title;
@ -373,6 +370,10 @@
}
[titleView sizeToFit];
#if TARGET_OS_MACCATALYST
self.view.window.windowScene.title = titleView.text;
#endif
}
- (IBAction)loadAddress:(id)sender {

View file

@ -14,7 +14,6 @@
@interface PremiumViewController : BaseViewController <UITableViewDelegate, UITableViewDataSource>
@property (nonatomic) IBOutlet NewsBlurAppDelegate *appDelegate;
@property (nonatomic) IBOutlet UITableView *premiumTable;

View file

@ -24,8 +24,6 @@
- (void)viewDidLoad {
[super viewDidLoad];
self.appDelegate = [NewsBlurAppDelegate sharedAppDelegate];
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithTitle: @"Done"
style: UIBarButtonItemStylePlain
target: self

View file

@ -0,0 +1,66 @@
//
// SceneDelegate.swift
// NewsBlur
//
// Created by David Sinclair on 2023-11-15.
// Copyright © 2023 NewsBlur. All rights reserved.
//
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
let appDelegate: NewsBlurAppDelegate = .shared
var window: UIWindow?
#if targetEnvironment(macCatalyst)
var toolbar = NSToolbar(identifier: "main")
var toolbarDelegate = ToolbarDelegate()
#endif
@objc(closeAuxWindows) class func closeAuxWindows() {
for window in UIApplication.shared.windows {
if window.windowScene?.delegate is AuxSceneDelegate, let session = window.windowScene?.session {
window.isHidden = true
UIApplication.shared.requestSceneSessionDestruction(session, options: .none)
}
}
}
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
if appDelegate.window != nil {
DispatchQueue.main.async {
self.window?.isHidden = true
UIApplication.shared.requestSceneSessionDestruction(session, options: .none)
}
return
}
appDelegate.window = window
#if targetEnvironment(macCatalyst)
guard let windowScene = scene as? UIWindowScene, let titlebar = windowScene.titlebar else {
return
}
if #available(macCatalyst 16.0, *) {
windowScene.windowingBehaviors?.isClosable = false
}
toolbar.delegate = toolbarDelegate
toolbar.displayMode = .iconOnly
titlebar.toolbar = toolbar
titlebar.toolbarStyle = .automatic
#endif
appDelegate.prepareViewControllers()
}
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let url = URLContexts.first?.url else {
return
}
appDelegate.open(url)
}
}

View file

@ -15,7 +15,6 @@
}
@property (nonatomic) IBOutlet UITextView *commentField;
@property (nonatomic) IBOutlet NewsBlurAppDelegate *appDelegate;
@property (nonatomic) IBOutlet UIButton *facebookButton;
@property (nonatomic) IBOutlet UIButton *twitterButton;
@property (nonatomic) IBOutlet UIBarButtonItem *submitButton;

View file

@ -21,7 +21,6 @@
@synthesize twitterButton;
@synthesize submitButton;
@synthesize commentField;
@synthesize appDelegate;
@synthesize activeReplyId;
@synthesize activeCommentId;
@synthesize activeStoryId;
@ -38,9 +37,7 @@
}
- (void)viewDidLoad {
self.appDelegate = [NewsBlurAppDelegate sharedAppDelegate];
[[NSNotificationCenter defaultCenter]
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(onTextChange:)
name:UITextViewTextDidChangeNotification
@ -202,7 +199,7 @@
self.storyTitle.frame = CGRectMake(20, 8, v.width - 20*2, 24);
stOffset = self.storyTitle.frame.origin.y + self.storyTitle.frame.size.height;
stHeight = self.storyTitle.frame.size.height;
} else if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
} else if (!self.isPhone) {
k = 0;
}
NSLog(@"Share type: %@", self.currentType);

View file

@ -9,6 +9,7 @@
#import "SmallActivityCell.h"
#import "UIImageView+AFNetworking.h"
#import <QuartzCore/QuartzCore.h>
#import "NewsBlurAppDelegate.h"
@implementation SmallActivityCell
@ -62,7 +63,7 @@
labelFrame.size.height = contentRect.size.height;
self.activityLabel.frame = labelFrame;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!((NewsBlurAppDelegate *)[[UIApplication sharedApplication] delegate]).isPhone) {
self.activityLabel.backgroundColor = UIColorFromRGB(0xd7dadf);
} else {
self.activityLabel.backgroundColor = UIColorFromRGB(0xf6f6f6);

View file

@ -9,6 +9,7 @@
#import "SmallInteractionCell.h"
#import "UIImageView+AFNetworking.h"
#import <QuartzCore/QuartzCore.h>
#import "NewsBlurAppDelegate.h"
@implementation SmallInteractionCell
@ -57,8 +58,8 @@
labelFrame.size.width = contentRect.size.width - leftMargin - avatarSize - leftMargin - rightMargin - 20;
labelFrame.size.height = contentRect.size.height;
self.interactionLabel.frame = labelFrame;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!((NewsBlurAppDelegate *)[[UIApplication sharedApplication] delegate]).isPhone) {
self.interactionLabel.backgroundColor = UIColorFromRGB(0xd7dadf);
} else {
self.interactionLabel.backgroundColor = UIColorFromRGB(0xf6f6f6);

View file

@ -10,6 +10,10 @@ import UIKit
/// Subclass of `UISplitViewController` to enable customizations.
class SplitViewController: UISplitViewController {
@objc var isFeedListHidden: Bool {
return [.oneBesideSecondary, .oneOverSecondary, .secondaryOnly].contains(displayMode)
}
/// Update the theme of the split view controller.
@objc func updateTheme() {
@ -24,4 +28,10 @@ class SplitViewController: UISplitViewController {
override var childForStatusBarStyle: UIViewController? {
return nil
}
// Can do menu validation here.
// override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
// print("canPerformAction: \(action) with \(sender ?? "nil")")
// return true
// }
}

View file

@ -59,10 +59,15 @@
@property (nonatomic, readwrite) BOOL transferredFromDashboard;
@property (nonatomic, readwrite) BOOL showHiddenStories;
@property (nonatomic, readwrite) BOOL inSearch;
@property (nonatomic, readonly) BOOL isEverything;
@property (nonatomic, readonly) BOOL isInfrequent;
@property (nonatomic, readonly) BOOL isRiverOrSocial;
@property (nonatomic, readonly) BOOL isCustomFolder;
@property (nonatomic, readonly) BOOL isCustomFolderOrFeed;
@property (nonatomic) NSString *searchQuery;
@property (nonatomic) NSString *savedSearchQuery;
@property (nonatomic, readonly) NSString *activeFeedIdStr;
@property (nonatomic, readonly) NSString *activeOrder;
@property (nonatomic, readonly) NSString *activeReadFilter;
@property (nonatomic, readonly) NSString *activeStoryTitlesPosition;

View file

@ -97,10 +97,26 @@
self.savedSearchQuery = fromCollection.savedSearchQuery;
}
- (BOOL)isEverything {
return [activeFolder isEqualToString:@"everything"];
}
- (BOOL)isInfrequent {
return [activeFolder isEqualToString:@"infrequent"];
}
- (BOOL)isRiverOrSocial {
return self.isRiverView || self.isSavedView || self.isReadView || self.isWidgetView || self.isSocialView || self.isSocialRiverView;
}
- (BOOL)isCustomFolder {
return self.isRiverView && !self.isEverything && !self.isInfrequent && !self.isSavedView && !self.isReadView && !self.isSocialView && !self.isWidgetView;
}
- (BOOL)isCustomFolderOrFeed {
return !self.isRiverView || self.isCustomFolder;
}
#pragma mark - Story Traversal
- (BOOL)isStoryUnread:(NSDictionary *)story {
@ -232,6 +248,10 @@
return [[activeFeedStoryLocations objectAtIndex:location] intValue];
}
- (NSString *)activeFeedIdStr {
return [NSString stringWithFormat:@"%@", [activeFeed objectForKey:@"id"]];
}
- (NSString *)activeOrder {
NSUserDefaults *userPreferences = [NSUserDefaults standardUserDefaults];
NSString *orderPrefDefault = [userPreferences stringForKey:@"default_order"];

View file

@ -8,17 +8,17 @@
import Foundation
// The Story and StoryCache classes could be quite useful going forward; Rather than calling getStory() to get the dictionary, could have a variation that returns a Story instance. Could fetch from the cache if available, or make and cache one from the dictionary. Would need to remove it from the cache when changing anything about a story. Could perhaps make the cache part of StoriesCollection.
// The Feed, Story, and StoryCache classes could be quite useful going forward; Rather than calling getStory() to get the dictionary, could have a variation that returns a Story instance. Could fetch from the cache if available, or make and cache one from the dictionary. Would need to remove it from the cache when changing anything about a story. Could perhaps make the cache part of StoriesCollection.
/// A story, wrapping the dictionary representation.
class Story: Identifiable {
let id = UUID()
let index: Int
var dictionary = [String : Any]()
var dictionary = AnyDictionary()
var feed: Feed?
var feedID = ""
var feedName = ""
var title = ""
var content = ""
var dateString = ""
@ -35,9 +35,34 @@ class Story: Identifiable {
return author.isEmpty ? dateString : "\(dateString) · \(author)"
}
var isRiverOrSocial = true
var feedColorBarLeft: UIColor?
var feedColorBarRight: UIColor?
var titles: [Feed.Training] {
guard let classifiers = feed?.classifiers(for: "titles") else {
return []
}
let lowercasedTitle = title.lowercased()
let keys = classifiers.keys.compactMap { $0 as? String }
let words = keys.filter { lowercasedTitle.contains($0.lowercased()) }
let sorted = words.sorted()
return sorted.map { Feed.Training(name: $0, count: 0, score: Feed.Score(rawValue: classifiers[$0] as? Int ?? 0) ?? .none) }
}
var authors: [Feed.Training] {
guard let classifiers = feed?.classifiers(for: "authors") else {
return []
}
return [Feed.Training(name: author, count: 0, score: Feed.Score(rawValue: classifiers[author] as? Int ?? 0) ?? .none)]
}
var tags: [Feed.Training] {
guard let tags = dictionary["story_tags"] as? [String], let classifiers = feed?.classifiers(for: "tags") else {
return []
}
return tags.map { Feed.Training(name: $0, count: 0, score: Feed.Score(rawValue: classifiers[$0] as? Int ?? 0) ?? .none) }
}
var isSelected: Bool {
return index == NewsBlurAppDelegate.shared!.storiesCollection.locationOfActiveStory()
@ -79,24 +104,13 @@ class Story: Identifiable {
dictionary = story
if let id = dictionary["story_feed_id"] {
feedID = appDelegate.feedIdWithoutSearchQuery("\(id)")
}
var feed: [String : Any]?
if storiesCollection.isRiverOrSocial {
feed = appDelegate.dictActiveFeeds[feedID] as? [String : Any]
}
if feed == nil {
feed = appDelegate.dictFeeds[feedID] as? [String : Any]
}
if let feed {
feedName = feed["feed_title"] as? String ?? ""
feedColorBarLeft = color(for: "favicon_fade", from: feed, default: "707070")
feedColorBarRight = color(for: "favicon_color", from: feed, default: "505050")
if let dictID = dictionary["story_feed_id"], let id = appDelegate.feedIdWithoutSearchQuery("\(dictID)") {
if let cachedFeed = StoryCache.feeds[id] {
feed = cachedFeed
} else {
feed = Feed(id: id)
StoryCache.feeds[id] = feed
}
}
title = (string(for: "story_title") as NSString).decodingHTMLEntities()
@ -114,17 +128,6 @@ class Story: Identifiable {
isRead = !storiesCollection .isStoryUnread(dictionary)
isReadAvailable = storiesCollection.activeFolder != "saved_stories"
isRiverOrSocial = storiesCollection.isRiverOrSocial
}
func color(for key: String, from feed: [String : Any], default defaultHex: String) -> UIColor {
let hex = feed[key] as? String ?? defaultHex
let scanner = Scanner(string: hex)
var color: Int64 = 0
scanner.scanHexInt64(&color)
let value = Int(color)
return ThemeManager.shared.fixedColor(fromRGB: value) ?? UIColor.gray
}
}
@ -136,235 +139,6 @@ extension Story: Equatable {
extension Story: CustomDebugStringConvertible {
var debugDescription: String {
return "Story #\(index) \"\(title)\" in \(feedName)"
}
}
/// A cache of stories for the feed detail grid view.
class StoryCache: ObservableObject {
let appDelegate = NewsBlurAppDelegate.shared!
let settings = StorySettings()
var isDarkTheme: Bool {
return ThemeManager.shared.isDarkTheme
}
var isGrid: Bool {
return appDelegate.detailViewController.layout == .grid
}
var isPhone: Bool {
return appDelegate.detailViewController.isPhone
}
var canPullToRefresh: Bool {
return appDelegate.feedDetailViewController.canPullToRefresh
}
@Published var before = [Story]()
@Published var selected: Story?
@Published var after = [Story]()
var all: [Story] {
if let selected {
return before + [selected] + after
} else {
return before + after
}
}
func story(with index: Int) -> Story? {
return all.first(where: { $0.index == index } )
}
func reload() {
let storyCount = Int(appDelegate.storiesCollection.storyLocationsCount)
var beforeSelection = [Int]()
var selectedIndex = -999
var afterSelection = [Int]()
if storyCount > 0 {
selectedIndex = appDelegate.storiesCollection.locationOfActiveStory()
if selectedIndex < 0 {
beforeSelection = Array(0..<storyCount)
} else {
beforeSelection = Array(0..<selectedIndex)
if selectedIndex + 1 < storyCount {
afterSelection = Array(selectedIndex + 1..<storyCount)
}
}
}
before = beforeSelection.map { Story(index: $0) }
selected = selectedIndex >= 0 ? Story(index: selectedIndex) : nil
after = afterSelection.map { Story(index: $0) }
print("🪿 Reload: \(before.count) before, \(selected == nil ? "none" : selected!.debugTitle) selected, \(after.count) after")
//
// #warning("hack")
//
// print("🪿 ... count: \(storyCount), index: \(selectedIndex)")
// print("🪿 ... before: \(before)")
// print("🪿 ... selection: \(selected == nil ? "none" : selected!.debugTitle)")
// print("🪿 ... after: \(after)")
}
func reload(story: Story) {
if story == selected {
selected = Story(index: story.index)
} else if let index = before.firstIndex(of: story) {
before[index] = Story(index: story.index)
} else if let index = after.firstIndex(of: story) {
after[index] = Story(index: story.index)
}
}
}
class StorySettings {
let defaults = UserDefaults.standard
enum Content: String, RawRepresentable {
case title
case short
case medium
case long
static let titleLimit = 6
static let contentLimit = 10
var limit: Int {
switch self {
case .title:
return 6
case .short:
return 2
case .medium:
return 4
case .long:
return 6
}
}
}
var content: Content {
if let string = defaults.string(forKey: "story_list_preview_text_size"), let value = Content(rawValue: string) {
return value
} else {
return .short
}
}
enum Preview: String, RawRepresentable {
case none
case smallLeft = "small_left"
case largeLeft = "large_left"
case largeRight = "large_right"
case smallRight = "small_right"
var isLeft: Bool {
return [.smallLeft, .largeLeft].contains(self)
}
var isSmall: Bool {
return [.smallLeft, .smallRight].contains(self)
}
}
var preview: Preview {
if let string = defaults.string(forKey: "story_list_preview_images_size"), let value = Preview(rawValue: string) {
return value
} else {
return .smallRight
}
}
enum FontSize: String, RawRepresentable {
case xs
case small
case medium
case large
case xl
var offset: CGFloat {
switch self {
case .xs:
return -2
case .small:
return -1
case .medium:
return 0
case .large:
return 1
case .xl:
return 2
}
}
}
var fontSize: FontSize {
if let string = defaults.string(forKey: "feed_list_font_size"), let value = FontSize(rawValue: string) {
return value
} else {
return .medium
}
}
enum Spacing: String, RawRepresentable {
case compact
case comfortable
}
var spacing: Spacing {
if let string = defaults.string(forKey: "feed_list_spacing"), let value = Spacing(rawValue: string) {
return value
} else {
return .comfortable
}
}
var gridColumns: Int {
guard let pref = UserDefaults.standard.string(forKey: "grid_columns"), let columns = Int(pref) else {
if NewsBlurAppDelegate.shared.isCompactWidth {
return 1
} else if NewsBlurAppDelegate.shared.isPortrait() {
return 2
} else {
return 4
}
}
if NewsBlurAppDelegate.shared.isPortrait(), columns > 3 {
return 3
}
return columns
}
var gridHeight: CGFloat {
guard let pref = UserDefaults.standard.string(forKey: "grid_height") else {
return 400
}
switch pref {
case "xs":
return 250
case "short":
return 300
case "tall":
return 500
case "xl":
return 600
default:
return 400
}
return "Story #\(index) \"\(title)\" in \(feed?.name ?? "<none>")"
}
}

View file

@ -0,0 +1,114 @@
//
// StoryCache.swift
// NewsBlur
//
// Created by David Sinclair on 2024-04-04.
// Copyright © 2024 NewsBlur. All rights reserved.
//
import Foundation
// The Feed, Story, and StoryCache classes could be quite useful going forward; Rather than calling getStory() to get the dictionary, could have a variation that returns a Story instance. Could fetch from the cache if available, or make and cache one from the dictionary. Would need to remove it from the cache when changing anything about a story. Could perhaps make the cache part of StoriesCollection.
/// A cache of stories for the feed detail grid view.
class StoryCache: ObservableObject {
let appDelegate = NewsBlurAppDelegate.shared!
let settings = StorySettings()
var isDarkTheme: Bool {
return ThemeManager.shared.isDarkTheme
}
var isGrid: Bool {
return appDelegate.detailViewController.layout == .grid
}
var isPhone: Bool {
return appDelegate.detailViewController.isPhone
}
var canPullToRefresh: Bool {
return appDelegate.feedDetailViewController.canPullToRefresh
}
@Published var before = [Story]()
@Published var selected: Story?
@Published var after = [Story]()
var all: [Story] {
if let selected {
return before + [selected] + after
} else {
return before + after
}
}
func story(with index: Int) -> Story? {
return all.first(where: { $0.index == index } )
}
static var feeds = [String : Feed]()
var currentFeed: Feed?
func reload() {
let debug = Date()
let storyCount = Int(appDelegate.storiesCollection.storyLocationsCount)
var beforeSelection = [Int]()
var selectedIndex = -999
var afterSelection = [Int]()
if storyCount > 0 {
selectedIndex = appDelegate.storiesCollection.locationOfActiveStory()
if selectedIndex < 0 {
beforeSelection = Array(0..<storyCount)
} else {
beforeSelection = Array(0..<selectedIndex)
if selectedIndex + 1 < storyCount {
afterSelection = Array(selectedIndex + 1..<storyCount)
}
}
}
Self.feeds.removeAll()
if let dictionary = appDelegate.storiesCollection.activeFeed {
let feed = Feed(dictionary: dictionary)
Self.feeds[feed.id] = feed
currentFeed = feed
} else {
currentFeed = nil
}
before = beforeSelection.map { Story(index: $0) }
selected = selectedIndex >= 0 ? Story(index: selectedIndex) : nil
after = afterSelection.map { Story(index: $0) }
print("🪿 Reload: \(before.count) before, \(selected == nil ? "none" : selected!.debugTitle) selected, \(after.count) after, took \(-debug.timeIntervalSinceNow) seconds")
//
// #warning("hack")
//
// print("🪿 ... count: \(storyCount), index: \(selectedIndex)")
// print("🪿 ... before: \(before)")
// print("🪿 ... selection: \(selected == nil ? "none" : selected!.debugTitle)")
// print("🪿 ... after: \(after)")
}
func reload(story: Story) {
if story == selected {
selected = Story(index: story.index)
} else if let index = before.firstIndex(of: story) {
before[index] = Story(index: story.index)
} else if let index = after.firstIndex(of: story) {
after[index] = Story(index: story.index)
}
}
}

View file

@ -11,13 +11,9 @@
#import "BaseViewController.h"
@import WebKit;
@class NewsBlurAppDelegate;
@interface StoryDetailObjCViewController : BaseViewController
<UIScrollViewDelegate, UIGestureRecognizerDelegate,
UIActionSheetDelegate, WKNavigationDelegate> {
NewsBlurAppDelegate *appDelegate;
NSString *activeStoryId;
NSMutableDictionary *activeStory;
UIView *innerView;
@ -34,7 +30,6 @@ UIActionSheetDelegate, WKNavigationDelegate> {
UIInterfaceOrientation _orientation;
}
@property (nonatomic) IBOutlet NewsBlurAppDelegate *appDelegate;
@property (nonatomic) NSString *activeStoryId;
@property (nonatomic, readwrite) NSMutableDictionary *activeStory;
@property (nonatomic) IBOutlet UIView *innerView;

View file

@ -24,8 +24,8 @@
#import "JNWThrottledBlock.h"
#import "NewsBlur-Swift.h"
#define iPadPro12 ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad && ([UIScreen mainScreen].bounds.size.height == 1366 || [UIScreen mainScreen].bounds.size.width == 1366))
#define iPadPro10 ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad && ([UIScreen mainScreen].bounds.size.height == 1112 || [UIScreen mainScreen].bounds.size.width == 1112))
#define iPadPro12 (!self.isPhone && ([UIScreen mainScreen].bounds.size.height == 1366 || [UIScreen mainScreen].bounds.size.width == 1366))
#define iPadPro10 (!self.isPhone && ([UIScreen mainScreen].bounds.size.height == 1112 || [UIScreen mainScreen].bounds.size.width == 1112))
@interface StoryDetailObjCViewController ()
@ -35,7 +35,6 @@
@implementation StoryDetailObjCViewController
@synthesize appDelegate;
@synthesize activeStoryId;
@synthesize activeStory;
@synthesize innerView;
@ -71,8 +70,6 @@
- (void)viewDidLoad {
[super viewDidLoad];
self.appDelegate = [NewsBlurAppDelegate sharedAppDelegate];
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
@ -99,7 +96,7 @@
[self.webView.scrollView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight)];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!self.isPhone) {
self.webView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
@ -314,6 +311,11 @@
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
#if TARGET_OS_MACCATALYST
[self.navigationController setNavigationBarHidden:YES animated:animated];
[self.navigationController setToolbarHidden:YES animated:animated];
#endif
if (!self.isPhoneOrCompact) {
[appDelegate.feedDetailViewController.view endEditing:YES];
}
@ -401,9 +403,13 @@
static NSURL *baseURL;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
#if TARGET_OS_MACCATALYST
baseURL = [NSBundle mainBundle].resourceURL;
#else
baseURL = [NSBundle mainBundle].bundleURL;
#endif
});
[self.webView loadHTMLString:html baseURL:baseURL];
}
@ -480,7 +486,7 @@
#if TARGET_OS_MACCATALYST
// CATALYST: probably will want to add custom CSS for Macs.
contentWidthClass = @"NB-ipad-wide NB-ipad-pro-12-wide NB-width-768";
contentWidthClass = @"NB-mac NB-ipad-pro-12-wide";
#else
if (UIInterfaceOrientationIsLandscape(orientation) && !self.isPhoneOrCompact) {
if (iPadPro12) {
@ -503,14 +509,15 @@
} else {
contentWidthClass = @"NB-iphone";
}
#endif
contentWidthClass = [NSString stringWithFormat:@"%@ NB-width-%d",
contentWidthClass, (int)floorf(CGRectGetWidth(self.view.frame))];
#endif
if (appDelegate.feedsViewController.isOffline) {
// if (appDelegate.feedsViewController.isOffline) {
NSString *storyHash = [self.activeStory objectForKey:@"story_hash"];
NSArray *imageUrls = [appDelegate.activeCachedImages objectForKey:storyHash];
// NSLog(@"📚 imageUrls: %@", imageUrls);
if (imageUrls) {
NSString *storyImagesDirectory = [appDelegate.documentsURL.path
stringByAppendingPathComponent:@"story_images"];
@ -524,7 +531,7 @@
withString:cachedUrl.absoluteString];
}
}
}
// }
NSString *feedIdStr = [NSString stringWithFormat:@"%@",
[self.activeStory
@ -614,7 +621,7 @@
NSString *htmlTopAndBottom = [htmlTop stringByAppendingString:htmlBottom];
// NSLog(@"\n\n\n\nStory html (%@):\n\n\n%@\n\n\n", self.activeStory[@"story_title"], htmlContent);
// NSLog(@"\n\n\n\nStory html (%@):\n\n\n%@\n\n\n", self.activeStory[@"story_title"], htmlContent);
self.hasStory = NO;
self.fullStoryHTML = htmlContent;
@ -1399,9 +1406,11 @@
int bottomPosition = webpageHeight - topPosition - viewportHeight;
BOOL singlePage = webpageHeight - 200 <= viewportHeight;
BOOL atBottom = bottomPosition < 150;
BOOL pullingDown = topPosition < 0;
BOOL atTop = topPosition < 50;
#if !TARGET_OS_MACCATALYST
BOOL pullingDown = topPosition < 0;
BOOL nearTop = topPosition < 100;
#endif
if (!hasScrolled && topPosition != 0) {
hasScrolled = YES;
@ -1417,6 +1426,7 @@
}
}
#if !TARGET_OS_MACCATALYST
if (!isNavBarHidden && self.canHideNavigationBar && !nearTop) {
[appDelegate.storyPagesViewController setNavigationBarHidden:YES];
}
@ -1424,6 +1434,7 @@
if (isNavBarHidden && pullingDown) {
[appDelegate.storyPagesViewController setNavigationBarHidden:NO];
}
#endif
if (!atTop && !atBottom && !singlePage) {
BOOL traversalVisible = appDelegate.storyPagesViewController.traverseView.alpha > 0;
@ -1878,6 +1889,12 @@
[self scrollToLastPosition:YES];
}
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView {
NSLog(@"Web content process did terminate: %@", webView); // log
[self drawStory];
}
- (void)checkTryFeedStory {
// see if it's a tryfeed for animation
if (!self.webView.hidden &&
@ -2241,9 +2258,11 @@
}
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
if ([self respondsToSelector:action])
return self.noStoryMessage.hidden;
return [super canPerformAction:action withSender:sender];
if ([self respondsToSelector:action]) {
return [super canPerformAction:action withSender:sender] && self.noStoryMessage.hidden;
} else {
return [super canPerformAction:action withSender:sender];
}
}
# pragma mark -
@ -2409,7 +2428,7 @@
#if TARGET_OS_MACCATALYST
// CATALYST: probably will want to add custom CSS for Macs.
contentWidthClass = @"NB-ipad-wide NB-ipad-pro-12-wide NB-width-768";
contentWidthClass = @"NB-mac NB-ipad-pro-12-wide";
#else
UIInterfaceOrientation orientation = self.view.window.windowScene.interfaceOrientation;
@ -2434,10 +2453,10 @@
} else {
contentWidthClass = @"NB-iphone";
}
#endif
contentWidthClass = [NSString stringWithFormat:@"%@ NB-width-%d",
contentWidthClass, (int)floorf(CGRectGetWidth(webView.scrollView.bounds))];
#endif
NSString *alternateViewClass = @"";
if (!self.isPhoneOrCompact) {

View file

@ -16,7 +16,6 @@
@interface StoryPagesObjCViewController : BaseViewController
<UIScrollViewDelegate, UIPopoverControllerDelegate, UIPopoverPresentationControllerDelegate, UIGestureRecognizerDelegate> {
NewsBlurAppDelegate *appDelegate;
THCircularProgressView *circularProgressView;
UIButton *buttonPrevious;
UIButton *buttonNext;
@ -37,7 +36,6 @@
CGFloat scrollPct;
}
@property (nonatomic, strong) NewsBlurAppDelegate *appDelegate;
@property (nonatomic) StoryDetailViewController *currentPage;
@property (nonatomic) StoryDetailViewController *nextPage;
@property (nonatomic) StoryDetailViewController *previousPage;
@ -153,9 +151,14 @@
- (IBAction)openSendToDialog:(id)sender;
- (IBAction)doNextUnreadStory:(id)sender;
- (IBAction)doPreviousStory:(id)sender;
- (void)changeToNextPage:(id)sender;
- (void)changeToPreviousPage:(id)sender;
- (IBAction)tapProgressBar:(id)sender;
- (IBAction)toggleTextView:(id)sender;
- (IBAction)toggleStorySaved:(id)sender;
- (IBAction)toggleStoryUnread:(id)sender;
- (void)finishMarkAsSaved:(NSDictionary *)params;
- (BOOL)failedMarkAsSaved:(NSDictionary *)params;
- (void)finishMarkAsUnsaved:(NSDictionary *)params;

View file

@ -36,7 +36,6 @@
@implementation StoryPagesObjCViewController
@synthesize appDelegate;
@synthesize currentPage, nextPage, previousPage;
@synthesize circularProgressView;
@synthesize separatorBarButton;
@ -76,7 +75,6 @@
- (void)viewDidLoad {
[super viewDidLoad];
appDelegate = [NewsBlurAppDelegate sharedAppDelegate];
currentPage = [[StoryDetailViewController alloc]
initWithNibName:@"StoryDetailViewController"
bundle:nil];
@ -109,7 +107,11 @@
[self.scrollView setAlwaysBounceHorizontal:self.isHorizontal];
[self.scrollView setAlwaysBounceVertical:!self.isHorizontal];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (@available(macCatalyst 17.0, *)) {
self.scrollView.allowsKeyboardScrolling = NO;
}
if (!self.isPhone) {
self.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
@ -172,13 +174,13 @@
[separatorBarButton setEnabled:NO];
separatorBarButton.isAccessibilityElement = NO;
UIImage *settingsImage = [Utilities imageNamed:@"settings" sized:30];
UIImage *settingsImage = [Utilities imageNamed:@"settings" sized:self.isMac ? 24 : 30];
fontSettingsButton = [UIBarButtonItem barItemWithImage:settingsImage
target:self
action:@selector(toggleFontSize:)];
fontSettingsButton.accessibilityLabel = @"Story settings";
UIImage *markreadImage = [UIImage imageNamed:@"original_button.png"];
UIImage *markreadImage = [Utilities imageNamed:@"original_button.png" sized:self.isMac ? 24 : 30];
originalStoryButton = [UIBarButtonItem barItemWithImage:markreadImage
target:self
action:@selector(showOriginalSubview:)];
@ -251,6 +253,11 @@
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
#if TARGET_OS_MACCATALYST
[self.navigationController setNavigationBarHidden:YES animated:animated];
[self.navigationController setToolbarHidden:YES animated:animated];
#endif
[self updateTheme];
[self updateAutoscrollButtons];
@ -374,6 +381,10 @@
self.scrollView.frame = CGRectMake(frame.origin.x, frame.origin.y, floor(frame.size.width), floor(frame.size.height));
}
if (self.scrollView.subviews.lastObject != self.currentPage.view) {
[self.scrollView bringSubviewToFront:self.currentPage.view];
}
[super viewDidLayoutSubviews];
}
@ -390,7 +401,10 @@
previousPage.view.hidden = YES;
appDelegate.detailViewController.parentNavigationController.interactivePopGestureRecognizer.enabled = YES;
#if !TARGET_OS_MACCATALYST
[appDelegate.detailViewController.parentNavigationController setNavigationBarHidden:NO animated:YES];
#endif
self.autoscrollActive = NO;
}
@ -484,7 +498,7 @@
}
- (void)setNavigationBarHidden:(BOOL)hide alsoTraverse:(BOOL)alsoTraverse {
if (self.navigationController == nil || self.navigationController.navigationBarHidden == hide || self.currentlyTogglingNavigationBar) {
if (appDelegate.isMac || self.navigationController == nil || self.navigationController.navigationBarHidden == hide || self.currentlyTogglingNavigationBar) {
return;
}
@ -662,7 +676,7 @@
[MBProgressHUD hideHUDForView:self.view animated:YES];
[self hideNotifier];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!self.isPhone) {
[currentPage realignScroll];
}
}
@ -771,7 +785,7 @@
if (pageIndex >= 0) {
[self changePage:pageIndex animated:NO];
} else if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
} else if (!self.isPhone) {
// If the story can't be found, don't show anything; uncomment this to instead show the first unread story:
// [self doNextUnreadStory:nil];
} else {
@ -1080,9 +1094,12 @@
}
self.scrollingToPage = pageIndex;
[self.currentPage hideNoStoryMessage];
[self.nextPage hideNoStoryMessage];
[self.previousPage hideNoStoryMessage];
if (pageIndex >= 0) {
[self.currentPage hideNoStoryMessage];
[self.nextPage hideNoStoryMessage];
[self.previousPage hideNoStoryMessage];
}
// Check if already on the selected page
if (self.isHorizontal ? offset.x == frame.origin.x : offset.y == frame.origin.y) {
@ -1225,7 +1242,11 @@
[appDelegate.storiesCollection pushReadStory:[appDelegate.activeStory objectForKey:@"story_hash"]];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
#if TARGET_OS_MACCATALYST
self.appDelegate.detailViewController.navigationItem.leftBarButtonItems = @[[[UIBarButtonItem alloc] initWithCustomView:[UIView new]]];
#endif
if (!self.isPhone) {
if (appDelegate.detailViewController.storyTitlesOnLeft) {
appDelegate.detailViewController.navigationItem.rightBarButtonItems = [NSArray arrayWithObjects:
originalStoryButton,
@ -1327,6 +1348,13 @@
fontSettingsButton.enabled = YES;
originalStoryButton.enabled = YES;
#if TARGET_OS_MACCATALYST
if (@available(macCatalyst 16.0, *)) {
fontSettingsButton.hidden = NO;
originalStoryButton.hidden = NO;
}
#endif
} else {
[buttonText setEnabled:NO];
[buttonText setAlpha:.4];
@ -1335,6 +1363,13 @@
fontSettingsButton.enabled = NO;
originalStoryButton.enabled = NO;
#if TARGET_OS_MACCATALYST
if (@available(macCatalyst 16.0, *)) {
fontSettingsButton.hidden = YES;
originalStoryButton.hidden = YES;
}
#endif
}
[buttonSend setBackgroundImage:[[ThemeManager themeManager] themedImage:[UIImage imageNamed:@"traverse_send.png"]]
@ -1455,11 +1490,11 @@
// [self.appDelegate.feedDetailViewController changedStoryHeight:currentPage.webView.scrollView.contentSize.height];
}
- (void)toggleStorySaved:(id)sender {
- (IBAction)toggleStorySaved:(id)sender {
[appDelegate.storiesCollection toggleStorySaved];
}
- (void)toggleStoryUnread:(id)sender {
- (IBAction)toggleStoryUnread:(id)sender {
[appDelegate.storiesCollection toggleStoryUnread];
[appDelegate.feedDetailViewController reload]; // XXX only if successful?
}
@ -1482,12 +1517,26 @@
#pragma mark -
#pragma mark Styles
//- (BOOL)validateToolbarItem:(NSToolbarItem *)item {
// if item.itemIdentifier ==
// return !self.currentPage.view.isHidden;
//}
- (IBAction)toggleFontSize:(id)sender {
UINavigationController *fontSettingsNavigationController = appDelegate.fontSettingsNavigationController;
[fontSettingsNavigationController popToRootViewControllerAnimated:NO];
// [appDelegate showPopoverWithViewController:fontSettingsNavigationController contentSize:CGSizeZero sourceNavigationController:self.navigationController barButtonItem:self.fontSettingsButton sourceView:nil sourceRect:CGRectZero permittedArrowDirections:UIPopoverArrowDirectionAny];
#if TARGET_OS_MACCATALYST
UINavigationController *storiesNavController = appDelegate.storyPagesViewController.navigationController;
UIView *sourceView = storiesNavController.view;
CGRect sourceRect = CGRectMake(storiesNavController.view.frame.size.width - 59, 0, 20, 20);
[appDelegate showPopoverWithViewController:fontSettingsNavigationController contentSize:CGSizeZero sourceView:sourceView sourceRect:sourceRect];
#else
[appDelegate showPopoverWithViewController:fontSettingsNavigationController contentSize:CGSizeZero barButtonItem:self.fontSettingsButton];
#endif
}
- (void)setFontStyle:(NSString *)fontStyle {

View file

@ -20,6 +20,16 @@ class StoryPagesViewController: StoryPagesObjCViewController {
/// Reload the widget timeline.
@objc func reloadWidget() {
WidgetCenter.shared.reloadTimelines(ofKind: "Latest")
WidgetCenter.shared.reloadAllTimelines()
}
#if targetEnvironment(macCatalyst)
@objc func validateToolbarItem(_ item: NSToolbarItem) -> Bool {
if [.storyPagesSettings, .storyPagesBrowser].contains(item.itemIdentifier) {
return self.isStoryShown
} else {
return true
}
}
#endif
}

View file

@ -0,0 +1,150 @@
//
// StorySettings.swift
// NewsBlur
//
// Created by David Sinclair on 2024-04-04.
// Copyright © 2024 NewsBlur. All rights reserved.
//
import Foundation
class StorySettings {
let defaults = UserDefaults.standard
enum Content: String, RawRepresentable {
case title
case short
case medium
case long
static let titleLimit = 6
static let contentLimit = 10
var limit: Int {
switch self {
case .title:
return 6
case .short:
return 2
case .medium:
return 4
case .long:
return 6
}
}
}
var content: Content {
if let string = defaults.string(forKey: "story_list_preview_text_size"), let value = Content(rawValue: string) {
return value
} else {
return .short
}
}
enum Preview: String, RawRepresentable {
case none
case smallLeft = "small_left"
case largeLeft = "large_left"
case largeRight = "large_right"
case smallRight = "small_right"
var isLeft: Bool {
return [.smallLeft, .largeLeft].contains(self)
}
var isSmall: Bool {
return [.smallLeft, .smallRight].contains(self)
}
}
var preview: Preview {
if let string = defaults.string(forKey: "story_list_preview_images_size"), let value = Preview(rawValue: string) {
return value
} else {
return .smallRight
}
}
enum FontSize: String, RawRepresentable {
case xs
case small
case medium
case large
case xl
var offset: CGFloat {
switch self {
case .xs:
return -2
case .small:
return -1
case .medium:
return 0
case .large:
return 1
case .xl:
return 2
}
}
}
var fontSize: FontSize {
if let string = defaults.string(forKey: "feed_list_font_size"), let value = FontSize(rawValue: string) {
return value
} else {
return .medium
}
}
enum Spacing: String, RawRepresentable {
case compact
case comfortable
}
var spacing: Spacing {
if let string = defaults.string(forKey: "feed_list_spacing"), let value = Spacing(rawValue: string) {
return value
} else {
return .comfortable
}
}
var gridColumns: Int {
guard let pref = UserDefaults.standard.string(forKey: "grid_columns"), let columns = Int(pref) else {
if NewsBlurAppDelegate.shared.isCompactWidth {
return 1
} else if NewsBlurAppDelegate.shared.isPortrait || NewsBlurAppDelegate.shared.isPhone {
return 2
} else {
return 4
}
}
if NewsBlurAppDelegate.shared.isPortrait, columns > 3 {
return 3
}
return columns
}
var gridHeight: CGFloat {
guard let pref = UserDefaults.standard.string(forKey: "grid_height") else {
return 400
}
switch pref {
case "xs":
return 250
case "short":
return 300
case "tall":
return 500
case "xl":
return 600
default:
return 400
}
}
}

View file

@ -38,6 +38,16 @@ extension View {
}
}
extension Text {
func colored(_ color: Color) -> Text {
if #available(iOS 17.0, *) {
self.foregroundStyle(color)
} else {
self.foregroundColor(color)
}
}
}
struct RoundedCorner: Shape {
var radius: CGFloat = .infinity
var corners: UIRectCorner = .allCorners
@ -131,3 +141,70 @@ struct OffsetObservingScrollView<Content: View>: View {
.coordinateSpace(name: coordinateSpaceName)
}
}
struct WrappingHStack<Model, V>: View where Model: Hashable, V: View {
typealias ViewGenerator = (Model) -> V
var models: [Model]
var horizontalSpacing: CGFloat = 2
var verticalSpacing: CGFloat = 0
var viewGenerator: ViewGenerator
@State private var totalHeight
= CGFloat.zero // << variant for ScrollView/List
// = CGFloat.infinity // << variant for VStack
var body: some View {
VStack {
GeometryReader { geometry in
self.generateContent(in: geometry)
}
}
.frame(height: totalHeight)// << variant for ScrollView/List
//.frame(maxHeight: totalHeight) // << variant for VStack
}
private func generateContent(in geometry: GeometryProxy) -> some View {
var width = CGFloat.zero
var height = CGFloat.zero
return ZStack(alignment: .topLeading) {
ForEach(self.models, id: \.self) { models in
viewGenerator(models)
.padding(.horizontal, horizontalSpacing)
.padding(.vertical, verticalSpacing)
.alignmentGuide(.leading, computeValue: { dimension in
if (abs(width - dimension.width) > geometry.size.width)
{
width = 0
height -= dimension.height
}
let result = width
if models == self.models.last! {
width = 0 //last item
} else {
width -= dimension.width
}
return result
})
.alignmentGuide(.top, computeValue: {dimension in
let result = height
if models == self.models.last! {
height = 0 // last item
}
return result
})
}
}.background(viewHeightReader($totalHeight))
}
private func viewHeightReader(_ binding: Binding<CGFloat>) -> some View {
return GeometryReader { geometry -> Color in
let rect = geometry.frame(in: .local)
DispatchQueue.main.async {
binding.wrappedValue = rect.size.height
}
return .clear
}
}
}

View file

@ -31,6 +31,8 @@ extern NSString * const ThemeStyleDark;
@property (nonatomic, readonly) NSString *themeDisplayName;
@property (nonatomic, readonly) NSString *themeCSSSuffix;
@property (nonatomic, readonly) BOOL isDarkTheme;
@property (nonatomic, readonly) BOOL isSystemDark;
@property (nonatomic, readonly) BOOL isLikeSystem;
+ (instancetype)themeManager;

View file

@ -136,6 +136,14 @@ NSString * const ThemeStyleDark = @"dark";
return [theme isEqualToString:ThemeStyleDark] || [theme isEqualToString:ThemeStyleMedium];
}
- (BOOL)isSystemDark {
return self.appDelegate.window.windowScene.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark;
}
- (BOOL)isLikeSystem {
return self.isDarkTheme == self.isSystemDark;
}
- (BOOL)isValidTheme:(NSString *)theme {
return [theme isEqualToString:ThemeStyleLight] || [theme isEqualToString:ThemeStyleSepia] || [theme isEqualToString:ThemeStyleMedium] || [theme isEqualToString:ThemeStyleDark];
}
@ -266,7 +274,9 @@ NSString * const ThemeStyleDark = @"dark";
- (void)updateSegmentedControl:(UISegmentedControl *)segmentedControl {
segmentedControl.tintColor = UIColorFromRGB(0x8F918B);
#if !TARGET_OS_MACCATALYST
segmentedControl.backgroundColor = UIColorFromLightDarkRGB(0xe7e6e7, 0x303030);
#endif
segmentedControl.selectedSegmentTintColor = UIColorFromLightDarkRGB(0xffffff, 0x6f6f75);
[self updateTextAttributesForSegmentedControl:segmentedControl forState:UIControlStateNormal foregroundColor:UIColorFromLightDarkRGB(0x909090, 0xaaaaaa)];
@ -275,7 +285,9 @@ NSString * const ThemeStyleDark = @"dark";
- (void)updateThemeSegmentedControl:(UISegmentedControl *)segmentedControl {
segmentedControl.tintColor = [UIColor clearColor];
#if !TARGET_OS_MACCATALYST
segmentedControl.backgroundColor = [UIColor clearColor];
#endif
segmentedControl.selectedSegmentTintColor = [UIColor clearColor];
}
@ -440,9 +452,7 @@ NSString * const ThemeStyleDark = @"dark";
}
- (void)updateForSystemAppearance {
BOOL isDark = self.appDelegate.window.windowScene.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark;
[self systemAppearanceDidChange:isDark];
[self systemAppearanceDidChange:self.isSystemDark];
}
- (void)systemAppearanceDidChange:(BOOL)isDark {

View file

@ -0,0 +1,97 @@
//
// ToolbarDelegate.swift
// NewsBlur
//
// Created by David Sinclair on 2024-01-05.
// Copyright © 2024 NewsBlur. All rights reserved.
//
import UIKit
#if targetEnvironment(macCatalyst)
class ToolbarDelegate: NSObject {
}
extension NSToolbarItem.Identifier {
static let reloadFeeds = NSToolbarItem.Identifier("com.newsblur.reloadFeeds")
static let feedDetailUnread = NSToolbarItem.Identifier("com.newsblur.feedDetailUnread")
static let feedDetailSettings = NSToolbarItem.Identifier("com.newsblur.feedDetailSettings")
static let storyPagesSettings = NSToolbarItem.Identifier("com.newsblur.storyPagesSettings")
static let storyPagesBrowser = NSToolbarItem.Identifier("com.newsblur.storyPagesBrowser")
}
extension ToolbarDelegate: NSToolbarDelegate {
func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
let identifiers: [NSToolbarItem.Identifier] = [
.toggleSidebar,
.space,
.reloadFeeds,
.space,
.feedDetailUnread,
.feedDetailSettings,
.flexibleSpace,
.storyPagesSettings,
.storyPagesBrowser
]
return identifiers
}
func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] {
return toolbarDefaultItemIdentifiers(toolbar)
}
func toolbar(_ toolbar: NSToolbar,
itemForItemIdentifier itemIdentifier: NSToolbarItem.Identifier,
willBeInsertedIntoToolbar flag: Bool) -> NSToolbarItem? {
switch itemIdentifier {
case .reloadFeeds:
return makeToolbarItem(itemIdentifier,
image: UIImage(systemName: "arrow.clockwise"),
label: "Reload Sites",
action: #selector(BaseViewController.reloadFeeds(_:)))
case .feedDetailUnread:
return makeToolbarItem(itemIdentifier,
image: Utilities.imageNamed("mark-read", sized: 24),
label: "Mark as Read",
action: #selector(BaseViewController.openMarkReadMenu(_:)))
case .feedDetailSettings:
return makeToolbarItem(itemIdentifier,
image: Utilities.imageNamed("settings", sized: 24),
label: "Site Settings",
action: #selector(BaseViewController.openSettingsMenu(_:)))
case .storyPagesSettings:
return makeToolbarItem(itemIdentifier,
image: Utilities.imageNamed("settings", sized: 24),
label: "Story Settings",
action: #selector(StoryPagesViewController.toggleFontSize(_:)))
case .storyPagesBrowser:
return makeToolbarItem(itemIdentifier,
image: Utilities.imageNamed("original_button.png", sized: 24),
label: "Show Original Story",
action: #selector(StoryPagesViewController.showOriginalSubview(_:)))
default:
return nil
}
}
func makeToolbarItem(_ identifier: NSToolbarItem.Identifier,
image: UIImage?,
label: String,
action: Selector,
target: AnyObject? = nil) -> NSToolbarItem {
let item = NSToolbarItem(itemIdentifier: identifier)
item.image = image
item.label = label
item.action = action
item.target = target
return item
}
}
#endif

View file

@ -0,0 +1,67 @@
//
// TrainerCapsule.swift
// NewsBlur
//
// Created by David Sinclair on 2024-04-02.
// Copyright © 2024 NewsBlur. All rights reserved.
//
import SwiftUI
struct TrainerCapsule: View {
var score: Feed.Score
var header: String
var image: UIImage?
var value: String
var count: Int = 0
var body: some View {
HStack {
HStack {
Image(systemName: score.imageName)
.foregroundColor(.white)
content
}
.padding([.top, .bottom], 5)
.padding([.leading, .trailing], 10)
.background(score == .like ? Color(red: 0, green: 0.5, blue: 0.0) : score == .dislike ? Color.red : Color(white: ThemeManager.shared.isSystemDark ? 0.35 : 0.6))
.clipShape(Capsule())
if count > 0 {
Text("x \(count)")
.colored(.gray)
.padding([.trailing], 10)
}
}
}
var content: Text {
Text("\(Text("\(header):").colored(.init(white: 0.85))) \(imageText)\(value)")
.colored(.white)
}
var imageText: Text {
if let image {
Text(Image(uiImage: image)).baselineOffset(-3) + Text(" ")
} else {
Text("")
}
}
}
#Preview {
TrainerCapsule(score: .none, header: "Tag", value: "None Example")
}
#Preview {
TrainerCapsule(score: .like, header: "Tag", value: "Liked Example")
}
#Preview {
TrainerCapsule(score: .dislike, header: "Tag", value: "Disliked Example")
}

View file

@ -0,0 +1,218 @@
//
// TrainerView.swift
// NewsBlur
//
// Created by David Sinclair on 2024-04-02.
// Copyright © 2024 NewsBlur. All rights reserved.
//
import SwiftUI
/// A protocol of interaction between the trainer view and the enclosing view controller.
protocol TrainerInteraction {
var isStoryTrainer: Bool { get set }
}
struct TrainerView: View {
var interaction: TrainerInteraction
@ObservedObject var cache: StoryCache
let columns = [GridItem(.adaptive(minimum: 50))]
var body: some View {
VStack(alignment: .leading) {
Text("What do you 👍 \(Text("like").colored(.green)) and 👎 \(Text("dislike").colored(.red)) about this \(feedOrStoryLowercase)?")
.font(font(named: "WhitneySSm-Medium", size: 16))
.padding()
List {
Section(content: {
VStack(alignment: .leading) {
if interaction.isStoryTrainer {
Text("Choose one or more words from the title:")
.font(font(named: "WhitneySSm-Medium", size: 12))
.padding([.top], 10)
WrappingHStack(models: titleWords, horizontalSpacing: 1) { word in
Button(action: {
if addingTitle.isEmpty {
addingTitle = word
} else {
addingTitle.append(" \(word)")
}
}, label: {
TrainerWord(word: word)
})
.buttonStyle(BorderlessButtonStyle())
.padding([.top, .bottom], 5)
}
if !addingTitle.isEmpty {
HStack {
Button(action: {
cache.appDelegate.toggleTitleClassifier(addingTitle, feedId: feed?.id, score: 0)
addingTitle = ""
}, label: {
TrainerCapsule(score: .none, header: "Title", value: addingTitle)
})
.buttonStyle(BorderlessButtonStyle())
.padding([.top, .bottom], 5)
Button {
addingTitle = ""
} label: {
Image(systemName: "xmark.circle.fill")
.imageScale(.large)
.foregroundColor(.gray)
}
}
}
}
WrappingHStack(models: titles) { title in
Button(action: {
cache.appDelegate.toggleTitleClassifier(title.name, feedId: feed?.id, score: 0)
}, label: {
TrainerCapsule(score: title.score, header: "Title", value: title.name, count: title.count)
})
.buttonStyle(BorderlessButtonStyle())
.padding([.top, .bottom], 5)
}
}
}, header: {
header(story: "Story Title", feed: "Titles & Phrases")
})
Section(content: {
WrappingHStack(models: authors) { author in
Button(action: {
cache.appDelegate.toggleAuthorClassifier(author.name, feedId: feed?.id)
}, label: {
TrainerCapsule(score: author.score, header: "Author", value: author.name, count: author.count)
})
.buttonStyle(BorderlessButtonStyle())
.padding([.top, .bottom], 5)
}
}, header: {
header(story: "Story Author", feed: "Authors")
})
Section(content: {
WrappingHStack(models: tags) { tag in
Button(action: {
cache.appDelegate.toggleTagClassifier(tag.name, feedId: feed?.id)
}, label: {
TrainerCapsule(score: tag.score, header: "Tag", value: tag.name, count: tag.count)
})
.buttonStyle(BorderlessButtonStyle())
.padding([.top, .bottom], 5)
}
}, header: {
header(story: "Story Categories & Tags", feed: "Categories & Tags")
})
Section(content: {
HStack {
if let feed = feed {
Button(action: {
cache.appDelegate.toggleFeedClassifier(feed.id)
}, label: {
TrainerCapsule(score: score(key: "feeds", value: feed.id), header: "Site", image: feed.image, value: feed.name)
})
.buttonStyle(BorderlessButtonStyle())
.padding([.top, .bottom], 5)
}
}
}, header: {
header(feed: "Everything by This Publisher")
})
}
.font(font(named: "WhitneySSm-Medium", size: 12))
}
.onAppear {
addingTitle = ""
}
}
func font(named: String, size: CGFloat) -> Font {
return Font.custom(named, size: size + cache.settings.fontSize.offset, relativeTo: .caption)
}
func reload() {
cache.reload()
addingTitle = ""
}
var feedOrStoryLowercase: String {
return interaction.isStoryTrainer ? "story" : "site"
}
@ViewBuilder
func header(story: String? = nil, feed: String) -> some View {
if let story {
Text(interaction.isStoryTrainer ? story : feed)
.font(font(named: "WhitneySSm-Medium", size: 16))
} else {
Text(feed)
.font(font(named: "WhitneySSm-Medium", size: 16))
}
}
func score(key: String, value: String) -> Feed.Score {
guard let classifiers = feed?.classifiers(for: key),
let score = classifiers[value] as? Int else {
return .none
}
if score > 0 {
return .like
} else if score < 0 {
return .dislike
} else {
return .none
}
}
var titleWords: [String] {
if interaction.isStoryTrainer, let story = cache.selected {
return story.title.components(separatedBy: .whitespaces)
} else {
return []
}
}
@State private var addingTitle = ""
var feed: Feed? {
return cache.currentFeed ?? cache.selected?.feed
}
var titles: [Feed.Training] {
if interaction.isStoryTrainer {
return cache.selected?.titles ?? []
} else {
return feed?.titles ?? []
}
}
var authors: [Feed.Training] {
if interaction.isStoryTrainer {
return cache.selected?.authors ?? []
} else {
return feed?.authors ?? []
}
}
var tags: [Feed.Training] {
if interaction.isStoryTrainer {
return cache.selected?.tags ?? []
} else {
return feed?.tags ?? []
}
}
}
//#Preview {
// TrainerViewController()
//}

View file

@ -6,6 +6,10 @@
// Copyright (c) 2012 NewsBlur. All rights reserved.
//
#warning This code is obsolete, and will be removed once the SwiftUI implementation is complete.
#import <UIKit/UIKit.h>
#import "BaseViewController.h"
#import "NewsBlurAppDelegate.h"
@ -21,9 +25,7 @@
@end
@interface TrainerViewController : BaseViewController <WKNavigationDelegate> {
NewsBlurAppDelegate *appDelegate;
@interface OldTrainerViewController : BaseViewController <WKNavigationDelegate> {
IBOutlet UIBarButtonItem * closeButton;
TrainerWebView *webView;
IBOutlet UINavigationBar *navBar;
@ -33,7 +35,6 @@
BOOL storyTrainer;
}
@property (nonatomic) IBOutlet NewsBlurAppDelegate *appDelegate;
@property (nonatomic) IBOutlet UIBarButtonItem *closeButton;
@property (nonatomic) IBOutlet TrainerWebView *webView;
@property (nonatomic) IBOutlet UINavigationBar *navBar;

View file

@ -6,18 +6,21 @@
// Copyright (c) 2012 NewsBlur. All rights reserved.
//
#warning This code is obsolete, and will be removed once the SwiftUI implementation is complete.
#import "TrainerViewController.h"
#import "StringHelper.h"
#import "Utilities.h"
#import "AFNetworking.h"
#import "StoriesCollection.h"
@implementation TrainerViewController
@implementation OldTrainerViewController
@synthesize closeButton;
@synthesize webView;
@synthesize navBar;
@synthesize appDelegate;
@synthesize feedTrainer;
@synthesize storyTrainer;
@synthesize feedLoaded;
@ -35,8 +38,6 @@
{
[super viewDidLoad];
self.appDelegate = [NewsBlurAppDelegate sharedAppDelegate];
UIBarButtonItem *done = [[UIBarButtonItem alloc]
initWithTitle:@"Done Training"
style:UIBarButtonItemStyleDone
@ -99,7 +100,7 @@
[self informError:@"Could not load trainer"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC),
dispatch_get_main_queue(), ^() {
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!self.isPhone) {
[self.appDelegate hidePopover];
} else {
[self.appDelegate.feedsNavigationController dismissViewControllerAnimated:YES completion:nil];
@ -162,6 +163,9 @@
int contentWidth = self.view.frame.size.width;
NSString *contentWidthClass;
#if TARGET_OS_MACCATALYST
contentWidthClass = @"NB-mac";
#else
if (contentWidth > 700) {
contentWidthClass = @"NB-ipad-wide";
} else if (contentWidth > 480) {
@ -169,6 +173,7 @@
} else {
contentWidthClass = @"NB-iphone";
}
#endif
// set up layout values based on iPad/iPhone
NSString *headerString = [NSString stringWithFormat:@
@ -542,7 +547,7 @@
- (IBAction)doCloseDialog:(id)sender {
[appDelegate hidePopover];
[appDelegate.trainerViewController dismissViewControllerAnimated:YES completion:nil];
// [appDelegate.trainerViewController dismissViewControllerAnimated:YES completion:nil];
}
- (void)changeTitle:(id)sender score:(int)score {
@ -603,12 +608,12 @@
- (void)focusTitle:(id)sender {
NewsBlurAppDelegate *appDelegate = [NewsBlurAppDelegate sharedAppDelegate];
[appDelegate.trainerViewController changeTitle:sender score:1];
// [appDelegate.trainerViewController changeTitle:sender score:1];
}
- (void)hideTitle:(id)sender {
NewsBlurAppDelegate *appDelegate = [NewsBlurAppDelegate sharedAppDelegate];
[appDelegate.trainerViewController changeTitle:sender score:-1];
// [appDelegate.trainerViewController changeTitle:sender score:-1];
}
// Work around iOS 9 issue where menu doesn't appear the first time

View file

@ -0,0 +1,58 @@
//
// TrainerViewController.swift
// NewsBlur
//
// Created by David Sinclair on 2024-04-01.
// Copyright © 2024 NewsBlur. All rights reserved.
//
import SwiftUI
@objc class TrainerViewController: BaseViewController {
@objc var isStoryTrainer = false
@objc var isFeedLoaded = false
lazy var hostingController = makeHostingController()
var trainerView: TrainerView {
return hostingController.rootView
}
var storyCache: StoryCache {
return appDelegate.feedDetailViewController.storyCache
}
private func makeHostingController() -> UIHostingController<TrainerView> {
let trainerView = TrainerView(interaction: self, cache: storyCache)
let trainerController = UIHostingController(rootView: trainerView)
trainerController.view.translatesAutoresizingMaskIntoConstraints = false
return trainerController
}
override func viewDidLoad() {
super.viewDidLoad()
addChild(hostingController)
view.addSubview(hostingController.view)
hostingController.didMove(toParent: self)
NSLayoutConstraint.activate([
hostingController.view.topAnchor.constraint(equalTo: view.topAnchor),
hostingController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
hostingController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
hostingController.view.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
])
// changedLayout()
}
@objc func reload() {
trainerView.reload()
}
}
extension TrainerViewController: TrainerInteraction {
//TODO: 🚧
}

View file

@ -0,0 +1,28 @@
//
// TrainerWord.swift
// NewsBlur
//
// Created by David Sinclair on 2024-04-03.
// Copyright © 2024 NewsBlur. All rights reserved.
//
import SwiftUI
struct TrainerWord: View {
var word: String
var body: some View {
HStack {
Text(word)
.colored(Color(white: ThemeManager.shared.isSystemDark ? 0.8 : 0.1))
.padding([.top, .bottom], 1)
.padding([.leading, .trailing], 1)
.background(Color(white: ThemeManager.shared.isSystemDark ? 0.35 : 0.95))
.clipShape(RoundedRectangle(cornerRadius: 5))
}
}
}
#Preview {
TrainerWord(word: "Example")
}

View file

@ -54,7 +54,7 @@ const int COUNT_HEIGHT = 18;
CGRect rr;
if (listType == NBFeedListSocial) {
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!appDelegate.isPhone) {
rr = CGRectMake(rect.size.width + rect.origin.x - psOffset, CGRectGetMidY(r)-COUNT_HEIGHT/2, psWidth, COUNT_HEIGHT);
} else {
rr = CGRectMake(rect.size.width + rect.origin.x - psOffset, CGRectGetMidY(r)-COUNT_HEIGHT/2, psWidth, COUNT_HEIGHT);
@ -98,7 +98,7 @@ const int COUNT_HEIGHT = 18;
if (nt > 0 && appDelegate.selectedIntelligence <= 0) {
CGRect rr;
if (listType == NBFeedListSocial) {
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
if (!appDelegate.isPhone) {
rr = CGRectMake(rect.size.width + rect.origin.x - psWidth - psPadding - ntOffset, CGRectGetMidY(r)-COUNT_HEIGHT/2, ntWidth, COUNT_HEIGHT);
} else {
rr = CGRectMake(rect.size.width + rect.origin.x - psWidth - psPadding - ntOffset, CGRectGetMidY(r)-COUNT_HEIGHT/2, ntWidth, COUNT_HEIGHT);

View file

@ -10,13 +10,10 @@
#import "NewsBlurAppDelegate.h"
#import "NewsBlur-Swift.h"
@class NewsBlurAppDelegate;
@class ProfileBadge;
@interface UserProfileViewController : BaseViewController
<UITableViewDataSource, UITableViewDelegate> {
NewsBlurAppDelegate *appDelegate;
UILabel *followingCount;
UILabel *followersCount;
ProfileBadge *profileBadge;
@ -26,7 +23,6 @@
NSDictionary *userProfile;
}
@property (nonatomic) NewsBlurAppDelegate *appDelegate;
@property (nonatomic) ProfileBadge *profileBadge;
@property (nonatomic) UITableView *profileTable;
@property (nonatomic) NSArray *activitiesArray;

View file

@ -17,7 +17,6 @@
@implementation UserProfileViewController
@synthesize appDelegate;
@synthesize profileBadge;
@synthesize profileTable;
@synthesize activitiesArray;
@ -40,9 +39,7 @@
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
self.appDelegate = (NewsBlurAppDelegate *)[[UIApplication sharedApplication] delegate];
UITableView *profiles = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) style:UITableViewStyleGrouped];
self.profileTable = profiles;
self.profileTable.dataSource = self;
@ -89,7 +86,6 @@
// self.view.frame = self.view.bounds;
self.preferredContentSize = CGSizeMake(320, 454);
self.appDelegate = (NewsBlurAppDelegate *)[[UIApplication sharedApplication] delegate];
[MBProgressHUD hideHUDForView:self.view animated:YES];
MBProgressHUD *HUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
HUD.labelText = @"Profiling...";

View file

@ -17,7 +17,15 @@
170E3CD124F8A664009CE819 /* SplitViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 170E3CD024F8A664009CE819 /* SplitViewDelegate.swift */; };
170E3CD324F8A89B009CE819 /* HorizontalPageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 170E3CD224F8A89B009CE819 /* HorizontalPageViewController.swift */; };
170E3CD724F8AB0D009CE819 /* FeedDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 170E3CD624F8AB0D009CE819 /* FeedDetailViewController.swift */; };
17150E1E2B05775A004D5309 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17150E1D2B05775A004D5309 /* SceneDelegate.swift */; };
17150E1F2B05775A004D5309 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17150E1D2B05775A004D5309 /* SceneDelegate.swift */; };
1715D02B2166B3F900227731 /* PremiumManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1715D02A2166B3F900227731 /* PremiumManager.m */; };
17179E292BD6F86C006B18D5 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 17179E282BD6F86C006B18D5 /* PrivacyInfo.xcprivacy */; };
17179E2A2BD6F86D006B18D5 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 17179E282BD6F86C006B18D5 /* PrivacyInfo.xcprivacy */; };
171904B52BBC8D4E004CCC96 /* TrainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171904B42BBC8D4E004CCC96 /* TrainerView.swift */; };
171904B62BBC8D4E004CCC96 /* TrainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171904B42BBC8D4E004CCC96 /* TrainerView.swift */; };
171904B82BBCA712004CCC96 /* TrainerCapsule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171904B72BBCA712004CCC96 /* TrainerCapsule.swift */; };
171904B92BBCA712004CCC96 /* TrainerCapsule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171904B72BBCA712004CCC96 /* TrainerCapsule.swift */; };
171B6FFD25C4C7C8008638A9 /* StoryPagesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171B6FFC25C4C7C8008638A9 /* StoryPagesViewController.swift */; };
1721C9D12497F91A00B0EDC4 /* mute_gray.png in Resources */ = {isa = PBXBuildFile; fileRef = 1721C9D02497F91900B0EDC4 /* mute_gray.png */; };
1723388B26BE43EB00610784 /* WidgetLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1723388A26BE43EB00610784 /* WidgetLoader.swift */; };
@ -503,7 +511,7 @@
1757920A2930605500490924 /* g_icn_textview_black.png in Resources */ = {isa = PBXBuildFile; fileRef = FF83FF0F1FB54691008DAC0F /* g_icn_textview_black.png */; };
1757920B2930605500490924 /* logo_58.png in Resources */ = {isa = PBXBuildFile; fileRef = FF322234185BC1AA004078AA /* logo_58.png */; };
1757920C2930605500490924 /* logo_144.png in Resources */ = {isa = PBXBuildFile; fileRef = FFC486A619CA40B700F4758F /* logo_144.png */; };
1757920D2930605500490924 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = FFF1E4C717750BDD00BF59D3 /* Settings.bundle */; };
1757920D2930605500490924 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = FFF1E4C717750BDD00BF59D3 /* Settings.bundle */; platformFilter = ios; };
1757920E2930605500490924 /* menu_icn_preferences.png in Resources */ = {isa = PBXBuildFile; fileRef = FFF1E4C917750D2C00BF59D3 /* menu_icn_preferences.png */; };
1757920F2930605500490924 /* menu_icn_preferences@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FFF1E4CA17750D2C00BF59D3 /* menu_icn_preferences@2x.png */; };
175792102930605500490924 /* checkmark.png in Resources */ = {isa = PBXBuildFile; fileRef = FF855B541794A53A0098D48A /* checkmark.png */; };
@ -708,6 +716,8 @@
175792DA2930605500490924 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 78095E3E128EF35400230C8E /* CFNetwork.framework */; };
175792E72930611C00490924 /* LaunchScreenDev.xib in Resources */ = {isa = PBXBuildFile; fileRef = 175792E62930611B00490924 /* LaunchScreenDev.xib */; };
175792E92930617600490924 /* logo_newsblur_512-dev.png in Resources */ = {isa = PBXBuildFile; fileRef = 175792E82930617600490924 /* logo_newsblur_512-dev.png */; };
175DC6AF2BBB87D200B3708F /* TrainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 175DC6AE2BBB87D200B3708F /* TrainerViewController.swift */; };
175DC6B02BBB87D200B3708F /* TrainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 175DC6AE2BBB87D200B3708F /* TrainerViewController.swift */; };
175FAC4C23AB34EB002AC38C /* menu_icn_widget.png in Resources */ = {isa = PBXBuildFile; fileRef = 175FAC4A23AB34EB002AC38C /* menu_icn_widget.png */; };
175FAC4D23AB34EB002AC38C /* menu_icn_widget@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 175FAC4B23AB34EB002AC38C /* menu_icn_widget@2x.png */; };
176129601C630AEB00702FE4 /* mute_feed_off.png in Resources */ = {isa = PBXBuildFile; fileRef = 1761295C1C630AEB00702FE4 /* mute_feed_off.png */; };
@ -716,6 +726,20 @@
176129631C630AEB00702FE4 /* mute_feed_on@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 1761295F1C630AEB00702FE4 /* mute_feed_on@2x.png */; };
1763E2A123B1BCC900BA080C /* WidgetFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1763E2A023B1BCC900BA080C /* WidgetFeed.swift */; };
1763E2A323B1CEB600BA080C /* WidgetBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1763E2A223B1CEB600BA080C /* WidgetBarView.swift */; };
17654E332B02C08700F61B2B /* WidgetLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1723388A26BE43EB00610784 /* WidgetLoader.swift */; };
17654E342B02C08700F61B2B /* WidgetCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1723389026BF7CFE00610784 /* WidgetCache.swift */; };
17654E352B02C08700F61B2B /* WidgetExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 173CB31326BCE94700BA872A /* WidgetExtension.swift */; };
17654E362B02C08700F61B2B /* WidgetStory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1723388C26BE440400610784 /* WidgetStory.swift */; };
17654E372B02C08700F61B2B /* WidgetBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1723389326C3775A00610784 /* WidgetBarView.swift */; };
17654E382B02C08700F61B2B /* WidgetDebugTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17997C5727A8FDD100483E69 /* WidgetDebugTimer.swift */; };
17654E392B02C08700F61B2B /* WidgetFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1723388D26BE440400610784 /* WidgetFeed.swift */; };
17654E3A2B02C08700F61B2B /* WidgetStoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1791C21426C4C7BC00D815AA /* WidgetStoryView.swift */; };
17654E3C2B02C08700F61B2B /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 173CB31026BCE94700BA872A /* SwiftUI.framework */; };
17654E3D2B02C08700F61B2B /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 173CB30E26BCE94700BA872A /* WidgetKit.framework */; };
17654E3F2B02C08700F61B2B /* WhitneySSm-Medium-Bas.otf in Resources */ = {isa = PBXBuildFile; fileRef = FF3A3E051BFBBAC600ADC01A /* WhitneySSm-Medium-Bas.otf */; };
17654E402B02C08700F61B2B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 173CB31526BCE94A00BA872A /* Assets.xcassets */; };
17654E412B02C08700F61B2B /* WhitneySSm-Book-Bas.otf in Resources */ = {isa = PBXBuildFile; fileRef = FF3A3E011BFBBAC600ADC01A /* WhitneySSm-Book-Bas.otf */; };
17654E472B02C0A700F61B2B /* NewsBlur Alpha Widget.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 17654E452B02C08700F61B2B /* NewsBlur Alpha Widget.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
176A5C7A24F8BD1B009E8DF9 /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 176A5C7924F8BD1B009E8DF9 /* DetailViewController.swift */; };
17731A9D23DFAD3D00759A7D /* ImportExportPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17731A9C23DFAD3D00759A7D /* ImportExportPreferences.swift */; };
177551D5238E228A00E27818 /* NotificationCenter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 177551D4238E228A00E27818 /* NotificationCenter.framework */; platformFilter = ios; };
@ -737,7 +761,13 @@
1788939D249332E6004CBA4E /* g_icn_search.png in Resources */ = {isa = PBXBuildFile; fileRef = 1788939C249332E6004CBA4E /* g_icn_search.png */; };
1791C21526C4C7BC00D815AA /* WidgetStoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1791C21426C4C7BC00D815AA /* WidgetStoryView.swift */; };
17997C5827A8FDD100483E69 /* WidgetDebugTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17997C5727A8FDD100483E69 /* WidgetDebugTimer.swift */; };
179A88022B48E64A00916CF4 /* ToolbarDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179A88012B48E64900916CF4 /* ToolbarDelegate.swift */; };
179A88032B48E64A00916CF4 /* ToolbarDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179A88012B48E64900916CF4 /* ToolbarDelegate.swift */; };
179DD9CF23DFDD51007BFD21 /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 179DD9CE23DFDD51007BFD21 /* CloudKit.framework */; };
17A0518A2C095B20000994E9 /* AuxSceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A051892C095B20000994E9 /* AuxSceneDelegate.swift */; };
17A0518B2C095B20000994E9 /* AuxSceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A051892C095B20000994E9 /* AuxSceneDelegate.swift */; };
17A0518D2C095E78000994E9 /* AuxInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 17A0518C2C095E78000994E9 /* AuxInterface.storyboard */; };
17A0518E2C095E78000994E9 /* AuxInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 17A0518C2C095E78000994E9 /* AuxInterface.storyboard */; };
17A396D924F86A8F0023C9E2 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 17A396D824F86A8F0023C9E2 /* MainInterface.storyboard */; };
17A92A3C289B7C6B00AB0A78 /* saved-stories@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 17A92A3B289B7C6B00AB0A78 /* saved-stories@2x.png */; };
17AACFE122279A3C00DE6EA4 /* autoscroll_resume@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 17AACFD722279A3900DE6EA4 /* autoscroll_resume@2x.png */; };
@ -754,6 +784,14 @@
17B14BDD23E24B4E00CF8D2C /* menu_icn_statistics@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 17B14BDB23E24B4E00CF8D2C /* menu_icn_statistics@2x.png */; };
17B33D1827D97282009108AD /* g_icn_folder_widget.png in Resources */ = {isa = PBXBuildFile; fileRef = 17B33D1627D97281009108AD /* g_icn_folder_widget.png */; };
17B33D1927D97282009108AD /* g_icn_folder_widget@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 17B33D1727D97282009108AD /* g_icn_folder_widget@2x.png */; };
17BC56A72BBE4A5600A30C41 /* TrainerWord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17BC56A62BBE4A5600A30C41 /* TrainerWord.swift */; };
17BC56A82BBE4A5600A30C41 /* TrainerWord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17BC56A62BBE4A5600A30C41 /* TrainerWord.swift */; };
17BC56AA2BBF6BC000A30C41 /* Feed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17BC56A92BBF6BC000A30C41 /* Feed.swift */; };
17BC56AB2BBF6BC000A30C41 /* Feed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17BC56A92BBF6BC000A30C41 /* Feed.swift */; };
17BC56AD2BBF6C0000A30C41 /* StoryCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17BC56AC2BBF6C0000A30C41 /* StoryCache.swift */; };
17BC56AE2BBF6C0000A30C41 /* StoryCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17BC56AC2BBF6C0000A30C41 /* StoryCache.swift */; };
17BC56B02BBF6C2200A30C41 /* StorySettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17BC56AF2BBF6C2200A30C41 /* StorySettings.swift */; };
17BC56B12BBF6C2200A30C41 /* StorySettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17BC56AF2BBF6C2200A30C41 /* StorySettings.swift */; };
17BD3BA52271102500F615EC /* Intents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17BD3BA42271102500F615EC /* Intents.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
17BD3BA72271122800F615EC /* CoreSpotlight.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17BD3BA62271122800F615EC /* CoreSpotlight.framework */; };
17BD3BA92271125400F615EC /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17BD3BA82271125400F615EC /* CoreServices.framework */; };
@ -830,6 +868,10 @@
17EB505D1BE4411E0021358B /* choose_font@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 17EB505B1BE4411E0021358B /* choose_font@2x.png */; };
17EB50601BE46A900021358B /* FontListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 17EB505F1BE46A900021358B /* FontListViewController.m */; };
17EB50621BE46BB00021358B /* FontListViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17EB50611BE46BB00021358B /* FontListViewController.xib */; };
17EE11C82B27FA0C00E7C0CC /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 17EE11C72B27FA0C00E7C0CC /* Settings.bundle */; platformFilter = maccatalyst; };
17EE11C92B27FA0C00E7C0CC /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 17EE11C72B27FA0C00E7C0CC /* Settings.bundle */; platformFilter = maccatalyst; };
17EE11CE2B28011D00E7C0CC /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 17EE11CD2B28011D00E7C0CC /* Credits.rtf */; };
17EE11CF2B28011D00E7C0CC /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 17EE11CD2B28011D00E7C0CC /* Credits.rtf */; };
17F156711BDABBF60092EBFD /* safari_shadow@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 17F156701BDABBF60092EBFD /* safari_shadow@2x.png */; };
17F363F2238E417300D5379D /* WidgetExtensionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17F363EF238E417300D5379D /* WidgetExtensionViewController.swift */; };
17F39EAA264754CD004B46D1 /* image_preview_large_left.png in Resources */ = {isa = PBXBuildFile; fileRef = 17F39EA6264754CC004B46D1 /* image_preview_large_left.png */; };
@ -1320,7 +1362,7 @@
FFEA5AEC19D340BC00ED87A0 /* logo_newsblur_512.png in Resources */ = {isa = PBXBuildFile; fileRef = FFEA5AEB19D340BC00ED87A0 /* logo_newsblur_512.png */; };
FFECD019172B105800D45A62 /* UIActivitySafari.png in Resources */ = {isa = PBXBuildFile; fileRef = FFECD017172B105800D45A62 /* UIActivitySafari.png */; };
FFECD01A172B105800D45A62 /* UIActivitySafari@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FFECD018172B105800D45A62 /* UIActivitySafari@2x.png */; };
FFF1E4C817750BDD00BF59D3 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = FFF1E4C717750BDD00BF59D3 /* Settings.bundle */; };
FFF1E4C817750BDD00BF59D3 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = FFF1E4C717750BDD00BF59D3 /* Settings.bundle */; platformFilter = ios; };
FFF1E4CB17750D2C00BF59D3 /* menu_icn_preferences.png in Resources */ = {isa = PBXBuildFile; fileRef = FFF1E4C917750D2C00BF59D3 /* menu_icn_preferences.png */; };
FFF1E4CC17750D2C00BF59D3 /* menu_icn_preferences@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FFF1E4CA17750D2C00BF59D3 /* menu_icn_preferences@2x.png */; };
FFF8B3AF1F847505001AB95E /* NBDashboardNavigationBar.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF8B3AE1F847505001AB95E /* NBDashboardNavigationBar.m */; };
@ -1342,6 +1384,13 @@
remoteGlobalIDString = 1749390F1C251BFE003D98AA;
remoteInfo = "Share Extension";
};
17654E482B02C0A800F61B2B /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */;
proxyType = 1;
remoteGlobalIDString = 17654E312B02C08700F61B2B;
remoteInfo = "NewsBlur Alpha Widget";
};
177551DD238E228A00E27818 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */;
@ -1359,6 +1408,17 @@
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
17654E4A2B02C0A800F61B2B /* Embed Foundation Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
17654E472B02C0A700F61B2B /* NewsBlur Alpha Widget.appex in Embed Foundation Extensions */,
);
name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
FF8A94A31DE3BB77000A4C31 /* Embed Foundation Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
@ -1397,8 +1457,12 @@
170E3CD024F8A664009CE819 /* SplitViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplitViewDelegate.swift; sourceTree = "<group>"; };
170E3CD224F8A89B009CE819 /* HorizontalPageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HorizontalPageViewController.swift; sourceTree = "<group>"; };
170E3CD624F8AB0D009CE819 /* FeedDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedDetailViewController.swift; sourceTree = "<group>"; };
17150E1D2B05775A004D5309 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
1715D0292166B3F900227731 /* PremiumManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PremiumManager.h; sourceTree = "<group>"; };
1715D02A2166B3F900227731 /* PremiumManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PremiumManager.m; sourceTree = "<group>"; };
17179E282BD6F86C006B18D5 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
171904B42BBC8D4E004CCC96 /* TrainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrainerView.swift; sourceTree = "<group>"; };
171904B72BBCA712004CCC96 /* TrainerCapsule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrainerCapsule.swift; sourceTree = "<group>"; };
171B6FFC25C4C7C8008638A9 /* StoryPagesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoryPagesViewController.swift; sourceTree = "<group>"; };
1721C9D02497F91900B0EDC4 /* mute_gray.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = mute_gray.png; sourceTree = "<group>"; };
1723388A26BE43EB00610784 /* WidgetLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetLoader.swift; sourceTree = "<group>"; };
@ -1465,6 +1529,7 @@
175792E42930605500490924 /* NB Alpha.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "NB Alpha.app"; sourceTree = BUILT_PRODUCTS_DIR; };
175792E62930611B00490924 /* LaunchScreenDev.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LaunchScreenDev.xib; sourceTree = "<group>"; };
175792E82930617600490924 /* logo_newsblur_512-dev.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "logo_newsblur_512-dev.png"; sourceTree = "<group>"; };
175DC6AE2BBB87D200B3708F /* TrainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrainerViewController.swift; sourceTree = "<group>"; };
175FAC4A23AB34EB002AC38C /* menu_icn_widget.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = menu_icn_widget.png; sourceTree = "<group>"; };
175FAC4B23AB34EB002AC38C /* menu_icn_widget@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "menu_icn_widget@2x.png"; sourceTree = "<group>"; };
1761295C1C630AEB00702FE4 /* mute_feed_off.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = mute_feed_off.png; sourceTree = "<group>"; };
@ -1473,6 +1538,7 @@
1761295F1C630AEB00702FE4 /* mute_feed_on@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "mute_feed_on@2x.png"; sourceTree = "<group>"; };
1763E2A023B1BCC900BA080C /* WidgetFeed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetFeed.swift; sourceTree = "<group>"; };
1763E2A223B1CEB600BA080C /* WidgetBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetBarView.swift; sourceTree = "<group>"; };
17654E452B02C08700F61B2B /* NewsBlur Alpha Widget.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "NewsBlur Alpha Widget.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
176A5C7924F8BD1B009E8DF9 /* DetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = "<group>"; };
17731A9C23DFAD3D00759A7D /* ImportExportPreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportExportPreferences.swift; sourceTree = "<group>"; };
177551D3238E228A00E27818 /* Old NewsBlur Latest.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Old NewsBlur Latest.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
@ -1494,8 +1560,11 @@
1788939C249332E6004CBA4E /* g_icn_search.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = g_icn_search.png; sourceTree = "<group>"; };
1791C21426C4C7BC00D815AA /* WidgetStoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetStoryView.swift; sourceTree = "<group>"; };
17997C5727A8FDD100483E69 /* WidgetDebugTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetDebugTimer.swift; sourceTree = "<group>"; };
179A88012B48E64900916CF4 /* ToolbarDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolbarDelegate.swift; sourceTree = "<group>"; };
179DD9CC23DFD20E007BFD21 /* BridgingHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BridgingHeader.h; path = "Other Sources/BridgingHeader.h"; sourceTree = "<group>"; };
179DD9CE23DFDD51007BFD21 /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; };
17A051892C095B20000994E9 /* AuxSceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuxSceneDelegate.swift; sourceTree = "<group>"; };
17A0518C2C095E78000994E9 /* AuxInterface.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = AuxInterface.storyboard; sourceTree = "<group>"; };
17A396D824F86A8F0023C9E2 /* MainInterface.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = MainInterface.storyboard; sourceTree = "<group>"; };
17A92A3B289B7C6B00AB0A78 /* saved-stories@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "saved-stories@2x.png"; sourceTree = "<group>"; };
17AACFD722279A3900DE6EA4 /* autoscroll_resume@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "autoscroll_resume@2x.png"; sourceTree = "<group>"; };
@ -1513,6 +1582,10 @@
17B33D1627D97281009108AD /* g_icn_folder_widget.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = g_icn_folder_widget.png; sourceTree = "<group>"; };
17B33D1727D97282009108AD /* g_icn_folder_widget@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "g_icn_folder_widget@2x.png"; sourceTree = "<group>"; };
17B96BE624304F72009A8EED /* Story Notification Service Extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Story Notification Service Extension.entitlements"; sourceTree = "<group>"; };
17BC56A62BBE4A5600A30C41 /* TrainerWord.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrainerWord.swift; sourceTree = "<group>"; };
17BC56A92BBF6BC000A30C41 /* Feed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Feed.swift; sourceTree = "<group>"; };
17BC56AC2BBF6C0000A30C41 /* StoryCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoryCache.swift; sourceTree = "<group>"; };
17BC56AF2BBF6C2200A30C41 /* StorySettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorySettings.swift; sourceTree = "<group>"; };
17BD3BA42271102500F615EC /* Intents.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Intents.framework; path = System/Library/Frameworks/Intents.framework; sourceTree = SDKROOT; };
17BD3BA62271122800F615EC /* CoreSpotlight.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreSpotlight.framework; path = System/Library/Frameworks/CoreSpotlight.framework; sourceTree = SDKROOT; };
17BD3BA82271125400F615EC /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; };
@ -1593,6 +1666,8 @@
17EB505E1BE46A900021358B /* FontListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FontListViewController.h; sourceTree = "<group>"; };
17EB505F1BE46A900021358B /* FontListViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FontListViewController.m; sourceTree = "<group>"; };
17EB50611BE46BB00021358B /* FontListViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = FontListViewController.xib; path = Classes/FontListViewController.xib; sourceTree = SOURCE_ROOT; };
17EE11C72B27FA0C00E7C0CC /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = Settings.bundle; path = mac/Settings.bundle; sourceTree = "<group>"; };
17EE11CD2B28011D00E7C0CC /* Credits.rtf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.rtf; name = Credits.rtf; path = mac/Credits.rtf; sourceTree = "<group>"; };
17F156701BDABBF60092EBFD /* safari_shadow@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "safari_shadow@2x.png"; sourceTree = "<group>"; };
17F363EF238E417300D5379D /* WidgetExtensionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WidgetExtensionViewController.swift; sourceTree = "<group>"; };
17F39EA6264754CC004B46D1 /* image_preview_large_left.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = image_preview_large_left.png; sourceTree = "<group>"; };
@ -1766,7 +1841,7 @@
784B50EA127E3F68008F90EA /* LoginViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LoginViewController.h; sourceTree = "<group>"; };
784B50EB127E3F68008F90EA /* LoginViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LoginViewController.m; sourceTree = "<group>"; };
788EF355127E5BC80088EDC5 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
8D1107310486CEB800E47090 /* NewsBlur-iPhone-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "NewsBlur-iPhone-Info.plist"; plistStructureDefinitionIdentifier = "com.apple.xcode.plist.structure-definition.iphone.info-plist"; sourceTree = "<group>"; };
8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; plistStructureDefinitionIdentifier = "com.apple.xcode.plist.structure-definition.iphone.info-plist"; sourceTree = "<group>"; };
E160F0551C9DAC2C00CB96DF /* UIViewController+HidePopover.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIViewController+HidePopover.h"; path = "Other Sources/UIViewController+HidePopover.h"; sourceTree = "<group>"; };
E160F0561C9DAC2C00CB96DF /* UIViewController+HidePopover.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIViewController+HidePopover.m"; path = "Other Sources/UIViewController+HidePopover.m"; sourceTree = "<group>"; };
E1C44B09200147ED002128AD /* StoryTitleAttributedString.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StoryTitleAttributedString.h; sourceTree = "<group>"; };
@ -2071,7 +2146,7 @@
FF8A949A1DE3BB77000A4C31 /* NotificationService.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NotificationService.m; sourceTree = "<group>"; };
FF8A949C1DE3BB77000A4C31 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
FF8AFE561CAC73C9005D9B40 /* unread_blue@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "unread_blue@3x.png"; sourceTree = "<group>"; };
FF8C49921BBC9D140010D894 /* NewsBlur.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = NewsBlur.entitlements; path = NewsBlur/NewsBlur.entitlements; sourceTree = "<group>"; };
FF8C49921BBC9D140010D894 /* App.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = App.entitlements; sourceTree = "<group>"; };
FF8D1EA51BAA304E00725D8A /* Reachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Reachability.h; sourceTree = "<group>"; };
FF8D1EA61BAA304E00725D8A /* Reachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Reachability.m; sourceTree = "<group>"; };
FF8D1EBD1BAA311000725D8A /* SBJson4.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJson4.h; sourceTree = "<group>"; };
@ -2313,6 +2388,15 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
17654E3B2B02C08700F61B2B /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
17654E3C2B02C08700F61B2B /* SwiftUI.framework in Frameworks */,
17654E3D2B02C08700F61B2B /* WidgetKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
177551D0238E228A00E27818 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@ -2513,6 +2597,7 @@
177551D3238E228A00E27818 /* Old NewsBlur Latest.appex */,
173CB30D26BCE94700BA872A /* NewsBlur Widget.appex */,
175792E42930605500490924 /* NB Alpha.app */,
17654E452B02C08700F61B2B /* NewsBlur Alpha Widget.appex */,
);
name = Products;
sourceTree = "<group>";
@ -2531,8 +2616,6 @@
173CB31226BCE94700BA872A /* Widget Extension */,
29B97323FDCFA39411CA2CEA /* Frameworks */,
19C28FACFE9D520D11CA2CBB /* Products */,
FF8C49921BBC9D140010D894 /* NewsBlur.entitlements */,
8D1107310486CEB800E47090 /* NewsBlur-iPhone-Info.plist */,
);
name = CustomTemplate;
sourceTree = "<group>";
@ -2636,14 +2719,20 @@
isa = PBXGroup;
children = (
17A396D824F86A8F0023C9E2 /* MainInterface.storyboard */,
17A0518C2C095E78000994E9 /* AuxInterface.storyboard */,
FF191E4E18A323F400473252 /* Images.xcassets */,
17EE11CD2B28011D00E7C0CC /* Credits.rtf */,
430C4BBE15D7208000B9F63B /* FTUX */,
431B857815A132C500DCE497 /* js */,
431B857715A132BE00DCE497 /* css */,
1753696F1BE535CF00904D00 /* fonts */,
431B857615A132B600DCE497 /* Images */,
FFF1E4C717750BDD00BF59D3 /* Settings.bundle */,
17EE11C72B27FA0C00E7C0CC /* Settings.bundle */,
E1D123FD1C66753D00434F40 /* Localizable.stringsdict */,
FF8C49921BBC9D140010D894 /* App.entitlements */,
8D1107310486CEB800E47090 /* Info.plist */,
17179E282BD6F86C006B18D5 /* PrivacyInfo.xcprivacy */,
);
path = Resources;
sourceTree = "<group>";
@ -2769,6 +2858,10 @@
17EB505F1BE46A900021358B /* FontListViewController.m */,
78095EC6128F30B500230C8E /* OriginalStoryViewController.h */,
78095EC7128F30B500230C8E /* OriginalStoryViewController.m */,
175DC6AE2BBB87D200B3708F /* TrainerViewController.swift */,
171904B42BBC8D4E004CCC96 /* TrainerView.swift */,
171904B72BBCA712004CCC96 /* TrainerCapsule.swift */,
17BC56A62BBE4A5600A30C41 /* TrainerWord.swift */,
FF67D3B0168924C40057A7DA /* TrainerViewController.h */,
FF67D3B1168924C40057A7DA /* TrainerViewController.m */,
FF6282131A11613900271FDB /* UserTagsViewController.h */,
@ -3230,7 +3323,10 @@
43D8189F15B9404D00733444 /* Models */ = {
isa = PBXGroup;
children = (
17BC56A92BBF6BC000A30C41 /* Feed.swift */,
172ECBF6298B1239006371BC /* Story.swift */,
17BC56AC2BBF6C0000A30C41 /* StoryCache.swift */,
17BC56AF2BBF6C2200A30C41 /* StorySettings.swift */,
);
name = Models;
sourceTree = "<group>";
@ -3274,6 +3370,9 @@
175792E62930611B00490924 /* LaunchScreenDev.xib */,
1D3623240D0F684500981E51 /* NewsBlurAppDelegate.h */,
1D3623250D0F684500981E51 /* NewsBlurAppDelegate.m */,
17150E1D2B05775A004D5309 /* SceneDelegate.swift */,
17A051892C095B20000994E9 /* AuxSceneDelegate.swift */,
179A88012B48E64900916CF4 /* ToolbarDelegate.swift */,
FFD1D72F1459B63500E46F89 /* BaseViewController.h */,
FFD1D7301459B63500E46F89 /* BaseViewController.m */,
17C074941C14C46B00CFCDB7 /* ThemeManager.h */,
@ -3696,10 +3795,12 @@
175790622930605500490924 /* Resources */,
175792252930605500490924 /* Sources */,
175792C12930605500490924 /* Frameworks */,
17654E4A2B02C0A800F61B2B /* Embed Foundation Extensions */,
);
buildRules = (
);
dependencies = (
17654E492B02C0A800F61B2B /* PBXTargetDependency */,
);
name = "NewsBlur Alpha";
packageProductDependencies = (
@ -3708,6 +3809,23 @@
productReference = 175792E42930605500490924 /* NB Alpha.app */;
productType = "com.apple.product-type.application";
};
17654E312B02C08700F61B2B /* NewsBlur Alpha Widget */ = {
isa = PBXNativeTarget;
buildConfigurationList = 17654E422B02C08700F61B2B /* Build configuration list for PBXNativeTarget "NewsBlur Alpha Widget" */;
buildPhases = (
17654E322B02C08700F61B2B /* Sources */,
17654E3B2B02C08700F61B2B /* Frameworks */,
17654E3E2B02C08700F61B2B /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "NewsBlur Alpha Widget";
productName = WidgetExtension;
productReference = 17654E452B02C08700F61B2B /* NewsBlur Alpha Widget.appex */;
productType = "com.apple.product-type.app-extension";
};
177551D2238E228A00E27818 /* Old Widget Extension */ = {
isa = PBXNativeTarget;
buildConfigurationList = 177551E2238E228A00E27818 /* Build configuration list for PBXNativeTarget "Old Widget Extension" */;
@ -3773,8 +3891,9 @@
29B97313FDCFA39411CA2CEA /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastSwiftUpdateCheck = 1120;
LastUpgradeCheck = 1420;
LastUpgradeCheck = 1540;
ORGANIZATIONNAME = NewsBlur;
TargetAttributes = {
173CB30C26BCE94700BA872A = {
@ -3864,6 +3983,7 @@
FF8A94961DE3BB77000A4C31 /* Story Notification Service Extension */,
177551D2238E228A00E27818 /* Old Widget Extension */,
173CB30C26BCE94700BA872A /* NewsBlur Widget */,
17654E312B02C08700F61B2B /* NewsBlur Alpha Widget */,
);
};
/* End PBXProject section */
@ -3895,10 +4015,13 @@
buildActionMask = 2147483647;
files = (
175790632930605500490924 /* ChronicleSSm-Book.otf in Resources */,
1757920D2930605500490924 /* Settings.bundle in Resources */,
175790642930605500490924 /* WhitneySSm-Book-Bas.otf in Resources */,
175790652930605500490924 /* ChronicleSSm-BookItalic.otf in Resources */,
175790662930605500490924 /* WhitneySSm-Medium-Bas.otf in Resources */,
17A0518E2C095E78000994E9 /* AuxInterface.storyboard in Resources */,
175790672930605500490924 /* icons8-stack-of-paper-100.png in Resources */,
17EE11C92B27FA0C00E7C0CC /* Settings.bundle in Resources */,
175790682930605500490924 /* WhitneySSm-MediumItalic-Bas.otf in Resources */,
175790692930605500490924 /* WhitneySSm-BookItalic-Bas.otf in Resources */,
1757906A2930605500490924 /* barbutton_sort_desc@3x.png in Resources */,
@ -4106,6 +4229,7 @@
175791342930605500490924 /* g_icn_greensun@2x.png in Resources */,
175791352930605500490924 /* ak-icon-infrequent.png in Resources */,
175791362930605500490924 /* nav_icn_add.png in Resources */,
17EE11CF2B28011D00E7C0CC /* Credits.rtf in Resources */,
175791372930605500490924 /* icons8-pyramids-100.png in Resources */,
175791382930605500490924 /* ak-icon-global.png in Resources */,
175791392930605500490924 /* content_preview_large@2x.png in Resources */,
@ -4284,6 +4408,7 @@
175791E42930605500490924 /* train@2x.png in Resources */,
175791E52930605500490924 /* logo_40.png in Resources */,
175791E62930605500490924 /* menu_icn_share.png in Resources */,
17179E2A2BD6F86D006B18D5 /* PrivacyInfo.xcprivacy in Resources */,
175791E72930605500490924 /* autoscroll_pause.png in Resources */,
175791E82930605500490924 /* safari@3x.png in Resources */,
175791E92930605500490924 /* g_icn_eating.png in Resources */,
@ -4322,7 +4447,6 @@
1757920A2930605500490924 /* g_icn_textview_black.png in Resources */,
1757920B2930605500490924 /* logo_58.png in Resources */,
1757920C2930605500490924 /* logo_144.png in Resources */,
1757920D2930605500490924 /* Settings.bundle in Resources */,
1757920E2930605500490924 /* menu_icn_preferences.png in Resources */,
1757920F2930605500490924 /* menu_icn_preferences@2x.png in Resources */,
175792102930605500490924 /* checkmark.png in Resources */,
@ -4349,6 +4473,16 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
17654E3E2B02C08700F61B2B /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
17654E3F2B02C08700F61B2B /* WhitneySSm-Medium-Bas.otf in Resources */,
17654E402B02C08700F61B2B /* Assets.xcassets in Resources */,
17654E412B02C08700F61B2B /* WhitneySSm-Book-Bas.otf in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
177551D1238E228A00E27818 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@ -4410,6 +4544,7 @@
433323B9158901A40025064D /* fountain_pen@2x.png in Resources */,
433323BB158901C10025064D /* login_background.png in Resources */,
FF83FF151FB54693008DAC0F /* g_icn_lightning.png in Resources */,
17EE11CE2B28011D00E7C0CC /* Credits.rtf in Resources */,
17BE5A761C5DDA8C0075F92C /* barbutton_sort_desc@2x.png in Resources */,
433323BE1589022C0025064D /* user.png in Resources */,
433323BF1589022C0025064D /* user@2x.png in Resources */,
@ -4615,6 +4750,7 @@
17EB505D1BE4411E0021358B /* choose_font@2x.png in Resources */,
FFC5F30C16E2D2C2007AC72C /* story_share_appnet_active@2x.png in Resources */,
FFC5F30D16E2D2C2007AC72C /* story_share_appnet.png in Resources */,
17EE11C82B27FA0C00E7C0CC /* Settings.bundle in Resources */,
FFC5F30E16E2D2C2007AC72C /* story_share_appnet@2x.png in Resources */,
1740C6A11C1110BA005EA453 /* theme_color_medium-sel@2x.png in Resources */,
17876BA01C9911D40055DD15 /* g_icn_folder_sm.png in Resources */,
@ -4661,6 +4797,7 @@
FF22FE6E16E554540046165A /* barbutton_refresh.png in Resources */,
FF22FE6F16E554540046165A /* barbutton_refresh@2x.png in Resources */,
FF22FE7216E554FD0046165A /* barbutton_sendto.png in Resources */,
17A0518D2C095E78000994E9 /* AuxInterface.storyboard in Resources */,
FFB9BE4D17F4B65B00FE0A36 /* logo_120@2x.png in Resources */,
FF03AFFF19F881380063002A /* ARChromeActivity@2x.png in Resources */,
FF83FF1B1FB54693008DAC0F /* g_icn_privacy.png in Resources */,
@ -4689,6 +4826,7 @@
FF03AFE419F87A770063002A /* g_icn_folder_read.png in Resources */,
FFDD845E16E8871A000AA0A2 /* menu_icn_fetch_subscribers.png in Resources */,
FF5ACC211DE5ED7500FBD044 /* menu_icn_notifications.png in Resources */,
17179E292BD6F86C006B18D5 /* PrivacyInfo.xcprivacy in Resources */,
FFDD845F16E8871A000AA0A2 /* menu_icn_fetch_subscribers@2x.png in Resources */,
FFDD846016E8871A000AA0A2 /* menu_icn_fetch.png in Resources */,
FFDD846116E8871A000AA0A2 /* menu_icn_fetch@2x.png in Resources */,
@ -4911,6 +5049,7 @@
175792542930605500490924 /* IASKPSTextFieldSpecifierViewCell.m in Sources */,
175792552930605500490924 /* SBJson4StreamWriter.m in Sources */,
175792562930605500490924 /* FontSettingsViewController.m in Sources */,
17BC56B12BBF6C2200A30C41 /* StorySettings.swift in Sources */,
175792572930605500490924 /* IASKAppSettingsViewController.m in Sources */,
175792582930605500490924 /* MarkReadMenuViewController.m in Sources */,
175792592930605500490924 /* SSWAnimator.m in Sources */,
@ -4932,10 +5071,14 @@
175792692930605500490924 /* main.m in Sources */,
1757926A2930605500490924 /* MBProgressHUD.m in Sources */,
1757926B2930605500490924 /* NSString+HTML.m in Sources */,
171904B92BBCA712004CCC96 /* TrainerCapsule.swift in Sources */,
1757926C2930605500490924 /* IASKSettingsReader.m in Sources */,
1757926D2930605500490924 /* UserTagsViewController.m in Sources */,
1757926E2930605500490924 /* StringHelper.m in Sources */,
1757926F2930605500490924 /* TransparentToolbar.m in Sources */,
179A88032B48E64A00916CF4 /* ToolbarDelegate.swift in Sources */,
175DC6B02BBB87D200B3708F /* TrainerViewController.swift in Sources */,
171904B62BBC8D4E004CCC96 /* TrainerView.swift in Sources */,
175792702930605500490924 /* THCircularProgressView.m in Sources */,
175792712930605500490924 /* IASKSpecifier.m in Sources */,
175792722930605500490924 /* UIView+ViewController.m in Sources */,
@ -4970,6 +5113,7 @@
1757928C2930605500490924 /* PINMemoryCache.m in Sources */,
1757928D2930605500490924 /* SiteCell.m in Sources */,
1757928E2930605500490924 /* SloppySwiper.m in Sources */,
17BC56A82BBE4A5600A30C41 /* TrainerWord.swift in Sources */,
1757928F2930605500490924 /* UIViewController+HidePopover.m in Sources */,
175792902930605500490924 /* FolderTitleView.m in Sources */,
175792912930605500490924 /* HorizontalPageDelegate.swift in Sources */,
@ -5008,16 +5152,20 @@
175792B02930605500490924 /* FMResultSet.m in Sources */,
175792B12930605500490924 /* NBNotifier.m in Sources */,
175792B22930605500490924 /* TUSafariActivity.m in Sources */,
17BC56AE2BBF6C0000A30C41 /* StoryCache.swift in Sources */,
175792B32930605500490924 /* PremiumViewController.m in Sources */,
175792B42930605500490924 /* NBLoadingCell.m in Sources */,
175792B52930605500490924 /* IASKTextViewCell.m in Sources */,
175792B62930605500490924 /* StoriesCollection.m in Sources */,
175792B72930605500490924 /* IASKTextView.m in Sources */,
17A0518B2C095B20000994E9 /* AuxSceneDelegate.swift in Sources */,
175792B82930605500490924 /* OfflineSyncUnreads.m in Sources */,
175792B92930605500490924 /* IASKPSSliderSpecifierViewCell.m in Sources */,
17BC56AB2BBF6BC000A30C41 /* Feed.swift in Sources */,
175792BA2930605500490924 /* OfflineFetchStories.m in Sources */,
175792BB2930605500490924 /* OfflineFetchText.m in Sources */,
175792BC2930605500490924 /* OfflineFetchImages.m in Sources */,
17150E1F2B05775A004D5309 /* SceneDelegate.swift in Sources */,
175792BD2930605500490924 /* SplitViewDelegate.swift in Sources */,
175792BE2930605500490924 /* Reachability.m in Sources */,
175792BF2930605500490924 /* NBSwipeableCell.m in Sources */,
@ -5025,6 +5173,21 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
17654E322B02C08700F61B2B /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
17654E332B02C08700F61B2B /* WidgetLoader.swift in Sources */,
17654E342B02C08700F61B2B /* WidgetCache.swift in Sources */,
17654E352B02C08700F61B2B /* WidgetExtension.swift in Sources */,
17654E362B02C08700F61B2B /* WidgetStory.swift in Sources */,
17654E372B02C08700F61B2B /* WidgetBarView.swift in Sources */,
17654E382B02C08700F61B2B /* WidgetDebugTimer.swift in Sources */,
17654E392B02C08700F61B2B /* WidgetFeed.swift in Sources */,
17654E3A2B02C08700F61B2B /* WidgetStoryView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
177551CF238E228A00E27818 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@ -5095,6 +5258,7 @@
FF34FD6B1E9D93CB0062F8ED /* IASKPSTextFieldSpecifierViewCell.m in Sources */,
FF8D1ED01BAA311000725D8A /* SBJson4StreamWriter.m in Sources */,
43763AD1158F90B100B3DBE2 /* FontSettingsViewController.m in Sources */,
17BC56B02BBF6C2200A30C41 /* StorySettings.swift in Sources */,
FF34FD601E9D93CB0062F8ED /* IASKAppSettingsViewController.m in Sources */,
17CBD3BF1BF66B6C003FCCAE /* MarkReadMenuViewController.m in Sources */,
FFA045B519CA49D700618DC4 /* SSWAnimator.m in Sources */,
@ -5116,10 +5280,14 @@
43A4C3DC15B00966008787B5 /* main.m in Sources */,
43A4C3DD15B00966008787B5 /* MBProgressHUD.m in Sources */,
43A4C3E115B00966008787B5 /* NSString+HTML.m in Sources */,
171904B82BBCA712004CCC96 /* TrainerCapsule.swift in Sources */,
FF34FD641E9D93CB0062F8ED /* IASKSettingsReader.m in Sources */,
FF6282151A11613900271FDB /* UserTagsViewController.m in Sources */,
43A4C3E315B00966008787B5 /* StringHelper.m in Sources */,
43A4C3E415B00966008787B5 /* TransparentToolbar.m in Sources */,
179A88022B48E64A00916CF4 /* ToolbarDelegate.swift in Sources */,
175DC6AF2BBB87D200B3708F /* TrainerViewController.swift in Sources */,
171904B52BBC8D4E004CCC96 /* TrainerView.swift in Sources */,
FFD6604C1BACA45D006E4B8D /* THCircularProgressView.m in Sources */,
FF34FD681E9D93CB0062F8ED /* IASKSpecifier.m in Sources */,
FFA0484419CA73B700618DC4 /* UIView+ViewController.m in Sources */,
@ -5154,6 +5322,7 @@
FF2924E71E932D2900FCFA63 /* PINMemoryCache.m in Sources */,
43CE0F5F15DADB7F00608ED8 /* SiteCell.m in Sources */,
FFA045B419CA49D700618DC4 /* SloppySwiper.m in Sources */,
17BC56A72BBE4A5600A30C41 /* TrainerWord.swift in Sources */,
E160F0571C9DAC2C00CB96DF /* UIViewController+HidePopover.m in Sources */,
FFDE35CC161B8F870034BFDE /* FolderTitleView.m in Sources */,
172AD264251D901D000BB264 /* HorizontalPageDelegate.swift in Sources */,
@ -5192,16 +5361,20 @@
FF753CD3175858FC00344EC9 /* FMResultSet.m in Sources */,
FF6618C8176184560039913B /* NBNotifier.m in Sources */,
FF03AFF319F87F2E0063002A /* TUSafariActivity.m in Sources */,
17BC56AD2BBF6C0000A30C41 /* StoryCache.swift in Sources */,
FF83FF051FB52565008DAC0F /* PremiumViewController.m in Sources */,
FF11045F176950F900502C29 /* NBLoadingCell.m in Sources */,
FF34FD701E9D93CB0062F8ED /* IASKTextViewCell.m in Sources */,
FFAD89C218AC45A100D68567 /* StoriesCollection.m in Sources */,
FF34FD6F1E9D93CB0062F8ED /* IASKTextView.m in Sources */,
17A0518A2C095B20000994E9 /* AuxSceneDelegate.swift in Sources */,
FF855B5B1794B0670098D48A /* OfflineSyncUnreads.m in Sources */,
FF34FD6A1E9D93CB0062F8ED /* IASKPSSliderSpecifierViewCell.m in Sources */,
17BC56AA2BBF6BC000A30C41 /* Feed.swift in Sources */,
FF855B5E1794B0760098D48A /* OfflineFetchStories.m in Sources */,
17362ADD23639B4E00A0FCCC /* OfflineFetchText.m in Sources */,
FF855B611794B0830098D48A /* OfflineFetchImages.m in Sources */,
17150E1E2B05775A004D5309 /* SceneDelegate.swift in Sources */,
170E3CD124F8A664009CE819 /* SplitViewDelegate.swift in Sources */,
FF8D1EA71BAA304E00725D8A /* Reachability.m in Sources */,
FFCDD90117F65A71000C6483 /* NBSwipeableCell.m in Sources */,
@ -5231,6 +5404,11 @@
target = 1749390F1C251BFE003D98AA /* Share Extension */;
targetProxy = 174939191C251BFE003D98AA /* PBXContainerItemProxy */;
};
17654E492B02C0A800F61B2B /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 17654E312B02C08700F61B2B /* NewsBlur Alpha Widget */;
targetProxy = 17654E482B02C0A800F61B2B /* PBXContainerItemProxy */;
};
177551DE238E228A00E27818 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
platformFilter = ios;
@ -5327,6 +5505,7 @@
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
INFOPLIST_FILE = "Widget Extension/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -5337,8 +5516,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.newsblur.NewsBlur.widget;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MACCATALYST = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2,6";
@ -5371,6 +5549,7 @@
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
INFOPLIST_FILE = "Widget Extension/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -5381,8 +5560,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.newsblur.NewsBlur.widget;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MACCATALYST = YES;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2,6";
@ -5438,8 +5616,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MACCATALYST = YES;
TARGETED_DEVICE_FAMILY = "1,2,6";
};
name = Debug;
@ -5486,8 +5663,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MACCATALYST = YES;
TARGETED_DEVICE_FAMILY = "1,2,6";
VALIDATE_PRODUCT = YES;
};
@ -5499,11 +5675,10 @@
ALWAYS_SEARCH_USER_PATHS = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIconDev;
CLANG_ENABLE_OBJC_ARC = YES;
CODE_SIGN_ENTITLEMENTS = NewsBlur/NewsBlur.entitlements;
CODE_SIGN_ENTITLEMENTS = Resources/App.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 152;
DEVELOPMENT_TEAM = HR7P97SD72;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@ -5516,7 +5691,7 @@
GCC_THUMB_SUPPORT = NO;
GCC_VERSION = "";
HEADER_SEARCH_PATHS = "";
INFOPLIST_FILE = "NewsBlur-iPhone-Info.plist";
INFOPLIST_FILE = Resources/Info.plist;
LAUNCH_SCREEN_NAME = LaunchScreenDev;
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
@ -5535,9 +5710,9 @@
PRODUCT_NAME = "NB Alpha";
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "";
STRIP_INSTALLED_PRODUCT = NO;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MACCATALYST = YES;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "Other Sources/BridgingHeader.h";
SWIFT_OBJC_INTERFACE_HEADER_NAME = "NewsBlur-Swift.h";
TARGETED_DEVICE_FAMILY = "1,2,6";
@ -5551,11 +5726,10 @@
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIconDev;
CLANG_ENABLE_OBJC_ARC = YES;
CODE_SIGN_ENTITLEMENTS = NewsBlur/NewsBlur.entitlements;
CODE_SIGN_ENTITLEMENTS = Resources/App.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
CURRENT_PROJECT_VERSION = 152;
DEVELOPMENT_TEAM = HR7P97SD72;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@ -5567,7 +5741,7 @@
GCC_THUMB_SUPPORT = NO;
GCC_VERSION = "";
HEADER_SEARCH_PATHS = "";
INFOPLIST_FILE = "NewsBlur-iPhone-Info.plist";
INFOPLIST_FILE = Resources/Info.plist;
LAUNCH_SCREEN_NAME = LaunchScreenDev;
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
@ -5586,7 +5760,8 @@
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MACCATALYST = YES;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "Other Sources/BridgingHeader.h";
SWIFT_OBJC_INTERFACE_HEADER_NAME = "NewsBlur-Swift.h";
TARGETED_DEVICE_FAMILY = "1,2,6";
@ -5594,6 +5769,101 @@
};
name = Release;
};
17654E432B02C08700F61B2B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = "Widget Extension/WidgetExtension.entitlements";
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = HR7P97SD72;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
INFOPLIST_FILE = "Widget Extension/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.newsblur.NB-Alpha.widget";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SUPPORTS_MACCATALYST = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2,6";
};
name = Debug;
};
17654E442B02C08700F61B2B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = "Widget Extension/WidgetExtension.entitlements";
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = HR7P97SD72;
ENABLE_NS_ASSERTIONS = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
INFOPLIST_FILE = "Widget Extension/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.newsblur.NB-Alpha.widget";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SUPPORTS_MACCATALYST = YES;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2,6";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
177551E0238E228A00E27818 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -5694,11 +5964,10 @@
ALWAYS_SEARCH_USER_PATHS = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_OBJC_ARC = YES;
CODE_SIGN_ENTITLEMENTS = NewsBlur/NewsBlur.entitlements;
CODE_SIGN_ENTITLEMENTS = Resources/App.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 152;
DEVELOPMENT_TEAM = HR7P97SD72;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@ -5711,7 +5980,7 @@
GCC_THUMB_SUPPORT = NO;
GCC_VERSION = "";
HEADER_SEARCH_PATHS = "";
INFOPLIST_FILE = "NewsBlur-iPhone-Info.plist";
INFOPLIST_FILE = Resources/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
@ -5728,9 +5997,9 @@
PRODUCT_NAME = NewsBlur;
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "";
STRIP_INSTALLED_PRODUCT = NO;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MACCATALYST = YES;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "Other Sources/BridgingHeader.h";
TARGETED_DEVICE_FAMILY = "1,2,6";
"WARNING_CFLAGS[arch=*]" = "-Wall";
@ -5744,11 +6013,10 @@
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_OBJC_ARC = YES;
CODE_SIGN_ENTITLEMENTS = NewsBlur/NewsBlur.entitlements;
CODE_SIGN_ENTITLEMENTS = Resources/App.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
CURRENT_PROJECT_VERSION = 152;
DEVELOPMENT_TEAM = HR7P97SD72;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@ -5760,7 +6028,7 @@
GCC_THUMB_SUPPORT = NO;
GCC_VERSION = "";
HEADER_SEARCH_PATHS = "";
INFOPLIST_FILE = "NewsBlur-iPhone-Info.plist";
INFOPLIST_FILE = Resources/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
@ -5777,7 +6045,8 @@
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MACCATALYST = YES;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "Other Sources/BridgingHeader.h";
TARGETED_DEVICE_FAMILY = "1,2,6";
VALIDATE_PRODUCT = YES;
@ -5788,6 +6057,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = NO;
CLANG_ENABLE_MODULES = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
@ -5810,10 +6080,11 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 151;
CURRENT_PROJECT_VERSION = 153;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = "compiler-default";
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@ -5826,13 +6097,12 @@
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LAUNCH_SCREEN_NAME = LaunchScreen;
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 13.0;
MARKETING_VERSION = 13.1;
ONLY_ACTIVE_ARCH = YES;
OTHER_LDFLAGS = "-ObjC";
PROVISIONING_PROFILE = "";
RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = iphoneos;
STRIP_INSTALLED_PRODUCT = NO;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -5843,6 +6113,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = NO;
CLANG_ENABLE_MODULES = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
@ -5865,9 +6136,10 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 151;
CURRENT_PROJECT_VERSION = 153;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = "compiler-default";
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@ -5880,13 +6152,12 @@
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LAUNCH_SCREEN_NAME = LaunchScreen;
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 13.0;
MARKETING_VERSION = 13.1;
ONLY_ACTIVE_ARCH = NO;
OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
OTHER_LDFLAGS = "-ObjC";
PROVISIONING_PROFILE = "";
SDKROOT = iphoneos;
STRIP_INSTALLED_PRODUCT = NO;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 5.0;
@ -5932,8 +6203,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MACCATALYST = YES;
TARGETED_DEVICE_FAMILY = "1,2,6";
};
name = Debug;
@ -5970,8 +6240,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MACCATALYST = YES;
TARGETED_DEVICE_FAMILY = "1,2,6";
VALIDATE_PRODUCT = YES;
};
@ -6007,6 +6276,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
17654E422B02C08700F61B2B /* Build configuration list for PBXNativeTarget "NewsBlur Alpha Widget" */ = {
isa = XCConfigurationList;
buildConfigurations = (
17654E432B02C08700F61B2B /* Debug */,
17654E442B02C08700F61B2B /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
177551E2238E228A00E27818 /* Build configuration list for PBXNativeTarget "Old Widget Extension" */ = {
isa = XCConfigurationList;
buildConfigurations = (

View file

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1540"
version = "2.0">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "17654E312B02C08700F61B2B"
BuildableName = "NewsBlur Alpha Widget.appex"
BlueprintName = "NewsBlur Alpha Widget"
ReferencedContainer = "container:NewsBlur.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "175790592930605500490924"
BuildableName = "NB Alpha.app"
BlueprintName = "NewsBlur Alpha"
ReferencedContainer = "container:NewsBlur.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = ""
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
launchStyle = "0"
askForAppToLaunch = "Yes"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
launchAutomaticallySubstyle = "2">
<EnvironmentVariables>
<EnvironmentVariable
key = "_XCWidgetKind"
value = ""
isEnabled = "YES">
</EnvironmentVariable>
<EnvironmentVariable
key = "_XCWidgetDefaultView"
value = "timeline"
isEnabled = "YES">
</EnvironmentVariable>
<EnvironmentVariable
key = "_XCWidgetFamily"
value = "systemMedium"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
askForAppToLaunch = "Yes"
launchAutomaticallySubstyle = "2">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "17654E312B02C08700F61B2B"
BuildableName = "NewsBlur Alpha Widget.appex"
BlueprintName = "NewsBlur Alpha Widget"
ReferencedContainer = "container:NewsBlur.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1420"
LastUpgradeVersion = "1540"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1420"
LastUpgradeVersion = "1540"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1420"
LastUpgradeVersion = "1540"
wasCreatedForAppExtension = "YES"
version = "1.3">
<BuildAction

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1420"
LastUpgradeVersion = "1540"
wasCreatedForAppExtension = "YES"
version = "2.0">
<BuildAction

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1420"
LastUpgradeVersion = "1540"
wasCreatedForAppExtension = "YES"
version = "2.0">
<BuildAction

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1420"
LastUpgradeVersion = "1540"
wasCreatedForAppExtension = "YES"
version = "2.0">
<BuildAction

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -171,7 +171,7 @@ NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
#pragma mark -
static NSArray * AFHTTPRequestSerializerObservedKeyPaths() {
static NSArray * AFHTTPRequestSerializerObservedKeyPaths(void) {
static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
@ -591,7 +591,7 @@ forHTTPHeaderField:(NSString *)field
#pragma mark -
static NSString * AFCreateMultipartFormBoundary() {
static NSString * AFCreateMultipartFormBoundary(void) {
return [NSString stringWithFormat:@"Boundary+%08X%08X", arc4random(), arc4random()];
}

View file

@ -28,7 +28,7 @@
#define NSFoundationVersionNumber_With_Fixed_5871104061079552_bug NSFoundationVersionNumber_iOS_8_0
#endif
static dispatch_queue_t url_session_manager_creation_queue() {
static dispatch_queue_t url_session_manager_creation_queue(void) {
static dispatch_queue_t af_url_session_manager_creation_queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
@ -49,7 +49,7 @@ static void url_session_manager_create_task_safely(dispatch_block_t block) {
}
}
static dispatch_queue_t url_session_manager_processing_queue() {
static dispatch_queue_t url_session_manager_processing_queue(void) {
static dispatch_queue_t af_url_session_manager_processing_queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
@ -59,7 +59,7 @@ static dispatch_queue_t url_session_manager_processing_queue() {
return af_url_session_manager_processing_queue;
}
static dispatch_group_t url_session_manager_completion_group() {
static dispatch_group_t url_session_manager_completion_group(void) {
static dispatch_group_t af_url_session_manager_completion_group;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{

View file

@ -24,7 +24,7 @@
#import <TargetConditionals.h>
#if TARGET_OS_IOS
#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
#import <UIKit/UIKit.h>

View file

@ -23,7 +23,7 @@
#import "UIRefreshControl+AFNetworking.h"
#import <objc/runtime.h>
#if TARGET_OS_IOS
#if TARGET_OS_IOS && !TARGET_OS_MACCATALYST
#import "AFURLSessionManager.h"

View file

@ -11,6 +11,7 @@
#import <UIKit/UIKit.h>
#import "NSString+HTML.h"
#import "Utilities.h"
#import "NewsBlurAppDelegate.h"
#import "ThemeManager.h"
#import "StoriesCollection.h"

View file

@ -123,7 +123,7 @@ CGRect IASKCGRectSwap(CGRect rect);
- (BOOL)isPad {
BOOL isPad = NO;
#if (__IPHONE_OS_VERSION_MAX_ALLOWED >= 30200)
isPad = [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad;
isPad = [[UIDevice currentDevice] userInterfaceIdiom] != UIUserInterfaceIdiomPhone;
#endif
return isPad;
}

View file

@ -279,7 +279,7 @@
switch (interfaceIdiom) {
case UIUserInterfaceIdiomPad: return @"~ipad";
case UIUserInterfaceIdiomPhone: return @"~iphone";
default: return @"~iphone";
default: return @"~ipad";
}
}

View file

@ -466,7 +466,7 @@ static NSString *const AppExtensionWebViewPageDetails = @"pageDetails";
}
- (UIActivityViewController *)activityViewControllerForItem:(nonnull NSDictionary *)item viewController:(nonnull UIViewController*)viewController sender:(nullable id)sender typeIdentifier:(nonnull NSString *)typeIdentifier {
NSAssert(NO == ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad && sender == nil), @"sender must not be nil on iPad.");
NSAssert(NO == ([[UIDevice currentDevice] userInterfaceIdiom] != UIUserInterfaceIdiomPhone && sender == nil), @"sender must not be nil on iPad.");
NSItemProvider *itemProvider = [[NSItemProvider alloc] initWithItem:item typeIdentifier:typeIdentifier];

View file

@ -1,43 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15705" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15706"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="TrainerViewController">
<connections>
<outlet property="view" destination="1" id="14"/>
<outlet property="webView" destination="GKy-Nf-Wvj" id="zud-uf-diH"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="1">
<rect key="frame" x="0.0" y="0.0" width="320" height="504"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<wkWebView contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="GKy-Nf-Wvj" customClass="TrainerWebView">
<rect key="frame" x="0.0" y="0.0" width="320" height="504"/>
<color key="backgroundColor" red="0.36078431370000003" green="0.38823529410000002" blue="0.4039215686" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<wkWebViewConfiguration key="configuration">
<audiovisualMediaTypes key="mediaTypesRequiringUserActionForPlayback" none="YES"/>
<wkPreferences key="preferences"/>
</wkWebViewConfiguration>
</wkWebView>
</subviews>
<color key="backgroundColor" cocoaTouchSystemColor="scrollViewTexturedBackgroundColor"/>
<constraints>
<constraint firstItem="GKy-Nf-Wvj" firstAttribute="top" secondItem="1" secondAttribute="top" id="1a7-1T-kvM"/>
<constraint firstItem="GKy-Nf-Wvj" firstAttribute="leading" secondItem="1" secondAttribute="leading" id="TyU-gH-Y2q"/>
<constraint firstAttribute="trailing" secondItem="GKy-Nf-Wvj" secondAttribute="trailing" id="awn-3M-7sd"/>
<constraint firstAttribute="bottom" secondItem="GKy-Nf-Wvj" secondAttribute="bottom" id="r5q-ka-FVF"/>
</constraints>
<color key="backgroundColor" systemColor="scrollViewTexturedBackgroundColor"/>
<nil key="simulatedStatusBarMetrics"/>
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" prompted="NO"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="-548" y="153"/>
</view>
</objects>
<resources>
<systemColor name="scrollViewTexturedBackgroundColor">
<color red="0.43529411764705883" green="0.44313725490196076" blue="0.47450980392156861" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
</resources>
</document>

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Y6W-OH-hqX">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="s0d-6b-0kx">
<objects>
<viewController id="Y6W-OH-hqX" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="5EZ-qb-Rvc">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<viewLayoutGuide key="safeArea" id="vDu-zF-Fre"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Ief-a0-LHa" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="37" y="-17"/>
</scene>
</scenes>
<resources>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>

View file

@ -148,6 +148,25 @@
<string>ChronicleSSm-MediumItalic.otf</string>
<string>ChronicleSSm-BookItalic.otf</string>
</array>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<true/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>MainInterface</string>
</dict>
</array>
</dict>
</dict>
<key>UIApplicationShortcutItems</key>
<array>
<dict>
@ -189,8 +208,9 @@
</array>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>audio</string>
<string>fetch</string>
<string>processing</string>
</array>
<key>UILaunchStoryboardName</key>
<string>$(LAUNCH_SCREEN_NAME)</string>

View file

@ -1,11 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="22154" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="6pv-7g-17r">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="6pv-7g-17r" initialMenu="YWi-Ju-M3z">
<device id="ipad11_0rounded" orientation="landscape" layout="fullscreen" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22130"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="UIMenu" message="Requires Xcode 11 or later." minToolsVersion="11.0" requiredIntegratedClassName="UICommandDiff"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
@ -14,39 +15,39 @@
<objects>
<viewController storyboardIdentifier="DetailViewController" useStoryboardIdentifierAsRestorationIdentifier="YES" id="djW-7k-haK" customClass="DetailViewController" customModule="NewsBlur" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jTZ-4O-xyT">
<rect key="frame" x="0.0" y="0.0" width="818.5" height="834"/>
<rect key="frame" x="0.0" y="0.0" width="514" height="834"/>
<subviews>
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="bPa-u1-Aml">
<rect key="frame" x="0.0" y="74" width="1194" height="580"/>
<rect key="frame" x="0.0" y="74" width="1210" height="580"/>
<connections>
<segue destination="afA-W0-XsQ" kind="embed" id="jh9-xN-7qk"/>
</connections>
</containerView>
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5M7-bO-MVK">
<rect key="frame" x="0.0" y="666" width="1194" height="168"/>
<rect key="frame" x="0.0" y="666" width="1210" height="168"/>
<connections>
<segue destination="1tQ-fy-A3c" kind="embed" id="VHY-vp-wSt"/>
</connections>
</containerView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="tFd-Uv-aMP">
<rect key="frame" x="0.0" y="654" width="1194" height="12"/>
<rect key="frame" x="0.0" y="654" width="1210" height="12"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="qGY-je-8bb">
<rect key="frame" x="0.0" y="-1" width="1194" height="1"/>
<rect key="frame" x="0.0" y="-1" width="1210" height="1"/>
<color key="backgroundColor" red="0.75644385810000003" green="0.75644385810000003" blue="0.75644385810000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="1" id="rXL-Rl-xkq"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="bSN-6S-SA0">
<rect key="frame" x="0.0" y="12" width="1194" height="1"/>
<rect key="frame" x="0.0" y="12" width="1210" height="1"/>
<color key="backgroundColor" red="0.75644385810000003" green="0.75644385810000003" blue="0.75644385810000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="1" id="tto-L2-TaQ"/>
</constraints>
</view>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="drag_icon.png" translatesAutoresizingMaskIntoConstraints="NO" id="xeQ-Iz-FR0">
<rect key="frame" x="580" y="2" width="34" height="9"/>
<rect key="frame" x="588" y="2" width="34" height="9"/>
<constraints>
<constraint firstAttribute="height" constant="9" id="dFD-2h-iag"/>
<constraint firstAttribute="width" constant="34" id="wAd-rJ-dVL"/>
@ -226,7 +227,10 @@
<navigationItem key="navigationItem" id="ejU-V5-SiF"/>
<connections>
<outlet property="addBarButton" destination="Wu8-MQ-nN6" id="mk5-Nm-bYF"/>
<outlet property="feedTitlesLeadingConstraint" destination="mcu-rd-QiB" id="lNY-Ya-6pE"/>
<outlet property="feedTitlesTable" destination="Gil-Jz-die" id="Jby-a1-If4"/>
<outlet property="feedTitlesTopConstraint" destination="YMn-QK-fCb" id="Bjn-sa-Qdx"/>
<outlet property="feedTitlesTrailingConstraint" destination="YOq-sh-lXt" id="nLy-3P-sgq"/>
<outlet property="feedViewToolbar" destination="pWr-U1-GMQ" id="FFI-k8-5iO"/>
<outlet property="innerView" destination="hRx-bd-6Da" id="vd8-hw-hZT"/>
<outlet property="intelligenceControl" destination="ADC-ot-Aao" id="8EU-ja-Dan"/>
@ -239,6 +243,605 @@
</objects>
<point key="canvasLocation" x="180.90452261306532" y="285.61151079136692"/>
</scene>
<!--Root-->
<scene sceneID="eNE-X1-NXV">
<objects>
<menu isSystemItem="YES" title="Root" identifier="com.apple.menu.root" id="YWi-Ju-M3z" sceneMemberID="viewController">
<children>
<menu isSystemItem="YES" title="File" identifier="com.apple.menu.file" id="cyC-FU-wa2">
<children>
<menu anchor="com.apple.menu.close" id="ksZ-gH-4gO">
<menuOptions key="menuOptions" displayInline="YES"/>
<children>
<command title="New Site" input="n" id="Y3F-bj-KeX">
<keyModifierFlags key="modifierFlags" option="YES" command="YES"/>
<connections>
<action selector="newSite:" destination="J28-e1-gcC" id="eEX-pv-sl0"/>
</connections>
</command>
<command title="Reload Sites" input="r" id="9ee-Z4-HxV">
<keyModifierFlags key="modifierFlags" command="YES"/>
<connections>
<action selector="reloadFeeds:" destination="J28-e1-gcC" id="Mn6-7w-6qY"/>
</connections>
</command>
</children>
</menu>
<menu anchor="com.apple.menu.close" id="6cc-W3-a06">
<menuOptions key="menuOptions" displayInline="YES"/>
<children>
<command title="Mute Sites" input="M" id="M7P-gv-spA">
<keyModifierFlags key="modifierFlags" command="YES"/>
<connections>
<action selector="showMuteSites:" destination="J28-e1-gcC" id="sE9-iR-0dr"/>
</connections>
</command>
<command title="Organize Sites" input="O" id="Wte-kG-AH2">
<keyModifierFlags key="modifierFlags" command="YES"/>
<connections>
<action selector="showOrganizeSites:" destination="J28-e1-gcC" id="9cl-DR-gBb"/>
</connections>
</command>
<command title="Widget Sites" input="W" id="4CD-Pc-fDT">
<keyModifierFlags key="modifierFlags" command="YES"/>
<connections>
<action selector="showWidgetSites:" destination="J28-e1-gcC" id="V7w-If-8Cv"/>
</connections>
</command>
</children>
</menu>
<menu anchor="com.apple.menu.close" id="obB-Ib-5UM">
<menuOptions key="menuOptions" displayInline="YES"/>
<children>
<command title="Notifications" input="N" id="2ax-3D-3zm">
<keyModifierFlags key="modifierFlags" command="YES"/>
<connections>
<action selector="showNotifications:" destination="J28-e1-gcC" id="GHY-vF-j44"/>
</connections>
</command>
<command title="Find Friends" input="F" id="0eN-vR-Gok">
<keyModifierFlags key="modifierFlags" command="YES"/>
<connections>
<action selector="showFindFriends:" destination="J28-e1-gcC" id="b0r-KD-HkV"/>
</connections>
</command>
</children>
</menu>
<menu anchor="com.apple.menu.close" id="WdP-hc-0fU">
<menuOptions key="menuOptions" displayInline="YES"/>
<children>
<command title="NewsBlur Premium" id="HGH-9Q-Dzh">
<connections>
<action selector="showPremium:" destination="J28-e1-gcC" id="adm-3i-APm"/>
</connections>
</command>
</children>
</menu>
<menu anchor="com.apple.menu.close" id="cRW-qu-7aj">
<menuOptions key="menuOptions" displayInline="YES"/>
<children>
<command title="Logout NewsBlur" input="L" id="kvb-M9-tEl">
<keyModifierFlags key="modifierFlags" command="YES"/>
<connections>
<action selector="showLogout:" destination="J28-e1-gcC" id="wkE-Rx-OAt"/>
</connections>
</command>
</children>
</menu>
<menu isSystemItem="YES" title="Print" identifier="com.apple.menu.print" id="L8Z-gR-6rG">
<menuOptions key="menuOptions" displayInline="YES"/>
<systemMenuChildDeletions>
<itemDeletion anchorAction="_saveDocumentToPDF:"/>
<itemDeletion anchorAction="_printDocument:"/>
</systemMenuChildDeletions>
</menu>
</children>
<systemMenuChildDeletions>
<menuDeletion anchor="com.apple.menu.new-scene"/>
<menuDeletion anchor="com.apple.menu.open"/>
<menuDeletion anchor="com.apple.menu.document"/>
</systemMenuChildDeletions>
</menu>
<menu isSystemItem="YES" title="Edit" identifier="com.apple.menu.edit" id="a13-uu-myc">
<children>
<menu anchor="com.apple.menu.standard-edit" id="rH8-0t-SpF">
<menuOptions key="menuOptions" displayInline="YES"/>
<children>
<command title="Find in Sites" input="f" id="Oy9-Js-Eoz">
<keyModifierFlags key="modifierFlags" option="YES" command="YES"/>
<connections>
<action selector="findInFeeds:" destination="J28-e1-gcC" id="3J9-2o-DYH"/>
</connections>
</command>
<command title="Find in Feed" input="f" id="2M3-xa-Uu2">
<keyModifierFlags key="modifierFlags" command="YES"/>
<connections>
<action selector="findInFeedDetail:" destination="J28-e1-gcC" id="KMi-J9-3OA"/>
</connections>
</command>
</children>
</menu>
</children>
<systemMenuChildDeletions>
<menuDeletion anchor="com.apple.menu.find"/>
</systemMenuChildDeletions>
</menu>
<menu isSystemItem="YES" title="View" identifier="com.apple.menu.view" id="pdl-gx-X6C">
<children>
<menu title="Columns" id="Zsw-UK-Yxk">
<children>
<command title="Automatic" propertyListString="auto" menuElementState="on" input="4" id="6Vl-Xq-5Ko">
<keyModifierFlags key="modifierFlags" option="YES" command="YES"/>
<connections>
<action selector="chooseColumns:" destination="J28-e1-gcC" id="vNz-nO-9sE"/>
</connections>
</command>
<command title="Three" propertyListString="tile" input="3" id="UGQ-RS-mbs">
<keyModifierFlags key="modifierFlags" option="YES" command="YES"/>
<connections>
<action selector="chooseColumns:" destination="J28-e1-gcC" id="ihd-Wz-zxe"/>
</connections>
</command>
<command title="Two" propertyListString="displace" input="2" id="coF-9M-sfo">
<keyModifierFlags key="modifierFlags" option="YES" command="YES"/>
<connections>
<action selector="chooseColumns:" destination="J28-e1-gcC" id="ejV-aM-O5L"/>
</connections>
</command>
<command title="One" propertyListString="overlay" input="1" id="uQ4-TQ-9rv">
<keyModifierFlags key="modifierFlags" option="YES" command="YES"/>
<connections>
<action selector="chooseColumns:" destination="J28-e1-gcC" id="hwz-R7-v1M"/>
</connections>
</command>
</children>
</menu>
<menu title="Layout" id="2UT-ZB-x5H">
<children>
<command title="Left" propertyListString="titles_on_left" menuElementState="on" id="SHF-Gm-yHM">
<connections>
<action selector="chooseLayout:" destination="J28-e1-gcC" id="v7b-YA-qHb"/>
</connections>
</command>
<command title="Top" propertyListString="titles_on_top" id="Zeq-Nu-dqs">
<connections>
<action selector="chooseLayout:" destination="J28-e1-gcC" id="E0V-4w-fMx"/>
</connections>
</command>
<command title="Bottom" propertyListString="titles_on_bottom" id="iQo-Uv-UPm">
<connections>
<action selector="chooseLayout:" destination="J28-e1-gcC" id="APi-KB-zyb"/>
</connections>
</command>
<command title="Grid" propertyListString="titles_in_grid" id="hc6-3c-pEr">
<connections>
<action selector="chooseLayout:" destination="J28-e1-gcC" id="lPQ-q9-ULB"/>
</connections>
</command>
</children>
</menu>
<menu title="Show" id="yxK-59-1Mg">
<children>
<command title="All Stories" propertyListString="0" menuElementState="on" input="1" id="ccO-Ne-aJ2">
<keyModifierFlags key="modifierFlags" command="YES"/>
<connections>
<action selector="chooseIntelligence:" destination="J28-e1-gcC" id="BEW-3n-izO"/>
</connections>
</command>
<command title="Unread Stories" propertyListString="1" input="2" id="Flw-XS-uGO">
<keyModifierFlags key="modifierFlags" command="YES"/>
<connections>
<action selector="chooseIntelligence:" destination="J28-e1-gcC" id="Sjf-mN-l3c"/>
</connections>
</command>
<command title="Focus Stories" propertyListString="2" input="3" id="Pml-hQ-IST">
<keyModifierFlags key="modifierFlags" command="YES"/>
<connections>
<action selector="chooseIntelligence:" destination="J28-e1-gcC" id="O2v-f5-adA"/>
</connections>
</command>
<command title="Saved Stories" propertyListString="3" input="4" id="YTZ-1i-9Qj">
<keyModifierFlags key="modifierFlags" command="YES"/>
<connections>
<action selector="chooseIntelligence:" destination="J28-e1-gcC" id="6Bm-Us-ckr"/>
</connections>
</command>
</children>
</menu>
<menu title="Inline Menu" id="rEb-O5-WxE">
<menuOptions key="menuOptions" displayInline="YES"/>
</menu>
<menu title="Story Preview" id="11o-Ny-Ay8">
<children>
<command title="Only Title" propertyListString="title" menuElementState="on" id="3VQ-tS-tca">
<connections>
<action selector="chooseTitle:" destination="J28-e1-gcC" id="dVZ-e9-eMx"/>
</connections>
</command>
<command title="Short" propertyListString="short" id="kKo-nO-rdD">
<connections>
<action selector="chooseTitle:" destination="J28-e1-gcC" id="L2L-i9-jJu"/>
</connections>
</command>
<command title="Medium" propertyListString="medium" id="8kl-JH-cAR">
<connections>
<action selector="chooseTitle:" destination="J28-e1-gcC" id="cHy-Qn-qYA"/>
</connections>
</command>
<command title="Long" propertyListString="long" id="6c5-Aa-VWc">
<connections>
<action selector="chooseTitle:" destination="J28-e1-gcC" id="laa-aj-lRm"/>
</connections>
</command>
</children>
</menu>
<menu title="Image Preview" id="JBe-EV-vd0">
<children>
<command title="None" propertyListString="none" menuElementState="on" id="xUQ-iP-Xav">
<keyModifierFlags key="modifierFlags" command="YES"/>
<connections>
<action selector="choosePreview:" destination="J28-e1-gcC" id="ydY-6y-Vu7"/>
</connections>
</command>
<command title="Small Left" propertyListString="small_left" id="pgV-T3-JgP">
<connections>
<action selector="choosePreview:" destination="J28-e1-gcC" id="6bd-eh-Jkf"/>
</connections>
</command>
<command title="Large Left" propertyListString="large_left" id="rJC-gP-ub8">
<connections>
<action selector="choosePreview:" destination="J28-e1-gcC" id="dXF-jY-GB9"/>
</connections>
</command>
<command title="Large Right" propertyListString="large_right" id="DHS-T6-h9f">
<connections>
<action selector="choosePreview:" destination="J28-e1-gcC" id="ZrD-28-3r7"/>
</connections>
</command>
<command title="Small Right" propertyListString="small_right" id="0ZJ-kc-nG7">
<connections>
<action selector="choosePreview:" destination="J28-e1-gcC" id="yQ3-7h-2eZ"/>
</connections>
</command>
</children>
</menu>
<menu title="Inline Menu" id="uRc-h2-2ur">
<menuOptions key="menuOptions" displayInline="YES"/>
</menu>
<menu title="Grid Columns" id="gPp-Vp-Aqb">
<children>
<command title="Automatic" propertyListString="auto" menuElementState="on" id="W9M-jz-7Wo">
<connections>
<action selector="chooseGridColumns:" destination="J28-e1-gcC" id="r7t-6H-NdZ"/>
</connections>
</command>
<command title="1" propertyListString="1" id="COJ-8T-jFG">
<connections>
<action selector="chooseGridColumns:" destination="J28-e1-gcC" id="sg6-ao-Ca6"/>
</connections>
</command>
<command title="2" propertyListString="2" id="Enn-ac-qRo">
<connections>
<action selector="chooseGridColumns:" destination="J28-e1-gcC" id="9uC-A2-7y3"/>
</connections>
</command>
<command title="3" propertyListString="3" id="LL9-1i-7gs">
<connections>
<action selector="chooseGridColumns:" destination="J28-e1-gcC" id="7Ki-1P-JNN"/>
</connections>
</command>
<command title="4" propertyListString="4" id="TAe-RC-Isj">
<connections>
<action selector="chooseGridColumns:" destination="J28-e1-gcC" id="8rE-3w-9G0"/>
</connections>
</command>
</children>
</menu>
<menu title="Grid Height" id="YgQ-8j-p4J">
<children>
<command title="Extra Short" propertyListString="xs" id="QkE-4U-qCZ">
<connections>
<action selector="chooseGridHeight:" destination="J28-e1-gcC" id="gM2-NN-oey"/>
</connections>
</command>
<command title="Short" propertyListString="short" id="QiP-55-H7Q">
<connections>
<action selector="chooseGridHeight:" destination="J28-e1-gcC" id="ayB-De-Drm"/>
</connections>
</command>
<command title="Medium" propertyListString="medium" id="Z1h-b3-VUc">
<connections>
<action selector="chooseGridHeight:" destination="J28-e1-gcC" id="2L5-oA-LRA"/>
</connections>
</command>
<command title="Tall" propertyListString="tall" id="nuu-UE-jx7">
<connections>
<action selector="chooseGridHeight:" destination="J28-e1-gcC" id="NWu-Lm-Mcy"/>
</connections>
</command>
<command title="Extra Tall" propertyListString="xl" id="7pm-OP-kNf">
<connections>
<action selector="chooseGridHeight:" destination="J28-e1-gcC" id="rZo-Hs-UjD"/>
</connections>
</command>
</children>
</menu>
<menu title="Inline Menu" id="gSR-Tp-3Cp">
<menuOptions key="menuOptions" displayInline="YES"/>
</menu>
<menu title="Text Size" id="si2-4b-JdU">
<children>
<command title="Extra Small" propertyListString="xs" input="5" id="1J1-HG-ovw">
<keyModifierFlags key="modifierFlags" option="YES" command="YES"/>
<connections>
<action selector="chooseFontSize:" destination="J28-e1-gcC" id="Fq4-fK-3Bs"/>
</connections>
</command>
<command title="Small" propertyListString="small" input="6" id="M7K-32-93Q">
<keyModifierFlags key="modifierFlags" option="YES" command="YES"/>
<connections>
<action selector="chooseFontSize:" destination="J28-e1-gcC" id="syn-70-bmd"/>
</connections>
</command>
<command title="Medium" propertyListString="medium" input="7" id="EOM-3c-Cjp">
<keyModifierFlags key="modifierFlags" option="YES" command="YES"/>
<connections>
<action selector="chooseFontSize:" destination="J28-e1-gcC" id="Qe4-E0-Qhz"/>
</connections>
</command>
<command title="Large" propertyListString="large" input="8" id="PWP-X9-KBw">
<keyModifierFlags key="modifierFlags" option="YES" command="YES"/>
<connections>
<action selector="chooseFontSize:" destination="J28-e1-gcC" id="Lnl-mp-hOV"/>
</connections>
</command>
<command title="Extra Large" propertyListString="xl" input="9" id="yfW-eZ-Rax">
<keyModifierFlags key="modifierFlags" option="YES" command="YES"/>
<connections>
<action selector="chooseFontSize:" destination="J28-e1-gcC" id="cl8-ay-saj"/>
</connections>
</command>
</children>
</menu>
<menu title="Spacing" id="MQ7-6f-UwT">
<children>
<command title="Compact" propertyListString="compact" id="FEf-Wk-caC">
<connections>
<action selector="chooseSpacing:" destination="J28-e1-gcC" id="qx2-aa-FAd"/>
</connections>
</command>
<command title="Comfortable" propertyListString="comfortable" id="A5r-hu-lBP">
<connections>
<action selector="chooseSpacing:" destination="J28-e1-gcC" id="Idm-Ge-25T"/>
</connections>
</command>
</children>
</menu>
<menu title="Theme" id="k0L-se-kpA">
<children>
<command title="Light" propertyListString="light" menuElementState="on" id="nvf-Kg-GEz">
<connections>
<action selector="chooseTheme:" destination="J28-e1-gcC" id="AGW-N1-hqV"/>
</connections>
</command>
<command title="Sepia" propertyListString="sepia" id="Nvo-zG-r0S">
<connections>
<action selector="chooseTheme:" destination="J28-e1-gcC" id="F6m-fp-d1B"/>
</connections>
</command>
<command title="Medium" propertyListString="medium" id="u5z-am-9MT">
<connections>
<action selector="chooseTheme:" destination="J28-e1-gcC" id="fOO-Pu-NR6"/>
</connections>
</command>
<command title="Dark" propertyListString="dark" id="roz-8B-xfb">
<connections>
<action selector="chooseTheme:" destination="J28-e1-gcC" id="Zpe-za-poM"/>
</connections>
</command>
</children>
</menu>
</children>
<systemMenuChildDeletions>
<menuDeletion anchor="com.apple.menu.toolbar"/>
</systemMenuChildDeletions>
</menu>
<menu title="Site" identifier="site" anchor="com.apple.menu.view" id="Kg8-V5-XsH">
<children>
<menu title="Manage" id="uU0-yq-BC8">
<children>
<command title="Rename Site…" id="ogl-RU-MAV">
<connections>
<action selector="openRenameSite:" destination="J28-e1-gcC" id="uUs-SK-j71"/>
</connections>
</command>
<command title="Mute Site…" id="USf-x7-etD">
<connections>
<action selector="muteSite:" destination="J28-e1-gcC" id="J7R-Wr-qZZ"/>
</connections>
</command>
<menu id="HKb-sW-DLl">
<menuOptions key="menuOptions" displayInline="YES"/>
<children>
<command title="Delete Site…" id="RuQ-ea-MNw">
<menuElementAttributes key="attributes" destructive="YES"/>
<connections>
<action selector="deleteSite:" destination="J28-e1-gcC" id="iu8-R6-1oB"/>
</connections>
</command>
</children>
</menu>
</children>
</menu>
<menu id="CCe-ei-YVh">
<menuOptions key="menuOptions" displayInline="YES"/>
</menu>
<command title="Train…" id="hIh-yf-xP8">
<connections>
<action selector="openTrainSite:" destination="J28-e1-gcC" id="5Ka-Ra-khJ"/>
</connections>
</command>
<command title="Notifications…" id="zeT-bO-F72">
<connections>
<action selector="openNotifications:" destination="J28-e1-gcC" id="4dl-l2-1n6"/>
</connections>
</command>
<command title="Statistics…" id="IRg-Uz-w76">
<connections>
<action selector="openStatistics:" destination="J28-e1-gcC" id="4ps-fi-6FI"/>
</connections>
</command>
<menu id="ffV-Jd-hmj">
<menuOptions key="menuOptions" displayInline="YES"/>
</menu>
<command title="Insta-Fetch Stories" input="r" id="skD-1M-foi">
<keyModifierFlags key="modifierFlags" option="YES" command="YES"/>
<connections>
<action selector="instaFetchFeed:" destination="J28-e1-gcC" id="soI-a3-gDT"/>
</connections>
</command>
<menu id="ze6-Sa-c31">
<menuOptions key="menuOptions" displayInline="YES"/>
<children>
<command title="Mark All as Read" input="a" id="BDX-7T-oPE">
<keyModifierFlags key="modifierFlags" option="YES" command="YES"/>
<connections>
<action selector="doMarkAllRead:" destination="J28-e1-gcC" id="2eI-UY-pbY"/>
</connections>
</command>
</children>
</menu>
<menu id="dAb-4p-3Qc">
<menuOptions key="menuOptions" displayInline="YES"/>
<children>
<command title="Next Site" input="j" id="Ux2-qi-iV3">
<keyModifierFlags key="modifierFlags" option="YES" command="YES"/>
<connections>
<action selector="nextSite:" destination="J28-e1-gcC" id="p14-gh-luF"/>
</connections>
</command>
<command title="Previous Site" input="k" id="cvB-e9-ie9">
<keyModifierFlags key="modifierFlags" option="YES" command="YES"/>
<connections>
<action selector="previousSite:" destination="J28-e1-gcC" id="9N0-gQ-7bR"/>
</connections>
</command>
<command title="Next Folder" input="J" id="dcy-Oz-PFw">
<keyModifierFlags key="modifierFlags" command="YES"/>
<connections>
<action selector="nextFolder:" destination="J28-e1-gcC" id="fbT-o2-ZNT"/>
</connections>
</command>
<command title="Previous Folder" input="K" id="CEJ-G9-fRi">
<keyModifierFlags key="modifierFlags" command="YES"/>
<connections>
<action selector="previousFolder:" destination="J28-e1-gcC" id="ynW-dP-UuT"/>
</connections>
</command>
</children>
</menu>
<menu id="IGD-cP-sh8">
<menuOptions key="menuOptions" displayInline="YES"/>
<children>
<command title="Open All Stories" input="E" id="sW2-Jm-pRP">
<keyModifierFlags key="modifierFlags" command="YES"/>
<connections>
<action selector="openAllStories:" destination="J28-e1-gcC" id="m4I-zj-Kta"/>
</connections>
</command>
</children>
</menu>
</children>
</menu>
<menu title="Story" identifier="story" anchor="com.apple.menu.view" id="KgH-xe-mTD">
<children>
<command title="Save This Story" input="s" id="mRM-X6-xCI">
<keyModifierFlags key="modifierFlags" command="YES"/>
<connections>
<action selector="toggleStorySaved:" destination="J28-e1-gcC" id="w9j-wM-yXJ"/>
</connections>
</command>
<command title="Mark as Read" input="m" id="MOV-1R-tmb">
<keyModifierFlags key="modifierFlags" option="YES" command="YES"/>
<connections>
<action selector="toggleStoryUnread:" destination="J28-e1-gcC" id="iNX-pg-hgT"/>
</connections>
</command>
<command title="Send To…" id="d3Y-Ik-PAB">
<connections>
<action selector="showSendTo:" destination="J28-e1-gcC" id="XJ8-Tz-tG6"/>
</connections>
</command>
<command title="Train This Story…" id="GJC-ae-c80">
<connections>
<action selector="showTrain:" destination="J28-e1-gcC" id="pr3-BS-f2g"/>
</connections>
</command>
<command title="Share This Story…" id="wmQ-3e-8h8">
<connections>
<action selector="showShare:" destination="J28-e1-gcC" id="UGy-Xo-sDK"/>
</connections>
</command>
<menu id="nKV-Y3-j73">
<menuOptions key="menuOptions" displayInline="YES"/>
<children>
<command title="Next Unread Story" input="n" id="ofa-dG-qfi">
<keyModifierFlags key="modifierFlags" command="YES"/>
<connections>
<action selector="nextUnreadStory:" destination="J28-e1-gcC" id="us7-1R-z9P"/>
</connections>
</command>
<command title="Next Story" input="j" id="FyB-cA-EZJ">
<keyModifierFlags key="modifierFlags" command="YES"/>
<connections>
<action selector="nextStory:" destination="J28-e1-gcC" id="yl9-dW-g5i"/>
</connections>
</command>
<command title="Previous Story" input="k" id="Tfg-Wu-4wp">
<keyModifierFlags key="modifierFlags" command="YES"/>
<connections>
<action selector="previousStory:" destination="J28-e1-gcC" id="Ymu-C3-BYE"/>
</connections>
</command>
</children>
</menu>
<menu id="XRI-fA-vEa">
<menuOptions key="menuOptions" displayInline="YES"/>
<children>
<command title="Text View" input="t" id="G4z-4B-Qeb">
<keyModifierFlags key="modifierFlags" option="YES" command="YES"/>
<connections>
<action selector="toggleTextStory:" destination="J28-e1-gcC" id="QJp-Jw-8hd"/>
</connections>
</command>
<command title="Open in Browser" input="o" id="Dvp-0s-e2A">
<keyModifierFlags key="modifierFlags" command="YES"/>
<connections>
<action selector="openInBrowser:" destination="J28-e1-gcC" id="ZIJ-IW-Ybq"/>
</connections>
</command>
</children>
</menu>
</children>
</menu>
<menu isSystemItem="YES" title="Help" identifier="com.apple.menu.help" id="2fJ-ya-vwD">
<children>
<command title="Support Forum" anchorAction="showHelp:" id="eDw-yh-3PE">
<connections>
<action selector="showSupportForum:" destination="J28-e1-gcC" id="sW1-ne-jja"/>
</connections>
</command>
</children>
</menu>
</children>
<systemMenuChildDeletions>
<menuDeletion anchor="com.apple.menu.format"/>
</systemMenuChildDeletions>
</menu>
<placeholder placeholderIdentifier="IBFirstResponder" id="J28-e1-gcC" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-1484" y="632"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="gvC-iq-nYU">
<objects>
@ -292,7 +895,7 @@
<objects>
<navigationController storyboardIdentifier="DetailNavigationController" useStoryboardIdentifierAsRestorationIdentifier="YES" id="HsP-cn-nIY" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="Noh-2M-34G">
<rect key="frame" x="0.0" y="24" width="818.5" height="50"/>
<rect key="frame" x="0.0" y="24" width="514" height="50"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
@ -405,7 +1008,7 @@
<objects>
<viewController id="afA-W0-XsQ" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="jBw-7b-YGl">
<rect key="frame" x="0.0" y="0.0" width="1194" height="580"/>
<rect key="frame" x="0.0" y="0.0" width="1210" height="580"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<viewLayoutGuide key="safeArea" id="xqp-qa-ugn"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
@ -420,7 +1023,7 @@
<objects>
<viewController id="1tQ-fy-A3c" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="jdW-Cp-Pkg">
<rect key="frame" x="0.0" y="0.0" width="1194" height="168"/>
<rect key="frame" x="0.0" y="0.0" width="1210" height="168"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<viewLayoutGuide key="safeArea" id="k4r-Od-mPr"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string></string>
<string>1C8F.1</string>
</array>
</dict>
</array>
</dict>
</plist>

View file

@ -0,0 +1,13 @@
{\rtf1\ansi\ansicpg1252\cocoartf2758
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;}
{\*\expandedcolortbl;;}
\margl1440\margr1440\vieww28800\viewh21020\viewkind0
\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\qc\partightenfactor0
{\field{\*\fldinst{HYPERLINK "https://www.newsblur.com/"}}{\fldrslt
\f0\fs22 \cf0 newsblur.com}}
\f0\fs22 \
\
{\field{\*\fldinst{HYPERLINK "https://newsblur.com/privacy"}}{\fldrslt Privacy Policy}} \'95 {\field{\*\fldinst{HYPERLINK "https://newsblur.com/tos"}}{\fldrslt Terms of Service}}\
\
Copyright NewsBlur, Inc.}

Some files were not shown because too many files have changed in this diff Show more