Skip to content

Commit e281c98

Browse files
committed
Add a relative date column to the commits list table view
Shows a relative time ("x minutes ago", "Yesterday", "x days ago", "x years ago"). - Sort the column on the commit's date and not on the string value - Use a custom formatter connected to the cell in the XIB - Show or hide using the contextual menu for the table header
1 parent 986f49f commit e281c98

File tree

4 files changed

+768
-27
lines changed

4 files changed

+768
-27
lines changed

GitX.xcodeproj/project.pbxproj

+6
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
D8E105471157C18200FC28A4 /* PBQLTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = D8E105461157C18200FC28A4 /* PBQLTextView.m */; };
7878
D8E3B2B810DC9FB2001096A3 /* ScriptingBridge.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8E3B2B710DC9FB2001096A3 /* ScriptingBridge.framework */; };
7979
D8E3B34D10DCA958001096A3 /* PBCreateTagSheet.m in Sources */ = {isa = PBXBuildFile; fileRef = D8E3B34C10DCA958001096A3 /* PBCreateTagSheet.m */; };
80+
D8EB616A122F643E00FCCAF4 /* GitXRelativeDateFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = D8EB6169122F643E00FCCAF4 /* GitXRelativeDateFormatter.m */; };
8081
D8FBCF19115FA20C0098676A /* PBGitSHA.m in Sources */ = {isa = PBXBuildFile; fileRef = D8FBCF18115FA20C0098676A /* PBGitSHA.m */; };
8182
D8FDD9F711432A12005647F6 /* PBCloneRepositoryPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = D8FDD9F511432A12005647F6 /* PBCloneRepositoryPanel.xib */; };
8283
D8FDDA6A114335E8005647F6 /* PBGitSVBranchItem.m in Sources */ = {isa = PBXBuildFile; fileRef = D8FDDA5D114335E8005647F6 /* PBGitSVBranchItem.m */; };
@@ -318,6 +319,8 @@
318319
D8E3B34B10DCA958001096A3 /* PBCreateTagSheet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBCreateTagSheet.h; sourceTree = "<group>"; };
319320
D8E3B34C10DCA958001096A3 /* PBCreateTagSheet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBCreateTagSheet.m; sourceTree = "<group>"; };
320321
D8E3B38110DD4E2C001096A3 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/PBCreateTagSheet.xib; sourceTree = "<group>"; };
322+
D8EB6168122F643E00FCCAF4 /* GitXRelativeDateFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GitXRelativeDateFormatter.h; sourceTree = "<group>"; };
323+
D8EB6169122F643E00FCCAF4 /* GitXRelativeDateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GitXRelativeDateFormatter.m; sourceTree = "<group>"; };
321324
D8FBCF17115FA20C0098676A /* PBGitSHA.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitSHA.h; sourceTree = "<group>"; };
322325
D8FBCF18115FA20C0098676A /* PBGitSHA.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitSHA.m; sourceTree = "<group>"; };
323326
D8FDD9F611432A12005647F6 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/PBCloneRepositoryPanel.xib; sourceTree = "<group>"; };
@@ -801,6 +804,8 @@
801804
F567B88C1057FA9F000DB976 /* NSOutlineViewExt.m */,
802805
D8A4BB6D11337D5C00E92D51 /* PBGitGradientBarView.h */,
803806
D8A4BB6E11337D5C00E92D51 /* PBGitGradientBarView.m */,
807+
D8EB6168122F643E00FCCAF4 /* GitXRelativeDateFormatter.h */,
808+
D8EB6169122F643E00FCCAF4 /* GitXRelativeDateFormatter.m */,
804809
);
805810
name = Aux;
806811
sourceTree = "<group>";
@@ -1258,6 +1263,7 @@
12581263
D8E105471157C18200FC28A4 /* PBQLTextView.m in Sources */,
12591264
D8FBCF19115FA20C0098676A /* PBGitSHA.m in Sources */,
12601265
D8022FED11E124C8003C21F6 /* PBGitXMessageSheet.m in Sources */,
1266+
D8EB616A122F643E00FCCAF4 /* GitXRelativeDateFormatter.m in Sources */,
12611267
);
12621268
runOnlyForDeploymentPostprocessing = 0;
12631269
};

GitXRelativeDateFormatter.h

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//
2+
// GitXRelativeDateFormatter.h
3+
// GitX
4+
//
5+
// Created by Nathan Kinsinger on 9/1/10.
6+
// Copyright 2010 Nathan Kinsinger. All rights reserved.
7+
//
8+
9+
#import <Cocoa/Cocoa.h>
10+
11+
12+
@interface GitXRelativeDateFormatter : NSFormatter {
13+
14+
}
15+
16+
@end

GitXRelativeDateFormatter.m

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//
2+
// GitXRelativeDateFormatter.m
3+
// GitX
4+
//
5+
// Created by Nathan Kinsinger on 9/1/10.
6+
// Copyright 2010 Nathan Kinsinger. All rights reserved.
7+
//
8+
9+
#import "GitXRelativeDateFormatter.h"
10+
11+
12+
#define MINUTE 60
13+
#define HOUR (60 * MINUTE)
14+
15+
#define WEEK 7
16+
17+
18+
@implementation GitXRelativeDateFormatter
19+
20+
- (NSString *)stringForObjectValue:(id)date
21+
{
22+
if (![date isKindOfClass:[NSDate class]])
23+
return nil;
24+
25+
NSDate *now = [NSDate date];
26+
27+
NSInteger secondsAgo = lround([now timeIntervalSinceDate:date]);
28+
29+
if (secondsAgo < 0)
30+
return @"In the future!";
31+
32+
if (secondsAgo < MINUTE)
33+
return @"seconds ago";
34+
35+
if (secondsAgo < (2 * MINUTE))
36+
return @"1 minute ago";
37+
38+
if (secondsAgo < HOUR)
39+
return [NSString stringWithFormat:@"%d minutes ago", (secondsAgo / MINUTE)];
40+
41+
if (secondsAgo < (2 * HOUR))
42+
return @"1 hour ago";
43+
44+
// figure out # of days ago based on calender days (so yesterday is the day before today not 24 hours ago)
45+
NSDateFormatter *midnightFormmatter = [[NSDateFormatter alloc] init];
46+
[midnightFormmatter setDateFormat:@"yyyy-MM-dd"];
47+
NSDate *midnightOnTargetDate = [midnightFormmatter dateFromString:[midnightFormmatter stringFromDate:date]];
48+
NSDate *midnightToday = [midnightFormmatter dateFromString:[midnightFormmatter stringFromDate:now]];
49+
50+
// use NSCalendar so it will handle things like leap years correctly
51+
NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit)
52+
fromDate:midnightOnTargetDate
53+
toDate:midnightToday
54+
options:0];
55+
NSInteger yearsAgo = [components year];
56+
NSInteger monthsAgo = [components month];
57+
NSInteger daysAgo = [components day];
58+
59+
if (yearsAgo == 0) {
60+
if (monthsAgo == 0) {
61+
// return "hours ago" if it's still today, but "Yesterday" only if more than 6 hours ago
62+
// gives people a little time to get used to the idea that yesterday is over :)
63+
if ((daysAgo == 0) || (secondsAgo < (6 * HOUR)))
64+
return [NSString stringWithFormat:@"%d hours ago", (secondsAgo / HOUR)];
65+
if (daysAgo == 1)
66+
return @"Yesterday";
67+
68+
if (daysAgo >= (2 * WEEK))
69+
return [NSString stringWithFormat:@"%d weeks ago", (daysAgo / WEEK)];
70+
71+
return [NSString stringWithFormat:@"%d days ago", daysAgo];
72+
}
73+
74+
if (monthsAgo == 1)
75+
return @"1 month ago";
76+
77+
return [NSString stringWithFormat:@"%d months ago", monthsAgo];
78+
}
79+
80+
if (yearsAgo == 1) {
81+
if (monthsAgo == 0)
82+
return @"1 year ago";
83+
84+
if (monthsAgo == 1)
85+
return @"1 year 1 month ago";
86+
87+
return [NSString stringWithFormat:@"1 year %d months ago", monthsAgo];
88+
}
89+
90+
return [NSString stringWithFormat:@"%d years ago", yearsAgo];
91+
}
92+
93+
@end

0 commit comments

Comments
 (0)