Skip to content
This repository has been archived by the owner on Feb 13, 2024. It is now read-only.

Commit

Permalink
Merge pull request #4730 from toggl-open-source/feature/timeline_ui/o…
Browse files Browse the repository at this point in the history
…verlap_resolving

Context menu: overlap resolving
  • Loading branch information
skel35 authored Dec 4, 2020
2 parents 05388af + 1af5c13 commit 64dbeb9
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ final class TimelineData {
let started = entry.timeEntry.started else { return }

// Set the end time as a start time of selected entry
DesktopLibraryBridge.shared().updateTimeEntryWithEnd(atTimestamp: started.timeIntervalSince1970 - 1,
DesktopLibraryBridge.shared().updateTimeEntryWithEnd(atTimestamp: started.timeIntervalSince1970,
guid: firstEntry.timeEntry.guid)
}

Expand All @@ -222,7 +222,7 @@ final class TimelineData {
let endAt = firstEntry.timeEntry.ended else { return }

// Set the start time as a stop time of First entry
DesktopLibraryBridge.shared().updateTimeEntryWithStart(atTimestamp: endAt.timeIntervalSince1970 + 1,
DesktopLibraryBridge.shared().updateTimeEntryWithStart(atTimestamp: endAt.timeIntervalSince1970,
guid: entry.timeEntry.guid,
keepEndTimeFixed: true)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ public GapTimeEntryBlock(Func<double, double, string> addNewTimeEntry)
public class TimeEntryBlock : TimelineBlockViewModel
{
[Reactive]
public bool ShowDescription { get; set; }
public bool IsOverlapping { get; set; }
public bool ShowDescription { [ObservableAsProperty] get; }
public string Color => _timeEntry.Color;
public string Description => _timeEntry.Description.IsNullOrEmpty() ? "No Description" : _timeEntry.Description;
public string ProjectName => _timeEntry.ProjectLabel;
Expand Down Expand Up @@ -88,6 +89,9 @@ public TimeEntryBlock(Toggl.TogglTimeEntryView te, int hourHeight, DateTime date
.Select(h => h >= TimelineConstants.MinResizableTimeEntryBlockHeight)
.Where(_ => !IsDragged)
.ToPropertyEx(this, x => x.IsResizable);
this.WhenAnyValue(x => x.IsOverlapping)
.Select(isOverlapping => !isOverlapping && Height >= TimelineConstants.MinShowTEDescriptionHeight)
.ToPropertyEx(this, x => x.ShowDescription);
}

public void ChangeStartTime()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace TogglDesktop.ViewModels
public class TimelineViewModel : ReactiveObject
{
private DateTime _lastDateLoaded;
private List<TimeEntryBlock> SortedTimeEntryBlocks { [ObservableAsProperty] get; }

public TimelineViewModel()
{
Expand Down Expand Up @@ -51,6 +52,11 @@ public TimelineViewModel()
CreateFromEnd = ReactiveCommand.Create(() => TimelineUtils.CreateAndEditTimeEntry(ActiveTimeEntryBlock.Ended, ActiveTimeEntryBlock.Ended + TimelineConstants.DefaultTimeEntryLengthInSeconds), isNotRunningObservable);
StartFromEnd = ReactiveCommand.Create(() => TimelineUtils.CreateAndEditRunningTimeEntryFrom(ActiveTimeEntryBlock.Ended), isNotRunningObservable);
Delete = ReactiveCommand.Create(() => ActiveTimeEntryBlock.DeleteTimeEntry(), activeBlockObservable.Select(next => next != null));
var isOverlapping = activeBlockObservable.Select(next => next != null && next.IsOverlapping);
ChangeFirstTimeEntryStopCommand =
ReactiveCommand.Create(() => ChangeFirstEntryStop(ActiveTimeEntryBlock, SortedTimeEntryBlocks), isOverlapping);
ChangeLastTimeEntryStartCommand =
ReactiveCommand.Create(() => ChangeLastEntryStart(ActiveTimeEntryBlock, SortedTimeEntryBlocks), isOverlapping);
var scaleModeObservable = this.WhenAnyValue(x => x.SelectedScaleMode);
scaleModeObservable.Subscribe(_ =>
HourHeightView = TimelineConstants.ScaleModes[SelectedScaleMode] * GetHoursInLine(SelectedScaleMode));
Expand All @@ -74,7 +80,14 @@ public TimelineViewModel()
tuple.TimeEntries.Where(b => b.Key != tuple.Running?.GUID)
.ToDictionary(pair => pair.Key, pair => pair.Value))
.ToPropertyEx(this, x => x.TimeEntryBlocks);
blocksObservable.Select(blocks => GenerateGapTimeEntryBlocks(blocks.Values.ToList()))
var sortedTimeEntriesObservable = blocksObservable.Select(blocks =>
{
var timeEntries = blocks.Values.ToList();
timeEntries.Sort((te1, te2) => te1.VerticalOffset.CompareTo(te2.VerticalOffset));
return timeEntries;
});
sortedTimeEntriesObservable.ToPropertyEx(this, x => x.SortedTimeEntryBlocks);
sortedTimeEntriesObservable.Select(GenerateGapTimeEntryBlocks)
.ToPropertyEx(this, x => x.GapTimeEntryBlocks);
blocksWithRunningObservable
.Select(tuple =>
Expand Down Expand Up @@ -218,7 +231,7 @@ private static Dictionary<string, TimeEntryBlock> ConvertTimeEntriesToBlocks(Lis
{
Height = height,
VerticalOffset = ConvertTimeIntervalToHeight(selectedDate, startTime, selectedScaleMode),
ShowDescription = true
IsOverlapping = false
};
if (entry.Started < ended)
{
Expand Down Expand Up @@ -262,11 +275,11 @@ private static Dictionary<string, TimeEntryBlock> ConvertTimeEntriesToBlocks(Lis
offsets.Add(curOffset);
curOffset += TimelineConstants.TimeEntryBlockWidth+TimelineConstants.GapBetweenOverlappingTEs;
}
if (usedNumOfOffsets > 0 || item.Block.Height < TimelineConstants.MinShowTEDescriptionHeight)
if (usedNumOfOffsets > 0)
{
item.Block.ShowDescription = false;
item.Block.IsOverlapping = true;
if (prevLayerBlock != null)
prevLayerBlock.ShowDescription = false;
prevLayerBlock.IsOverlapping = true;
}
item.Block.HorizontalOffset = offsets.Min();
offsets.Remove(offsets.Min());
Expand All @@ -293,7 +306,6 @@ public static double ConvertTimeIntervalToHeight(DateTime start, DateTime end, i
private static List<GapTimeEntryBlock> GenerateGapTimeEntryBlocks(List<TimeEntryBlock> timeEntries)
{
var gaps = new List<GapTimeEntryBlock>();
timeEntries.Sort((te1,te2) => te1.VerticalOffset.CompareTo(te2.VerticalOffset));
TimeEntryBlock lastTimeEntry = null;
foreach (var entry in timeEntries)
{
Expand Down Expand Up @@ -350,6 +362,30 @@ public static string AddNewTimeEntry(double offset, double height, int scaleMode
return timeEntryId;
}

public static void ChangeFirstEntryStop(TimeEntryBlock item, List<TimeEntryBlock> blocks)
{
var (first, last) = GetOverlappingPair(item, blocks);
if (last == null) return;
Toggl.SetTimeEntryEndTimeStamp(first.TimeEntryId, (long)last.Started);
}

public static void ChangeLastEntryStart(TimeEntryBlock item, List<TimeEntryBlock> blocks)
{
var (first, last) = GetOverlappingPair(item, blocks);
if (last == null) return;
Toggl.SetTimeEntryStartTimeStamp(last.TimeEntryId, (long)first.Ended);
}

private static (TimeEntryBlock First, TimeEntryBlock Last) GetOverlappingPair(TimeEntryBlock item, IEnumerable<TimeEntryBlock> blocks)
{
var overlapping = blocks.FirstOrDefault(b => b.TimeEntryId != item.TimeEntryId && b.IsOverlappingWith(item));
if (overlapping == null) return (item, null);

var first = item.Started < overlapping.Started ? item : overlapping;
var last = item.Started < overlapping.Started ? overlapping : item;
return (first, last);
}

[Reactive]
public int SelectedScaleMode { get; private set; } = 0;
[Reactive]
Expand Down Expand Up @@ -402,6 +438,8 @@ public static string AddNewTimeEntry(double offset, double height, int scaleMode
public ReactiveCommand<Unit, Unit> CreateFromEnd { get; }
public ReactiveCommand<Unit, Unit> StartFromEnd { get; }
public ReactiveCommand<Unit, Unit> Delete { get; }
public ReactiveCommand<Unit, Unit> ChangeFirstTimeEntryStopCommand { get; }
public ReactiveCommand<Unit, Unit> ChangeLastTimeEntryStartCommand { get; }

public class ActivityBlock
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
<MenuItem Header="Create entry from the end of this entry" Command="{Binding CreateFromEnd}"
Visibility="{Binding IsTodaySelected, Converter={StaticResource BooleanInvertToVisibilityConverter}}"/>
<MenuItem Header="Delete" Command="{Binding Delete}"/>
<Separator Style="{StaticResource {x:Static MenuItem.SeparatorStyleKey}}"/>
<MenuItem Header="Change first entry stop time" Command="{Binding ChangeFirstTimeEntryStopCommand}"/>
<MenuItem Header="Change last entry start time" Command="{Binding ChangeLastTimeEntryStartCommand}"/>
</ContextMenu>
</ResourceDictionary>
</UserControl.Resources>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using TogglDesktop.ViewModels;

namespace TogglDesktop
{
Expand Down Expand Up @@ -38,5 +39,8 @@ public static void CreateAndEditTimeEntry(ulong started, ulong ended)
public static DateTime StartTime(this Toggl.TimelineChunkView chunk) => Toggl.DateTimeFromUnix(chunk.Started);

public static DateTime EndTime(this Toggl.TimelineChunkView chunk) => Toggl.DateTimeFromUnix(chunk.Ended);

public static bool IsOverlappingWith(this TimeEntryBlock first, TimeEntryBlock second) =>
first.Started < second.Ended && second.Started < first.Ended;
}
}

0 comments on commit 64dbeb9

Please sign in to comment.