This immense Dart program contains a nonexistent amount of errors and warnings. This code has no associated html or css.
Find this at dartpad.dartlang.org/?source=83783e68-9c14-4f0b-aad1-112d16d1fd0f.
Created with <3 with dartpad.dartlang.org.
This immense Dart program contains a nonexistent amount of errors and warnings. This code has no associated html or css.
Find this at dartpad.dartlang.org/?source=83783e68-9c14-4f0b-aad1-112d16d1fd0f.
Created with <3 with dartpad.dartlang.org.
// This algorithm works so that the items in the beginning of the list | |
// have an older date than the items at the end of the list. | |
// I.e., the items in the list are ordered the same way as they would | |
// be visually shown on the UI (older items at the top, recent at the bottom). | |
main() => new Page(); | |
class Item { | |
String user; | |
String message; | |
String type; | |
DateTime createdDate; | |
Item(this.user, this.message, this.createdDate, {this.type: 'regular'}); | |
String toString() => '${user}: ${message} (${createdDate.toString()}) – $type'; | |
} | |
enum TargetGroup { | |
Above, Below, Same, New | |
} | |
class Page { | |
List<ItemGroup> groups = []; | |
Page() { | |
// Initial items coming in! | |
var someItems = [ | |
new Item('kai', 'Kai 5', new DateTime(2015, 6, 1), type: 'notification'), | |
new Item('kai', 'Kai 6', new DateTime(2015, 6, 2)), | |
new Item('dave', 'Dave 4', new DateTime(2015, 6, 3)), | |
new Item('kai', 'Kai 7', new DateTime(2015, 6, 4)), | |
new Item('kai', 'Kai EARLIER', new DateTime(2015, 6, 10, 12, 10)), | |
new Item('dave', 'Dave 5', new DateTime(2015, 6, 5)), | |
new Item('dave', 'Dave 6', new DateTime(2015, 6, 6)), | |
new Item('kai', 'Kai LATER', new DateTime(2015, 6, 10, 12, 12)), | |
new Item('dave', 'Dave 7', new DateTime(2015, 6, 7)), | |
new Item('dave', 'Dave 8', new DateTime(2015, 6, 8)), | |
new Item('kai', 'Kai 8', new DateTime(2015, 6, 7)) | |
]; | |
processAll(someItems); | |
// Later we get more items! | |
var someMoreItems = [ | |
new Item('kai', 'Kai 1', new DateTime(2015, 5, 1)), | |
new Item('kai', 'Kai 2', new DateTime(2015, 5, 2)), | |
new Item('dave', 'Dave 1', new DateTime(2015, 5, 3)), | |
new Item('kai', 'Kai 3', new DateTime(2015, 5, 4)), | |
new Item('dave', 'Dave 2', new DateTime(2015, 5, 5)), | |
new Item('dave', 'Dave 3', new DateTime(2015, 5, 6)), | |
new Item('dave', 'Dave much later posted than the above', new DateTime(2015, 5, 8)), | |
new Item('kai', 'Kai 4', new DateTime(2015, 5, 9)) | |
]; | |
processAll(someMoreItems); | |
printItems(); | |
} | |
void processAll(List<Item> items) { | |
items.forEach(process); | |
} | |
void process(Item item) { | |
// Retrieve the group within this item belongs to, if any. | |
var group = groups.firstWhere((group) => group.isDateWithin(item.createdDate), orElse: () => null); | |
if (group != null) { | |
TargetGroup target = group.determineTargetGroup(item); | |
if (target == TargetGroup.Same) { | |
group.put(item); | |
} else { | |
var groupIndex = groups.indexOf(group); | |
var intersection = group.indexOf(item); | |
var topHalf = group.items.sublist(0, intersection); | |
var bottomHalf = group.items.sublist(intersection); | |
groups.remove(group); // Get rid of the old group, we need to split this thing! | |
if (target == TargetGroup.New) { | |
groups.insert(groupIndex, new ItemGroup.fromItems(bottomHalf)); | |
groups.insert(groupIndex, new ItemGroup(item)); | |
groups.insert(groupIndex, new ItemGroup.fromItems(topHalf)); | |
} else if (target == TargetGroup.Above) { | |
topHalf.add(item); | |
groups.insert(groupIndex, new ItemGroup.fromItems(bottomHalf)); | |
groups.insert(groupIndex, new ItemGroup.fromItems(topHalf)); | |
} else if (target == TargetGroup.Below) { | |
bottomHalf.insert(0, item); | |
groups.insert(groupIndex, new ItemGroup.fromItems(bottomHalf)); | |
groups.insert(groupIndex, new ItemGroup.fromItems(topHalf)); | |
} else { | |
throw 'Unknown target group!'; | |
} | |
} | |
} else { | |
// Okay, so the item is not within ANY group. | |
// This leaves a few options: | |
// 1) The item belongs to the top or bottom position of some group (i.e. not within). | |
// 2) The item needs its own new group. | |
// Fetch the first groups that are after/before this item. i.e. surrounding the item. | |
var groupBefore = groups.reversed.firstWhere((group) => item.createdDate.isAfter(group.getLatestDate()), orElse: () => null); | |
var groupAfter = groups.firstWhere((group) => item.createdDate.isBefore(group.getOldestDate()), orElse: () => null); | |
if (groupBefore == null && groupAfter == null) { | |
// No groups at all! | |
groups.add(new ItemGroup(item)); | |
} else if (groupBefore != null && groupAfter != null) { | |
if (groupBefore.determineTargetGroup(item) == TargetGroup.New && groupAfter.determineTargetGroup(item) == TargetGroup.New) { | |
// The item does not belong in either groups, | |
// so it has to go in between them in its own group. | |
var index = groups.indexOf(groupAfter); | |
groups.insert(index, new ItemGroup(item)); | |
} else if (groupBefore.determineTargetGroup(item) == TargetGroup.Same) { | |
// We do not belong to groupAfter, but we belong to groupBefore! | |
groupBefore.put(item); | |
} else { | |
// We belong to groupAfter. | |
groupAfter.put(item); | |
} | |
} else if (groupBefore == null) { | |
// There was no group before this item, but one after. | |
// Thus if we belong to groupAfter, let's go there, | |
// otherwise we need a new group at the top. | |
if (groupAfter.determineTargetGroup(item) == TargetGroup.Same) { | |
groupAfter.put(item); | |
} else { | |
groups.insert(0, new ItemGroup(item)); | |
} | |
} else { | |
// There was no group after this item, but one before. | |
// Same as earlier, let's put it there or in a new group at the bottom. | |
if (groupBefore.determineTargetGroup(item) == TargetGroup.Same) { | |
groupBefore.put(item); | |
} else { | |
groups.add(new ItemGroup(item)); | |
} | |
} | |
} | |
} | |
void printItems() { | |
groups.forEach((group) { | |
print('GROUP ${group.user}'); | |
group.items.forEach(print); | |
print('---'); | |
}); | |
} | |
} | |
class ItemGroup { | |
String user; | |
String type; | |
List<Item> items = []; | |
ItemGroup(Item item) { | |
user = item.user; | |
type = item.type; | |
items.add(item); | |
} | |
ItemGroup.fromItems(List<Item> list) { | |
user = list.first.user; | |
items.addAll(list); | |
} | |
DateTime getOldestDate() => items.first.createdDate; | |
DateTime getLatestDate() => items.last.createdDate; | |
/** | |
* Returns true when the given [DateTime] is in between | |
* the oldest and the latest item in this group. | |
*/ | |
bool isDateWithin(DateTime date) => date.isAfter(getOldestDate()) && date.isBefore(getLatestDate()); | |
/** | |
* Returns the index of where the given item should go to in this group. | |
*/ | |
int indexOf(Item item) { | |
for (var i = 0; i < items.length; i++) { | |
if (item.createdDate.isBefore(items[i].createdDate)) { | |
return i; | |
} | |
} | |
return items.length; | |
} | |
/** | |
* Puts the given item into this group at the right position. | |
*/ | |
void put(Item item) { | |
items.insert(indexOf(item), item); | |
} | |
/** | |
* Returns TargetGroup, which specifies where the item belongs to. | |
*/ | |
TargetGroup determineTargetGroup(Item item) { | |
var index = indexOf(item); | |
var lastIndex = items.length - 1; | |
bool farFromAboveItem = index > 0 && items[index - 1].createdDate.difference(item.createdDate).inSeconds.abs() > 86400; | |
bool farFromBelowItem = index < lastIndex && items[index + 1].createdDate.difference(item.createdDate).inSeconds.abs() > 86400; | |
if (item.user != user || item.type == 'notification' || this.isNotification) { | |
return TargetGroup.New; | |
} | |
if (!farFromAboveItem && !farFromBelowItem) { | |
return TargetGroup.Same; | |
} | |
if (!farFromAboveItem && farFromBelowItem) { | |
return TargetGroup.Above; | |
} | |
if (!farFromAboveItem && !farFromBelowItem) { | |
return TargetGroup.Below; | |
} | |
return TargetGroup.New; | |
} | |
bool get isNotification => items.first.type == 'notification'; | |
String get usernameForDisplay => items.first.user; | |
DateTime get lastCreatedDate => items.last.createdDate; | |
} |