Permalink
Browse files

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
  • Loading branch information...
1 parent 986f49f commit e281c983d6d46b380fa333b7020bd18b13b38711 @brotherbard committed Aug 22, 2010
Showing with 768 additions and 27 deletions.
  1. +6 −0 GitX.xcodeproj/project.pbxproj
  2. +16 −0 GitXRelativeDateFormatter.h
  3. +93 −0 GitXRelativeDateFormatter.m
  4. +653 −27 PBGitHistoryView.xib
@@ -77,6 +77,7 @@
D8E105471157C18200FC28A4 /* PBQLTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = D8E105461157C18200FC28A4 /* PBQLTextView.m */; };
D8E3B2B810DC9FB2001096A3 /* ScriptingBridge.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D8E3B2B710DC9FB2001096A3 /* ScriptingBridge.framework */; };
D8E3B34D10DCA958001096A3 /* PBCreateTagSheet.m in Sources */ = {isa = PBXBuildFile; fileRef = D8E3B34C10DCA958001096A3 /* PBCreateTagSheet.m */; };
+ D8EB616A122F643E00FCCAF4 /* GitXRelativeDateFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = D8EB6169122F643E00FCCAF4 /* GitXRelativeDateFormatter.m */; };
D8FBCF19115FA20C0098676A /* PBGitSHA.m in Sources */ = {isa = PBXBuildFile; fileRef = D8FBCF18115FA20C0098676A /* PBGitSHA.m */; };
D8FDD9F711432A12005647F6 /* PBCloneRepositoryPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = D8FDD9F511432A12005647F6 /* PBCloneRepositoryPanel.xib */; };
D8FDDA6A114335E8005647F6 /* PBGitSVBranchItem.m in Sources */ = {isa = PBXBuildFile; fileRef = D8FDDA5D114335E8005647F6 /* PBGitSVBranchItem.m */; };
@@ -318,6 +319,8 @@
D8E3B34B10DCA958001096A3 /* PBCreateTagSheet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBCreateTagSheet.h; sourceTree = "<group>"; };
D8E3B34C10DCA958001096A3 /* PBCreateTagSheet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBCreateTagSheet.m; sourceTree = "<group>"; };
D8E3B38110DD4E2C001096A3 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/PBCreateTagSheet.xib; sourceTree = "<group>"; };
+ D8EB6168122F643E00FCCAF4 /* GitXRelativeDateFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GitXRelativeDateFormatter.h; sourceTree = "<group>"; };
+ D8EB6169122F643E00FCCAF4 /* GitXRelativeDateFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GitXRelativeDateFormatter.m; sourceTree = "<group>"; };
D8FBCF17115FA20C0098676A /* PBGitSHA.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PBGitSHA.h; sourceTree = "<group>"; };
D8FBCF18115FA20C0098676A /* PBGitSHA.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PBGitSHA.m; sourceTree = "<group>"; };
D8FDD9F611432A12005647F6 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/PBCloneRepositoryPanel.xib; sourceTree = "<group>"; };
@@ -801,6 +804,8 @@
F567B88C1057FA9F000DB976 /* NSOutlineViewExt.m */,
D8A4BB6D11337D5C00E92D51 /* PBGitGradientBarView.h */,
D8A4BB6E11337D5C00E92D51 /* PBGitGradientBarView.m */,
+ D8EB6168122F643E00FCCAF4 /* GitXRelativeDateFormatter.h */,
+ D8EB6169122F643E00FCCAF4 /* GitXRelativeDateFormatter.m */,
);
name = Aux;
sourceTree = "<group>";
@@ -1258,6 +1263,7 @@
D8E105471157C18200FC28A4 /* PBQLTextView.m in Sources */,
D8FBCF19115FA20C0098676A /* PBGitSHA.m in Sources */,
D8022FED11E124C8003C21F6 /* PBGitXMessageSheet.m in Sources */,
+ D8EB616A122F643E00FCCAF4 /* GitXRelativeDateFormatter.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -0,0 +1,16 @@
+//
+// GitXRelativeDateFormatter.h
+// GitX
+//
+// Created by Nathan Kinsinger on 9/1/10.
+// Copyright 2010 Nathan Kinsinger. All rights reserved.
+//
+
+#import <Cocoa/Cocoa.h>
+
+
+@interface GitXRelativeDateFormatter : NSFormatter {
+
+}
+
+@end
@@ -0,0 +1,93 @@
+//
+// GitXRelativeDateFormatter.m
+// GitX
+//
+// Created by Nathan Kinsinger on 9/1/10.
+// Copyright 2010 Nathan Kinsinger. All rights reserved.
+//
+
+#import "GitXRelativeDateFormatter.h"
+
+
+#define MINUTE 60
+#define HOUR (60 * MINUTE)
+
+#define WEEK 7
+
+
+@implementation GitXRelativeDateFormatter
+
+- (NSString *)stringForObjectValue:(id)date
+{
+ if (![date isKindOfClass:[NSDate class]])
+ return nil;
+
+ NSDate *now = [NSDate date];
+
+ NSInteger secondsAgo = lround([now timeIntervalSinceDate:date]);
+
+ if (secondsAgo < 0)
+ return @"In the future!";
+
+ if (secondsAgo < MINUTE)
+ return @"seconds ago";
+
+ if (secondsAgo < (2 * MINUTE))
+ return @"1 minute ago";
+
+ if (secondsAgo < HOUR)
+ return [NSString stringWithFormat:@"%d minutes ago", (secondsAgo / MINUTE)];
+
+ if (secondsAgo < (2 * HOUR))
+ return @"1 hour ago";
+
+ // figure out # of days ago based on calender days (so yesterday is the day before today not 24 hours ago)
+ NSDateFormatter *midnightFormmatter = [[NSDateFormatter alloc] init];
+ [midnightFormmatter setDateFormat:@"yyyy-MM-dd"];
+ NSDate *midnightOnTargetDate = [midnightFormmatter dateFromString:[midnightFormmatter stringFromDate:date]];
+ NSDate *midnightToday = [midnightFormmatter dateFromString:[midnightFormmatter stringFromDate:now]];
+
+ // use NSCalendar so it will handle things like leap years correctly
+ NSDateComponents *components = [[NSCalendar currentCalendar] components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit)
+ fromDate:midnightOnTargetDate
+ toDate:midnightToday
+ options:0];
+ NSInteger yearsAgo = [components year];
+ NSInteger monthsAgo = [components month];
+ NSInteger daysAgo = [components day];
+
+ if (yearsAgo == 0) {
+ if (monthsAgo == 0) {
+ // return "hours ago" if it's still today, but "Yesterday" only if more than 6 hours ago
+ // gives people a little time to get used to the idea that yesterday is over :)
+ if ((daysAgo == 0) || (secondsAgo < (6 * HOUR)))
+ return [NSString stringWithFormat:@"%d hours ago", (secondsAgo / HOUR)];
+ if (daysAgo == 1)
+ return @"Yesterday";
+
+ if (daysAgo >= (2 * WEEK))
+ return [NSString stringWithFormat:@"%d weeks ago", (daysAgo / WEEK)];
+
+ return [NSString stringWithFormat:@"%d days ago", daysAgo];
+ }
+
+ if (monthsAgo == 1)
+ return @"1 month ago";
+
+ return [NSString stringWithFormat:@"%d months ago", monthsAgo];
+ }
+
+ if (yearsAgo == 1) {
+ if (monthsAgo == 0)
+ return @"1 year ago";
+
+ if (monthsAgo == 1)
+ return @"1 year 1 month ago";
+
+ return [NSString stringWithFormat:@"1 year %d months ago", monthsAgo];
+ }
+
+ return [NSString stringWithFormat:@"%d years ago", yearsAgo];
+}
+
+@end
Oops, something went wrong.

0 comments on commit e281c98

Please sign in to comment.