Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "Date Published" sorting option and table column #1770

Merged
merged 14 commits into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions Vienna Tests/ArticleTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,20 +81,20 @@ class ArticleTests: XCTestCase {
XCTAssertEqual(self.article.link, link)
}

func testDate() {
func testLastUpdate() {
let date = Date()

self.article.date = date
self.article.lastUpdate = date

XCTAssertEqual(self.article.date, date)
XCTAssertEqual(self.article.lastUpdate, date)
}

func testDateCreated() {
func testPublicationDate() {
let date = Date()

self.article.createdDate = date
self.article.publicationDate = date

XCTAssertEqual(self.article.createdDate, date)
XCTAssertEqual(self.article.publicationDate, date)
}

func testBody() {
Expand Down Expand Up @@ -193,9 +193,9 @@ class ArticleTests: XCTestCase {

func testCompatibilityDate() {
let date = Date()
let dateKeyPath = "articleData." + MA_Field_Date
let dateKeyPath = "articleData." + MA_Field_LastUpdate

self.article.date = date
self.article.lastUpdate = date

XCTAssertEqual(self.article.value(forKeyPath: dateKeyPath) as? Date, date)
}
Expand Down
16 changes: 8 additions & 8 deletions Vienna Tests/VNAArticleTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -73,22 +73,22 @@ - (void)testLink
XCTAssertEqualObjects(self.article.link, Link);
}

- (void)testDate
- (void)testLastUpdate
{
NSDate *date = [NSDate date];

self.article.date = date;
self.article.lastUpdate = date;

XCTAssertEqualObjects(self.article.date, date);
XCTAssertEqualObjects(self.article.lastUpdate, date);
}

- (void)testDateCreated
- (void)testPublicationDate
{
NSDate *date = [NSDate date];

self.article.createdDate = date;
self.article.publicationDate = date;

XCTAssertEqualObjects(self.article.createdDate, date);
XCTAssertEqualObjects(self.article.publicationDate, date);
}

- (void)testBody
Expand Down Expand Up @@ -201,9 +201,9 @@ - (void)testMarkEnclosureDowloaded
- (void)testCompatibilityDate
{
NSDate *date = [NSDate date];
NSString *dateKeyPath = [@"articleData." stringByAppendingString:MA_Field_Date];
NSString *dateKeyPath = [@"articleData." stringByAppendingString:MA_Field_LastUpdate];

self.article.date = date;
self.article.lastUpdate = date;

XCTAssertEqualObjects([self.article valueForKeyPath:dateKeyPath], date);
}
Expand Down
8 changes: 4 additions & 4 deletions Vienna/Resources/Base.lproj/Predicates.strings
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"%[All,Any,None]@ of the following are true" = "Article matches %1$[all,any,none]@ of the following conditions";
"%[Author,Folder,Subject,Text]@ %[is,is not]@ %@" = "%1$[Author,Folder,Subject,Content]@ %2$[is,is not]@ %3$@";
"%[Author,Subject,Text]@ %[contains,does not contain]@ %@" = "%1$[Author,Subject,Content]@ %2$[contains,does not contain]@ %3$@";
"%[Date]@ %[is,is greater than or equal to,is less than,is less than or equal to]@ %[yesterday]@" = "%1$[Date]@ %2$[is,is after or is,is before,is before or is]@ %3$[yesterday]@";
"%[Date]@ %[is,is greater than,is greater than or equal to,is less than,is less than or equal to]@ %[last week]@" = "%1$[Date]@ %2$[is,is after,is after or is,is before,is before or is]@ %3$[last week]@";
"%[Date]@ %[is,is less than,is less than or equal to]@ %[today]@" = "%1$[Date]@ %2$[is,is before,is before or is]@ %3$[today]@";
"%[Date]@ %[less than ago,more than ago]@ %[days,hours,minutes,months,weeks,years]@ %@" = "%1$[Date]@ %2$[is in the last,is not in the last]@ %4$@ %3$[days,hours,minutes,months,weeks,years]@";
"%[Date,PublicationDate]@ %[is,is greater than or equal to,is less than,is less than or equal to]@ %[yesterday]@" = "%1$[Last update, Date Published]@ %2$[is,is after or is,is before,is before or is]@ %3$[yesterday]@";
"%[Date,PublicationDate]@ %[is,is greater than,is greater than or equal to,is less than,is less than or equal to]@ %[last week]@" = "%1$[Last update,Date Published]@ %2$[is,is after,is after or is,is before,is before or is]@ %3$[last week]@";
"%[Date,PublicationDate]@ %[is,is less than,is less than or equal to]@ %[today]@" = "%1$[Last update,Date Published]@ %2$[is,is before,is before or is]@ %3$[today]@";
"%[Date,PublicationDate]@ %[less than ago,more than ago]@ %[days,hours,minutes,months,weeks,years]@ %@" = "%1$[Last update,Date Published]@ %2$[is in the last,is not in the last]@ %4$@ %3$[days,hours,minutes,months,weeks,years]@";
"%[Deleted,Flagged,HasEnclosure,Read]@ is %[No,Yes]@" = "%1$[Is deleted,Is flagged,Has enclosure,Is read]@ %2$[No,Yes]@";
7 changes: 7 additions & 0 deletions Vienna/Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -6498,6 +6498,7 @@
},
"Date" : {
"comment" : "Data field name visible in menu/article list/smart folder definition",
"extractionState" : "stale",
"localizations" : {
"cs" : {
"stringUnit" : {
Expand Down Expand Up @@ -6621,6 +6622,9 @@
}
}
},
"Date Published" : {
"comment" : "Data field name visible in menu/article list/smart folder definition"
},
"Delete" : {
"comment" : "Title of a button on an alert\n Title of a menu item",
"localizations" : {
Expand Down Expand Up @@ -12852,6 +12856,9 @@
}
}
},
"Last Update" : {
"comment" : "Data field name visible in menu/article list/smart folder definition"
},
"Link" : {
"comment" : "Data field name visible in menu/article list",
"localizations" : {
Expand Down
38 changes: 22 additions & 16 deletions Vienna/Sources/Alerts/SmartFolder.m
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,9 @@ - (void)prepareTemplates

// subject / author / text contains / = / != <text>
NSArray<NSExpression *> *textLeftExpressions = @[
[NSExpression expressionForConstantValue:@"Text"],
[NSExpression expressionForConstantValue:@"Author"],
[NSExpression expressionForConstantValue:@"Subject"]
[NSExpression expressionForConstantValue:MA_Field_Text],
[NSExpression expressionForConstantValue:MA_Field_Author],
[NSExpression expressionForConstantValue:MA_Field_Subject]
];
NSPredicateEditorRowTemplate *textTemplate = [[NSPredicateEditorRowTemplate alloc]
initWithLeftExpressions:textLeftExpressions
Expand All @@ -172,8 +172,14 @@ - (void)prepareTemplates

[rowTemplates addObject:[VNASeparatorPredicateEditorRowTemplate new]];


NSArray *dateLeftExpressions = @[
[NSExpression expressionForConstantValue:MA_Field_LastUpdate],
[NSExpression expressionForConstantValue:MA_Field_PublicationDate]];

// date < / > days / weeks / months / years old
NSPredicateEditorRowTemplate *dateCompareTemplate = [[VNADateWithUnitPredicateEditorRowTemplate alloc] initWithLeftExpressions:@[[NSExpression expressionForConstantValue:MA_Field_Date]]];
NSPredicateEditorRowTemplate *dateCompareTemplate = [[VNADateWithUnitPredicateEditorRowTemplate alloc]
initWithLeftExpressions:dateLeftExpressions];
[rowTemplates addObject:dateCompareTemplate];

// date = / < / <= today / yesterday / lastWeek
Expand All @@ -186,7 +192,7 @@ - (void)prepareTemplates
@(NSLessThanOrEqualToPredicateOperatorType),
];
NSPredicateEditorRowTemplate *todayTemplate = [[NSPredicateEditorRowTemplate alloc]
initWithLeftExpressions:@[[NSExpression expressionForConstantValue:@"Date"]]
initWithLeftExpressions:dateLeftExpressions
rightExpressions:todayRightExpressions
modifier:NSDirectPredicateModifier
operators:todayOperators
Expand All @@ -204,11 +210,11 @@ - (void)prepareTemplates
@(NSGreaterThanOrEqualToPredicateOperatorType)
];
NSPredicateEditorRowTemplate *yesterdayTemplate = [[NSPredicateEditorRowTemplate alloc]
initWithLeftExpressions:@[[NSExpression expressionForConstantValue:@"Date"]]
rightExpressions:yesterdayRightExpressions
modifier:NSDirectPredicateModifier
operators:yesterdayOperators
options:0];
initWithLeftExpressions:dateLeftExpressions
rightExpressions:yesterdayRightExpressions
modifier:NSDirectPredicateModifier
operators:yesterdayOperators
options:0];
[rowTemplates addObject:yesterdayTemplate];

// date = / > / >= / < / <= last week
Expand All @@ -223,7 +229,7 @@ - (void)prepareTemplates
@(NSLessThanOrEqualToPredicateOperatorType)
];
NSPredicateEditorRowTemplate *dateTemplate = [[NSPredicateEditorRowTemplate alloc]
initWithLeftExpressions:@[[NSExpression expressionForConstantValue:@"Date"]]
initWithLeftExpressions:dateLeftExpressions
rightExpressions:weekRightExpressions
modifier:NSDirectPredicateModifier
operators:weekOperators
Expand All @@ -234,10 +240,10 @@ - (void)prepareTemplates

// read / flagged / deleted / has_enclosure = YES / NO
NSArray<NSExpression *> *booleanLeftExpressions = @[
[NSExpression expressionForConstantValue:@"Read"],
[NSExpression expressionForConstantValue:@"Flagged"],
[NSExpression expressionForConstantValue:@"Deleted"],
[NSExpression expressionForConstantValue:@"HasEnclosure"]
[NSExpression expressionForConstantValue:MA_Field_Read],
[NSExpression expressionForConstantValue:MA_Field_Flagged],
[NSExpression expressionForConstantValue:MA_Field_Deleted],
[NSExpression expressionForConstantValue:MA_Field_HasEnclosure]
];
NSArray<NSExpression *> *booleanRightExpressions = @[
[NSExpression expressionForConstantValue:@"Yes"],
Expand All @@ -256,7 +262,7 @@ - (void)prepareTemplates
// folder is / is not
NSArray<NSExpression *> *folders = [self fillFolderValueField:VNAFolderTypeRoot atIndent:0];
NSPredicateEditorRowTemplate *folderTemplate = [[NSPredicateEditorRowTemplate alloc]
initWithLeftExpressions:@[[NSExpression expressionForConstantValue:@"Folder"]]
initWithLeftExpressions:@[[NSExpression expressionForConstantValue:MA_Field_Folder]]
rightExpressions:folders
modifier:NSDirectPredicateModifier
operators:@[@(NSEqualToPredicateOperatorType), @(NSNotEqualToPredicateOperatorType)]
Expand Down
34 changes: 15 additions & 19 deletions Vienna/Sources/Application/AppController.m
Original file line number Diff line number Diff line change
Expand Up @@ -1143,15 +1143,15 @@ -(void)initSortMenu
for (Field * field in [db arrayOfFields]) {
// Filter out columns we don't sort on. Later we should have an attribute in the
// field object itself based on which columns we can sort on.
if (field.tag != ArticleFieldIDParent &&
field.tag != ArticleFieldIDGUID &&
field.tag != ArticleFieldIDDeleted &&
field.tag != ArticleFieldIDHeadlines &&
field.tag != ArticleFieldIDSummary &&
field.tag != ArticleFieldIDLink &&
field.tag != ArticleFieldIDText &&
field.tag != ArticleFieldIDEnclosureDownloaded &&
field.tag != ArticleFieldIDEnclosure)
if (field.tag != VNAArticleFieldTagParent &&
field.tag != VNAArticleFieldTagGUID &&
field.tag != VNAArticleFieldTagDeleted &&
field.tag != VNAArticleFieldTagHeadlines &&
field.tag != VNAArticleFieldTagSummary &&
field.tag != VNAArticleFieldTagLink &&
field.tag != VNAArticleFieldTagText &&
field.tag != VNAArticleFieldTagEnclosureDownloaded &&
field.tag != VNAArticleFieldTagEnclosure)
{
NSMenuItem * menuItem = [[NSMenuItem alloc] initWithTitle:field.displayName action:@selector(doSortColumn:) keyEquivalent:@""];
menuItem.representedObject = field;
Expand Down Expand Up @@ -1184,12 +1184,12 @@ -(void)initColumnsMenu
for (Field * field in [db arrayOfFields]) {
// Filter out columns we don't view in the article list. Later we should have an attribute in the
// field object based on which columns are visible in the tableview.
if (field.tag != ArticleFieldIDText &&
field.tag != ArticleFieldIDGUID &&
field.tag != ArticleFieldIDDeleted &&
field.tag != ArticleFieldIDParent &&
field.tag != ArticleFieldIDHeadlines &&
field.tag != ArticleFieldIDEnclosureDownloaded)
if (field.tag != VNAArticleFieldTagText &&
field.tag != VNAArticleFieldTagGUID &&
field.tag != VNAArticleFieldTagDeleted &&
field.tag != VNAArticleFieldTagParent &&
field.tag != VNAArticleFieldTagHeadlines &&
field.tag != VNAArticleFieldTagEnclosureDownloaded)
{
NSMenuItem * menuItem = [[NSMenuItem alloc] initWithTitle:field.displayName action:@selector(doViewColumn:) keyEquivalent:@""];
menuItem.representedObject = field;
Expand Down Expand Up @@ -1764,10 +1764,6 @@ -(void)handleFolderNameChange:(NSNotification *)nc
-(void)handleRefreshStatusChange:(NSNotification *)nc
{
if (self.connecting) {
// Save the date/time of this refresh so we do the right thing when
// we apply the filter.
[[Preferences standardPreferences] setObject:[NSDate date] forKey:MAPref_LastRefreshDate];

// Toggle the refresh button
NSToolbarItem *item = [self toolbarItemWithIdentifier:@"Refresh"];
item.action = @selector(cancelAllRefreshesToolbar:);
Expand Down
12 changes: 7 additions & 5 deletions Vienna/Sources/Criteria/Criteria+NSPredicate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ extension Criteria: PredicateConvertible {
fallback = true
criteriaOperator = .equalTo
}
case MA_Field_Date:
case MA_Field_LastUpdate, MA_Field_PublicationDate:
switch predicate.predicateOperatorType {
case .lessThan:
criteriaOperator = .before
Expand Down Expand Up @@ -231,14 +231,15 @@ extension Criteria: PredicateConvertible {
let value = self.value
let operatorType = self.operatorType

if field == MA_Field_Date, let unit = DateUnit.allCases.first(where: { value.hasSuffix($0.rawValue) }) {
if field == MA_Field_LastUpdate || field == MA_Field_PublicationDate,
let unit = DateUnit.allCases.first(where: { value.hasSuffix($0.rawValue) }) {
let countString = value
.replacingOccurrences(of: unit.rawValue, with: "")
.trimmingCharacters(in: CharacterSet.whitespaces)
guard let count = UInt(countString) else {
fatalError("malformed criteria value \(value)")
}
return DatePredicateWithUnit(field: MA_Field_Date, comparisonOperator: operatorType, count: count, unit: unit)
return DatePredicateWithUnit(field: field, comparisonOperator: operatorType, count: count, unit: unit)
} else {
return buildComparisonPredicate(field, value, operatorType)
}
Expand Down Expand Up @@ -291,9 +292,10 @@ extension Criteria: PredicateConvertible {
// TODO: constants for fixed criteria values also for Criteria+SQL,
// e.g. YES, NO, yesterday, today, last week, ...

if field == MA_Field_Date && operatorType == .after && value == "yesterday" {
if (field == MA_Field_LastUpdate || field == MA_Field_PublicationDate)
&& operatorType == .after && value == DateOffset.yesterday.rawValue {
// Use canonical "is today" instead of "is after yesterday"
comparisonPredicate = NSComparisonPredicate(leftExpression: left, rightExpression: NSExpression(forConstantValue: "today"), modifier: .direct, type: .equalTo)
comparisonPredicate = NSComparisonPredicate(leftExpression: left, rightExpression: NSExpression(forConstantValue: DateOffset.today), modifier: .direct, type: .equalTo)
} else if operatorType == .notEqualTo && (value == "No" || value == "Yes") {
// Use canonical "is yes / is no" representation instead of allowing
// ambiguous "is not yes - is no / is not no - is yes"
Expand Down
12 changes: 6 additions & 6 deletions Vienna/Sources/Criteria/Criteria+SQL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,11 @@ extension Criteria: SQLConversion {
let startOfToday = Calendar.current.startOfDay(for: Date())

let startDate: Date?
if value == "today" {
if value == DateOffset.today.rawValue {
startDate = startOfToday
} else if value == "yesterday" {
} else if value == DateOffset.yesterday.rawValue {
startDate = Calendar.current.date(byAdding: .day, value: -1, to: startOfToday)
} else if value == "last week" {
} else if value == DateOffset.lastWeek.rawValue {
startDate = Calendar.current.date(byAdding: .weekOfYear, value: -1, to: startOfToday)
} else {
// Check for the pattern for date with unit criteria
Expand Down Expand Up @@ -142,10 +142,10 @@ extension Criteria: SQLConversion {
guard operatorType == .equalTo || operatorType == .notEqualTo else {
fatalError("Operator type \(operatorType) not applicable to flag field \(sqlFieldName)")
}
let val: String
if value == "Yes" { val = "1" } else { val = "0" }
let sqlValue: String
if value == "Yes" { sqlValue = "1" } else { sqlValue = "0" }
let sqlOperator = standardSqlOperator()
return "\(sqlFieldName) \(sqlOperator) \(val)"
return "\(sqlFieldName) \(sqlOperator) \(sqlValue)"
}

func folderSqlString(sqlFieldName: String, database: Database) -> String {
Expand Down
30 changes: 14 additions & 16 deletions Vienna/Sources/Criteria/Criteria.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,40 +45,38 @@ enum CriteriaOperator: Int {

// Workaround as long as this enum needs to be exposed to Objective-C and cannot have a string as raw value
init?(rawValue: String) {
let criteriaOperator: CriteriaOperator
switch rawValue {
case "\(CriteriaOperator.equalTo)":
criteriaOperator = CriteriaOperator.equalTo
self = CriteriaOperator.equalTo
case "\(CriteriaOperator.notEqualTo)":
criteriaOperator = CriteriaOperator.notEqualTo
self = CriteriaOperator.notEqualTo
case "\(CriteriaOperator.lessThan)":
criteriaOperator = CriteriaOperator.lessThan
self = CriteriaOperator.lessThan
case "\(CriteriaOperator.greaterThan)":
criteriaOperator = CriteriaOperator.greaterThan
self = CriteriaOperator.greaterThan
case "\(CriteriaOperator.lessThanOrEqualTo)":
criteriaOperator = CriteriaOperator.lessThanOrEqualTo
self = CriteriaOperator.lessThanOrEqualTo
case "\(CriteriaOperator.greaterThanOrEqualTo)":
criteriaOperator = CriteriaOperator.greaterThanOrEqualTo
self = CriteriaOperator.greaterThanOrEqualTo
case "\(CriteriaOperator.contains)":
criteriaOperator = CriteriaOperator.contains
self = CriteriaOperator.contains
case "\(CriteriaOperator.containsNot)":
criteriaOperator = CriteriaOperator.containsNot
self = CriteriaOperator.containsNot
case "\(CriteriaOperator.before)":
criteriaOperator = CriteriaOperator.before
self = CriteriaOperator.before
case "\(CriteriaOperator.after)":
criteriaOperator = CriteriaOperator.after
self = CriteriaOperator.after
case "\(CriteriaOperator.onOrBefore)":
criteriaOperator = CriteriaOperator.onOrBefore
self = CriteriaOperator.onOrBefore
case "\(CriteriaOperator.onOrAfter)":
criteriaOperator = CriteriaOperator.onOrAfter
self = CriteriaOperator.onOrAfter
case "\(CriteriaOperator.under)":
criteriaOperator = CriteriaOperator.under
self = CriteriaOperator.under
case "\(CriteriaOperator.notUnder)":
criteriaOperator = CriteriaOperator.notUnder
self = CriteriaOperator.notUnder
default:
return nil
}
self.init(rawValue: criteriaOperator.rawValue)
}

var intValue: Int {
Expand Down
4 changes: 4 additions & 0 deletions Vienna/Sources/Criteria/DatePredicateWithUnit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@

import Foundation

enum DateOffset: String {
case today, yesterday, lastWeek = "last week"
}

enum DateUnit: String, CaseIterable {
case minutes
case hours
Expand Down
Loading