public void CorrectlyIdentifiesNonMatchingIds(IThreadSafeTimeEntry timeEntryA, IThreadSafeTimeEntry timeEntryB) { var groupA = new GroupId(timeEntryA); var groupB = new GroupId(timeEntryB); groupA.Equals(groupB).Should().BeFalse(); }
private CalendarItem(IThreadSafeTimeEntry timeEntry) : this( timeEntry.Id.ToString(), CalendarItemSource.TimeEntry, timeEntry.Start, timeEntry.Duration.HasValue ? TimeSpan.FromSeconds(timeEntry.Duration.Value) : null as TimeSpan?, timeEntry.Description, CalendarIconKind.None, timeEntry.Project?.Color ?? ColorHelper.NoProject, timeEntry.Id) { switch (timeEntry.SyncStatus) { case SyncStatus.SyncNeeded: IconKind = CalendarIconKind.Unsynced; break; case SyncStatus.SyncFailed: IconKind = CalendarIconKind.Unsyncable; break; default: break; } }
private void notifyTimeEntryDeleted(IThreadSafeTimeEntry timeEntry) { if (dataSource is TimeEntriesDataSource timeEntriesDataSource) { timeEntriesDataSource.OnTimeEntrySoftDeleted(timeEntry); } }
private static IEnumerable <LogItemViewModel> single(IThreadSafeTimeEntry timeEntry, DurationFormat durationFormat) { yield return(timeEntry.ToViewModel( new GroupId(timeEntry), LogItemVisualizationIntent.SingleItem, durationFormat)); }
public static LogItemViewModel ToViewModel( this IThreadSafeTimeEntry timeEntry, GroupId groupId, LogItemVisualizationIntent visualizationIntent, DurationFormat durationFormat, int indexInLog, int dayInLog, int daysInThePast) => new LogItemViewModel( groupId, new[] { timeEntry.Id }, visualizationIntent, timeEntry.Billable, timeEntry.Description, timeEntry.Duration.HasValue ? DurationAndFormatToString.Convert( TimeSpan.FromSeconds(timeEntry.Duration.Value), durationFormat) : string.Empty, timeEntry.Project?.DisplayName(), timeEntry.Project?.DisplayColor(), timeEntry.Project?.Client?.Name, timeEntry.Task?.Name, timeEntry.TagIds.Any(), timeEntry.SyncStatus == SyncStatus.SyncNeeded, timeEntry.SyncStatus != SyncStatus.SyncFailed, timeEntry.IsInaccessible, indexInLog, dayInLog, daysInThePast);
private static void updateRunningNotification(IThreadSafeTimeEntry timeEntryViewModel, Activity activity, NotificationManager notificationManager) { if (notificationManager == null) { return; } if (timeEntryViewModel != null) { var startTime = timeEntryViewModel.Start.ToUnixTimeMilliseconds(); var timeEntryDescription = string.IsNullOrEmpty(timeEntryViewModel.Description) ? Resources.NoDescription : timeEntryViewModel.Description; var projectDetails = extractProjectDetails(timeEntryViewModel); var notification = activity.CreateNotificationBuilderWithDefaultChannel(notificationManager) .SetShowWhen(true) .SetUsesChronometer(true) .SetAutoCancel(false) .SetOngoing(true) .SetContentTitle(timeEntryDescription) .SetContentText(projectDetails) .SetWhen(startTime) .SetContentIntent(getIntentFor(activity)) .SetSmallIcon(Resource.Drawable.ic_icon_running) .Build(); notificationManager.Notify(runningTimeEntryNotificationId, notification); } else { notificationManager.Cancel(runningTimeEntryNotificationId); } }
public TimeEntrySuggestion(IThreadSafeTimeEntry timeEntry) { Description = timeEntry.Description; WorkspaceId = timeEntry.WorkspaceId; WorkspaceName = timeEntry.Workspace?.Name ?? ""; if (timeEntry.Project == null) { return; } HasProject = true; ProjectId = timeEntry.Project.Id; ProjectName = timeEntry.Project.DisplayName(); ProjectColor = timeEntry.Project.DisplayColor(); if (timeEntry.Project.Client != null) { ClientName = timeEntry.Project.Client.Name; } if (timeEntry.Task != null) { TaskId = timeEntry.Task.Id; TaskName = timeEntry.Task.Name; } }
public void DonateStartTimeEntry(IThreadSafeTimeEntry timeEntry) { if (!UIDevice.CurrentDevice.CheckSystemVersion(12, 0)) { return; } var relevantShortcuts = new List <INRelevantShortcut>(); var startTimerIntent = new StartTimerIntent(); startTimerIntent.Workspace = new INObject(timeEntry.Workspace.Id.ToString(), timeEntry.Workspace.Name); if (!string.IsNullOrEmpty(timeEntry.Description)) { // If any of the tags or the project id were just created and haven't sync we ignore this action until the user repeats it if (timeEntry.ProjectId < 0 || timeEntry.TagIds.Any(tagId => tagId < 0)) { return; } if (timeEntry.ProjectId is long projectId) { var projectINObject = new INObject(timeEntry.ProjectId.ToString(), timeEntry.Project.Name); startTimerIntent.ProjectId = projectINObject; } startTimerIntent.EntryDescription = timeEntry.Description; var tags = timeEntry.TagIds.Select(tag => new INObject(tag.ToString(), tag.ToString())).ToArray(); startTimerIntent.Tags = tags; var billable = new INObject(timeEntry.Billable.ToString(), timeEntry.Billable.ToString()); startTimerIntent.Billable = billable; startTimerIntent.SuggestedInvocationPhrase = string.Format(Resources.SiriTrackEntrySuggestedInvocationPhrase, timeEntry.Description); // Relevant shortcut for the Siri Watch Face relevantShortcuts.Add(createRelevantShortcut(startTimerIntent)); } else { startTimerIntent.SuggestedInvocationPhrase = Resources.StartTimerInvocationPhrase; } var startTimerInteraction = new INInteraction(startTimerIntent, null); startTimerInteraction.DonateInteraction(trackError); // Descriptionless Relevant Shortcut. Always added even if the intent has one var descriptionlessIntent = new StartTimerIntent(); descriptionlessIntent.Workspace = new INObject(timeEntry.Workspace.Id.ToString(), timeEntry.Workspace.Name); var descriptionlessShortcut = createRelevantShortcut(descriptionlessIntent); relevantShortcuts.Add(descriptionlessShortcut); donateRelevantShortcuts(relevantShortcuts.ToArray()); }
public ISpannable CreateProjectClientTaskLabel(IThreadSafeTimeEntry te) { if (te == null) return new SpannableString(string.Empty); var hasProject = te.ProjectId != null; return Extensions.TimeEntryExtensions.ToProjectTaskClient(hasProject, te.Project?.Name, te.Project?.Color, te.Task?.Name, te.Project?.Client?.Name); }
private static LogItemViewModel groupItem(IThreadSafeTimeEntry timeEntry, DurationFormat durationFormat) => timeEntry.ToViewModel( new GroupId(timeEntry), LogItemVisualizationIntent.GroupItem, durationFormat, 0, 0, 0);
private void insertNewTimeEntryGroup(IThreadSafeTimeEntry timeEntry, DateTime indexDate) { var timeEntryToAdd = new TimeEntryViewModel(timeEntry, durationFormat); var newCollection = new TimeEntryViewModelCollection(indexDate, new[] { timeEntryToAdd }, durationFormat); var foundIndex = TimeEntries.IndexOf(TimeEntries.FirstOrDefault(x => x.Date < indexDate)); var indexToInsert = foundIndex == -1 ? TimeEntries.Count : foundIndex; TimeEntries.Insert(indexToInsert, newCollection); }
public GroupId(IThreadSafeTimeEntry sample) { date = sample.Start.LocalDateTime.Date; description = sample.Description; workspaceId = sample.WorkspaceId; projectId = sample.Project?.Id; taskId = sample.Task?.Id; isBillable = sample.Billable; tagIds = new HashSet <long>(sample.TagIds ?? Array.Empty <long>()); }
public static ITimeEntryPrototype AsTimeEntryPrototype(this IThreadSafeTimeEntry timeEntry) => new TimeEntryPrototype( timeEntry.WorkspaceId, timeEntry.Description, TimeSpan.FromSeconds(timeEntry.Duration ?? 0.0), timeEntry.Start, timeEntry.Project?.Id, timeEntry.Task?.Id, timeEntry.TagIds.ToArray(), timeEntry.Billable);
public NSAttributedString Convert(IThreadSafeTimeEntry timeEntry) { var projectColor = new Color(timeEntry.Project?.Color ?? string.Empty).ToNativeColor(); return(Convert( timeEntry.Project?.Name ?? "", timeEntry.Task?.Name ?? "", timeEntry.Project?.Client?.Name ?? "", projectColor, timeEntry.Project?.IsPlaceholder() ?? false, timeEntry.Task?.IsPlaceholder() ?? false)); }
private EditTimeEntryDto createDto(IThreadSafeTimeEntry timeEntry) { return(new EditTimeEntryDto { Id = timeEntry.Id, Description = timeEntry.Description + " Updated", StartTime = DateTimeOffset.UtcNow, ProjectId = 13, Billable = true, WorkspaceId = 71, TagIds = new long[] { 1, 10, 34, 42 } }); }
private static NSAttributedString getFormattedProjectInfo( IThreadSafeTimeEntry timeEntry, UIColor projectColor, UIColor clientColor) { var builder = new StringBuilder(); var hasClient = !string.IsNullOrEmpty(timeEntry.Project.Client?.Name); if (!string.IsNullOrEmpty(timeEntry.Project.Name)) { builder.Append($" {timeEntry.Project.Name}"); } if (!string.IsNullOrEmpty(timeEntry.Task?.Name)) { builder.Append($": {timeEntry.Task?.Name}"); } if (hasClient) { builder.Append($" {timeEntry.Project.Client?.Name}"); } var text = builder.ToString(); var result = new NSMutableAttributedString(text); var clientIndex = text.Length - (timeEntry.Project?.Client?.Name.Length ?? 0); if (projectColor != null) { var projectNameRange = new NSRange(0, clientIndex); var projectNameAttributes = new UIStringAttributes { ForegroundColor = projectColor }; result.AddAttributes(projectNameAttributes, projectNameRange); } if (!hasClient) { return(result); } var clientNameRange = new NSRange(clientIndex, timeEntry.Project.Client.Name.Length); var clientNameAttributes = new UIStringAttributes { ForegroundColor = clientColor }; result.AddAttributes(clientNameAttributes, clientNameRange); return(result); }
public static void Save(IThreadSafeTimeEntry timeEntry) { var sharedPreferences = Application.Context.GetSharedPreferences(WidgetInfoSharedPreferencesName, FileCreationMode.Private); var editor = sharedPreferences.Edit(); editor.PutBoolean(IsRunningKey, timeEntry?.IsRunning() ?? false); editor.PutLong(StartTimeKey, timeEntry?.Start.ToUnixTimeSeconds() ?? default); editor.PutString(DescriptionKey, timeEntry?.Description ?? ""); editor.PutBoolean(HasProjectKey, timeEntry?.ProjectId.HasValue ?? false); editor.PutString(ProjectNameKey, timeEntry?.Project?.Name ?? ""); editor.PutString(ProjectColorKey, timeEntry?.Project?.Color ?? ""); editor.PutBoolean(HasClientKey, timeEntry?.Project?.ClientId.HasValue ?? false); editor.PutString(ClientNameKey, timeEntry?.Project?.Client?.Name ?? ""); editor.Apply(); }
public MockTimeEntry(IThreadSafeTimeEntry entity) { Id = entity.Id; WorkspaceId = entity.WorkspaceId; ProjectId = entity.ProjectId; TaskId = entity.TaskId; Billable = entity.Billable; Start = entity.Start; Duration = entity.Duration; Description = entity.Description; TagIds = entity.TagIds; At = entity.At; ServerDeletedAt = entity.ServerDeletedAt; UserId = entity.UserId; }
protected override void OnRunningTimeEntryChanged(IThreadSafeTimeEntry timeEntry) { if (timeEntry == null) { SharedStorage.Instance.SetRunningTimeEntry(null); return; } SharedStorage.Instance.SetRunningTimeEntry( timeEntry, timeEntry.Project?.Name ?? "", timeEntry.Project?.Color ?? "", timeEntry.Task?.Name ?? "", timeEntry.Project?.Client?.Name ?? ""); }
private void safeInsertTimeEntry(IThreadSafeTimeEntry timeEntry) { var indexDate = timeEntry.Start.LocalDateTime.Date; var collectionIndex = TimeEntries.IndexOf(x => x.Date.LocalDateTime == indexDate); var groupExists = collectionIndex >= 0; if (groupExists) { insertTimeEntryInGroup(timeEntry, collectionIndex); } else { insertNewTimeEntryGroup(timeEntry, indexDate); } RaisePropertyChanged(nameof(IsEmpty)); }
public override async Task Initialize(long[] timeEntryIds) { await base.Initialize(timeEntryIds); if (timeEntryIds == null || timeEntryIds.Length == 0) { throw new ArgumentException("Edit view has no Time Entries to edit."); } TimeEntryIds = timeEntryIds; var timeEntries = await interactorFactory.GetMultipleTimeEntriesById(TimeEntryIds).Execute(); var timeEntry = timeEntries.First(); originalTimeEntry = timeEntry; projectId = timeEntry.Project?.Id; taskId = timeEntry.Task?.Id; workspaceIdSubject.OnNext(timeEntry.WorkspaceId); Description.Accept(timeEntry.Description); projectClientTaskSubject.OnNext(new ProjectClientTaskInfo( timeEntry.Project?.DisplayName(), timeEntry.Project?.DisplayColor(), timeEntry.Project?.Client?.Name, timeEntry.Task?.Name, timeEntry.Project?.IsPlaceholder() ?? false, timeEntry.Task?.IsPlaceholder() ?? false)); isBillableSubject.OnNext(timeEntry.Billable); startTimeSubject.OnNext(timeEntry.Start); durationSubject.OnNext(timeEntry.TimeSpanDuration()); GroupDuration = timeEntries.Sum(entry => entry.TimeSpanDuration()); tagsSubject.OnNext(timeEntry.Tags?.ToImmutableList() ?? ImmutableList <IThreadSafeTag> .Empty); isInaccessibleSubject.OnNext(timeEntry.IsInaccessible); setupSyncError(timeEntries); }
private void setRunningEntry(IThreadSafeTimeEntry timeEntry) { CurrentTimeEntryId = timeEntry?.Id; currentTimeEntryStart = timeEntry?.Start; CurrentTimeEntryDescription = timeEntry?.Description ?? ""; CurrentTimeEntryElapsedTime = timeService.CurrentDateTime - currentTimeEntryStart ?? TimeSpan.Zero; CurrentTimeEntryTask = timeEntry?.Task?.Name ?? ""; CurrentTimeEntryProject = timeEntry?.Project?.DisplayName() ?? ""; CurrentTimeEntryProjectColor = timeEntry?.Project?.DisplayColor() ?? ""; CurrentTimeEntryClient = timeEntry?.Project?.Client?.Name ?? ""; isStopButtonEnabled = timeEntry != null; StopTimeEntryCommand.RaiseCanExecuteChanged(); StartTimeEntryCommand.RaiseCanExecuteChanged(); EditTimeEntryCommand.RaiseCanExecuteChanged(); RaisePropertyChanged(nameof(IsTimeEntryRunning)); }
private static string extractProjectDetails(IThreadSafeTimeEntry timeEntryViewModel) { if (timeEntryViewModel.ProjectId == null) { return(Resources.NoProject); } var projectDetails = new StringBuilder(timeEntryViewModel.Project.Name); if (timeEntryViewModel.TaskId != null) { projectDetails.Append($": {timeEntryViewModel.Task.Name}"); } if (timeEntryViewModel.Project.ClientId != null) { projectDetails.Append($" {dotSeparator} {timeEntryViewModel.Project.Client.Name}"); } return(projectDetails.ToString()); }
private string createAccessibilityLabelForRunningEntryCard(IThreadSafeTimeEntry timeEntry) { if (timeEntry == null) { return(null); } var accessibilityLabel = Resources.CurrentlyRunningTimeEntry; var duration = IosDependencyContainer.Instance.TimeService.CurrentDateTime - timeEntry.Start; accessibilityLabel += $", {duration}"; if (!string.IsNullOrEmpty(timeEntry.Description)) { accessibilityLabel += $", {timeEntry.Description}"; } var projectName = timeEntry.Project?.Name ?? ""; if (!string.IsNullOrEmpty(projectName)) { accessibilityLabel += $", {Resources.Project}: {projectName}"; } var taskName = timeEntry.Task?.Name ?? ""; if (!string.IsNullOrEmpty(taskName)) { accessibilityLabel += $", {Resources.Task}: {taskName}"; } var clientName = timeEntry.Project?.Client?.Name ?? ""; if (!string.IsNullOrEmpty(clientName)) { accessibilityLabel += $", {Resources.Client}: {clientName}"; } return(accessibilityLabel); }
public TimeEntryViewModel(IThreadSafeTimeEntry timeEntry, DurationFormat durationFormat) { Ensure.Argument.IsNotNull(timeEntry, nameof(timeEntry)); if (timeEntry.IsRunning()) { throw new InvalidOperationException("It is not possible to show a running time entry in the log."); } DurationFormat = durationFormat; Id = timeEntry.Id; WorkspaceId = timeEntry.WorkspaceId; StartTime = timeEntry.Start; IsBillable = timeEntry.Billable; TagIds = timeEntry.TagIds.ToArray(); HasTags = TagIds.Count() > 0; Description = timeEntry.Description; HasProject = timeEntry.Project != null; Duration = TimeSpan.FromSeconds(timeEntry.Duration.Value); HasDescription = !string.IsNullOrEmpty(timeEntry.Description); CanSync = timeEntry.SyncStatus != SyncStatus.SyncFailed; NeedsSync = timeEntry.SyncStatus == SyncStatus.SyncNeeded; IsGhost = timeEntry.IsGhost; if (!HasProject) { return; } ProjectId = timeEntry.Project.Id; ProjectName = timeEntry.Project.DisplayName(); ProjectColor = timeEntry.Project.DisplayColor(); TaskId = timeEntry.TaskId; TaskName = timeEntry.Task?.Name ?? ""; ClientName = timeEntry.Project.Client?.Name ?? ""; }
public static NSAttributedString ToFormattedTimeEntryString( this IThreadSafeTimeEntry timeEntry, nfloat fontHeight, UIColor clientColor, bool shouldColorProject) { if (string.IsNullOrEmpty(timeEntry.Project?.Name)) return new NSAttributedString(""); var displayColor = timeEntry.Project.DisplayColor(); var color = string.IsNullOrEmpty(displayColor) ? null : new Color(displayColor).ToNativeColor(); var projectColor = shouldColorProject ? color : null; var result = getColoredDot(color, fontHeight); var projectInfo = getFormattedProjectInfo(timeEntry, projectColor, clientColor); result.Append(projectInfo); return result; }
public override async Task Initialize() { stopwatchFromCalendar = stopwatchProvider.Get(MeasuredOperation.EditTimeEntryFromCalendar); stopwatchProvider.Remove(MeasuredOperation.EditTimeEntryFromCalendar); stopwatchFromMainLog = stopwatchProvider.Get(MeasuredOperation.EditTimeEntryFromMainLog); stopwatchProvider.Remove(MeasuredOperation.EditTimeEntryFromMainLog); var timeEntries = await interactorFactory.GetMultipleTimeEntriesById(TimeEntryIds).Execute(); var timeEntry = timeEntries.First(); originalTimeEntry = timeEntry; projectId = timeEntry.Project?.Id; taskId = timeEntry.Task?.Id; workspaceIdSubject.OnNext(timeEntry.WorkspaceId); Description.Accept(timeEntry.Description); projectClientTaskSubject.OnNext(new ProjectClientTaskInfo( timeEntry.Project?.DisplayName(), timeEntry.Project?.DisplayColor(), timeEntry.Project?.Client?.Name, timeEntry.Task?.Name)); isBillableSubject.OnNext(timeEntry.Billable); startTimeSubject.OnNext(timeEntry.Start); durationSubject.OnNext(timeEntry.TimeSpanDuration()); GroupDuration = timeEntries.Sum(entry => entry.TimeSpanDuration()); tagsSubject.OnNext(timeEntry.Tags?.ToImmutableList() ?? ImmutableList <IThreadSafeTag> .Empty); isInaccessibleSubject.OnNext(timeEntry.IsInaccessible); setupSyncError(timeEntries); }
public ISpannable CreateProjectClientTaskLabel(IThreadSafeTimeEntry te) { if (te == null) { return(new SpannableString(string.Empty)); } var hasProject = te.ProjectId != null; var projectIsPlaceholder = te.Project?.IsPlaceholder() ?? false; var taskIsPlaceholder = te.Task?.IsPlaceholder() ?? false; return(Extensions.TimeEntryExtensions.ToProjectTaskClient( Context, hasProject, te.Project?.Name, te.Project?.Color, te.Task?.Name, te.Project?.Client?.Name, projectIsPlaceholder, taskIsPlaceholder, displayPlaceholders: true)); }
public async Task DeleteProjectsUnreferencedByTasksInInaccessibleWorkspace() { var accessibleWorkspace = new MockWorkspace(1000, isInaccessible: false); var inaccessibleWorkspace = new MockWorkspace(2000, isInaccessible: true); var project1 = new MockProject(101, accessibleWorkspace, syncStatus: SyncStatus.InSync); var project2 = new MockProject(102, accessibleWorkspace, syncStatus: SyncStatus.RefetchingNeeded); var project3 = new MockProject(201, inaccessibleWorkspace, syncStatus: SyncStatus.InSync); var project4 = new MockProject(202, inaccessibleWorkspace, syncStatus: SyncStatus.SyncNeeded); var project5 = new MockProject(203, inaccessibleWorkspace, syncStatus: SyncStatus.SyncNeeded); var project6 = new MockProject(204, inaccessibleWorkspace, syncStatus: SyncStatus.InSync); var project7 = new MockProject(205, inaccessibleWorkspace, syncStatus: SyncStatus.InSync); var task1 = new MockTask(1001, accessibleWorkspace, project1, SyncStatus.InSync); var task2 = new MockTask(1002, accessibleWorkspace, project2, SyncStatus.RefetchingNeeded); var task3 = new MockTask(1003, accessibleWorkspace, project2, SyncStatus.SyncNeeded); var task4 = new MockTask(2001, inaccessibleWorkspace, project3, SyncStatus.InSync); var task5 = new MockTask(2002, inaccessibleWorkspace, project4, SyncStatus.RefetchingNeeded); var task6 = new MockTask(2003, inaccessibleWorkspace, project3, SyncStatus.SyncNeeded); var task7 = new MockTask(2004, inaccessibleWorkspace, project4, SyncStatus.InSync); var task8 = new MockTask(2005, inaccessibleWorkspace, project4, SyncStatus.InSync); var projects = new[] { project1, project2, project3, project4, project5, project6, project7 }; var tasks = new[] { task1, task2, task3, task4, task5, task6, task7, task8 }; var timeEntries = new IThreadSafeTimeEntry[] { }; var unreferencedProjects = new[] { project6, project7 }; var neededProjects = projects.Where(project => !unreferencedProjects.Contains(project)); configureDataSource(projects, tasks, timeEntries); await state.Start().SingleAsync(); projectsDataSource.Received().DeleteAll(Arg.Is <IEnumerable <IThreadSafeProject> >(arg => arg.All(project => unreferencedProjects.Contains(project)) && arg.None(project => neededProjects.Contains(project)))); }
private bool isNotDeleted(IThreadSafeTimeEntry timeEntry) => !hiddenTimeEntries.Contains(timeEntry.Id);