Skip to content

Commit

Permalink
Merge pull request #3 from TimVinkemeier/bugfix/find-and-replace
Browse files Browse the repository at this point in the history
Bug fixes, added minimal start page
  • Loading branch information
TimVinkemeier committed Sep 10, 2020
2 parents 44040d8 + 80952c5 commit 715e8af
Show file tree
Hide file tree
Showing 11 changed files with 190 additions and 85 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using System.Threading.Tasks;

using MvvmCross.Commands;
using MvvmCross.Commands;
using MvvmCross.Logging;
using MvvmCross.Navigation;
using MvvmCross.ViewModels;

using System.Threading.Tasks;

using TimVinkemeier.AzureDevOpsToolkit.Core.ViewModels.SearchAndReplace;
using TimVinkemeier.AzureDevOpsToolkit.Core.ViewModels.Settings;

Expand All @@ -15,6 +15,11 @@ public class MainMenuViewModel : MvxNavigationViewModel
public MainMenuViewModel(IMvxLogProvider logProvider, IMvxNavigationService navigationService)
: base(logProvider, navigationService)
{
StartPageItem = new MainMenuItemViewModel(LogProvider, NavigationService)
{
DisplayName = "Start Page",
TargetViewModelType = typeof(StartPageViewModel)
};
NavigateCommand = new MvxCommand<MainMenuItemViewModel>(vm => NavigationService.Navigate(vm.TargetViewModelType));
}

Expand All @@ -24,6 +29,8 @@ public MainMenuViewModel(IMvxLogProvider logProvider, IMvxNavigationService navi

public MvxObservableCollection<MainMenuItemViewModel> SecondaryItems { get; } = new MvxObservableCollection<MainMenuItemViewModel>();

public MainMenuItemViewModel StartPageItem { get; }

public override Task Initialize()
{
Items.Add(new MainMenuItemViewModel(LogProvider, NavigationService)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;
using Microsoft.VisualStudio.Services.WebApi.Patch;
using Microsoft.VisualStudio.Services.WebApi.Patch.Json;

using MvvmCross.Commands;
using MvvmCross.Logging;
using MvvmCross.Navigation;

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;

using TimVinkemeier.AzureDevOpsToolkit.Core.Extensions;
using TimVinkemeier.AzureDevOpsToolkit.Core.Messages.SearchAndReplace;
using TimVinkemeier.AzureDevOpsToolkit.Core.Services;
Expand Down Expand Up @@ -155,11 +154,12 @@ private WorkItemSearchResultViewModel BuildSearchResultViewModelForWorkItem(stri
const int highlightRange = 25;
var fieldValue = wi.Fields[selectedField.AzureDevOpsFieldName]?.ToString() ?? string.Empty;
fieldValue = fieldValue.ToLowerInvariant();
var findHighlightIndex = fieldValue.IndexOf(searchText);

var parts = fieldValue.Split(new[] { searchText }, StringSplitOptions.RemoveEmptyEntries);

var prefix = (parts[0].Length > highlightRange ? "..." : string.Empty) + new string(parts[0].Reverse().Take(highlightRange).Reverse().ToArray());
var prefix = parts.Length == 0
? string.Empty
: (parts[0].Length > highlightRange ? "..." : string.Empty) + new string(parts[0].Reverse().Take(highlightRange).Reverse().ToArray());
var postfix = parts.Length > 1
? new string(parts[1].Take(highlightRange).ToArray()) + (parts[1].Length > highlightRange ? "..." : string.Empty)
: string.Empty;
Expand Down Expand Up @@ -244,31 +244,15 @@ private async Task ReplaceAsync()
var replaceText = ReplaceText;
var itemsToUpdate = SearchResults.Where(r => r.IsSelected).ToList();

JsonPatchDocument patchDocumentFactory(WorkItem wi)
{
var itemToUpdate = itemsToUpdate.Find(i => i.Id == wi.Id);
var oldValue = wi.Fields[itemToUpdate.FieldName]?.ToString() ?? string.Empty;
var newValue = Regex.Replace(oldValue, itemToUpdate.SearchValue, replaceText, RegexOptions.IgnoreCase);
return new JsonPatchDocument
{
new JsonPatchOperation
{
Operation = Operation.Replace,
Path = $"/fields/{itemToUpdate.FieldName}",
Value = newValue
}
};
}

CurrentReplacementCount = 0;
TotalReplacementCount = itemsToUpdate.Count;
var count = 0;
foreach (var workItemVm in itemsToUpdate)
{
try
{
var workItem = await _workItemService.GetWorkItemAsync(workItemVm.Id).ConfigureAwait(false);
var oldValue = workItem.Fields[workItemVm.FieldName]?.ToString() ?? string.Empty;
var newValue = Regex.Replace(oldValue, workItemVm.SearchValue, replaceText, RegexOptions.IgnoreCase);
var newValue = oldValue?.Replace(workItemVm.SearchValue, replaceText);
var patchDocument = new JsonPatchDocument
{
new JsonPatchOperation
Expand All @@ -286,8 +270,8 @@ JsonPatchDocument patchDocumentFactory(WorkItem wi)
workItemVm.ReplaceResult = $"Replacement failed ({ex.Message})";
}

CurrentReplacementCount = ++count;
workItemVm.IsSelected = false;
CurrentReplacementCount++;
}

CurrentReplacementCount = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<Identity
Name="6e54fe58-54ff-4b52-8593-92ad1afe8542"
Publisher="CN=Tim Vinkemeier"
Version="0.1.0.0" />
Version="0.1.2.0" />

<Properties>
<DisplayName>Toolkit for Azure DevOps</DisplayName>
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 41 additions & 0 deletions TimVinkemeier.AzureDevOpsToolkit/Extensions/HyperlinkExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System.Diagnostics;
using System.Windows;
using System.Windows.Documents;

namespace TimVinkemeier.AzureDevOpsToolkit.Extensions
{
public static class HyperlinkExtensions
{
public static readonly DependencyProperty IsExternalProperty =
DependencyProperty.RegisterAttached("IsExternal", typeof(bool), typeof(HyperlinkExtensions), new UIPropertyMetadata(false, OnIsExternalChanged));

public static bool GetIsExternal(DependencyObject obj)
{
return (bool)obj.GetValue(IsExternalProperty);
}

public static void SetIsExternal(DependencyObject obj, bool value)
{
obj.SetValue(IsExternalProperty, value);
}

private static void Hyperlink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
{
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri)
{
UseShellExecute = true
});
e.Handled = true;
}

private static void OnIsExternalChanged(object sender, DependencyPropertyChangedEventArgs args)
{
var hyperlink = sender as Hyperlink;

if ((bool)args.NewValue)
hyperlink.RequestNavigate += Hyperlink_RequestNavigate;
else
hyperlink.RequestNavigate -= Hyperlink_RequestNavigate;
}
}
}
75 changes: 48 additions & 27 deletions TimVinkemeier.AzureDevOpsToolkit/Services/SettingsService.cs
Original file line number Diff line number Diff line change
@@ -1,50 +1,71 @@
using System;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Services.Common;

using Newtonsoft.Json;

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;

using TimVinkemeier.AzureDevOpsToolkit.Core.Services;

namespace TimVinkemeier.AzureDevOpsToolkit.Services
{
public class SettingsService : ISettingsService
{
public Task<T> GetSettingAsync<T>(Setting setting)
private IDictionary<string, string> _allSettings;

public async Task<T> GetSettingAsync<T>(Setting setting)
{
var value = setting switch
if (_allSettings is null)
{
Setting.AzureDevOpsToken => Properties.Settings.Default.AzureDevOpsToken,
Setting.OrganisationBaseUrl => Properties.Settings.Default.OrganisationBaseUrl,
Setting.ProjectName => Properties.Settings.Default.ProjectName,
_ => throw new ArgumentException(null, nameof(setting))
};
await ReadSettingsAsync().ConfigureAwait(false);
}

return Task.FromResult(JsonConvert.DeserializeObject<T>(value));
var key = Enum.GetName(setting);
return _allSettings.TryGetValue(key, out var value)
? JsonConvert.DeserializeObject<T>(value)
: default;
}

public Task SetSettingAsync<T>(Setting setting, T value)
public async Task SetSettingAsync<T>(Setting setting, T value)
{
switch (setting)
{
case Setting.AzureDevOpsToken:
Properties.Settings.Default.AzureDevOpsToken = JsonConvert.SerializeObject(value);
break;
await ReadSettingsAsync().ConfigureAwait(false);

var key = Enum.GetName(setting);
var json = JsonConvert.SerializeObject(value);
_allSettings.AddOrUpdate(key, json, (_, _) => json);

await SaveSettingsAsync().ConfigureAwait(false);
await ReadSettingsAsync().ConfigureAwait(false);
}

private static FileInfo GetSettingsFileInfo()
=> new FileInfo(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "TimVinkemeier.AzureDevOps", "settings.json"));

case Setting.OrganisationBaseUrl:
Properties.Settings.Default.OrganisationBaseUrl = JsonConvert.SerializeObject(value);
break;
private async Task ReadSettingsAsync()
{
var file = GetSettingsFileInfo();

case Setting.ProjectName:
Properties.Settings.Default.ProjectName = JsonConvert.SerializeObject(value);
break;
if (!file.Exists)
{
_allSettings = new Dictionary<string, string>();
await SaveSettingsAsync().ConfigureAwait(false);
return;
}

default:
throw new ArgumentException(null, nameof(setting));
};
var fullJson = await File.ReadAllTextAsync(file.FullName).ConfigureAwait(false);
_allSettings = JsonConvert.DeserializeObject<IDictionary<string, string>>(fullJson);
}

private async Task SaveSettingsAsync()
{
var file = GetSettingsFileInfo();

Properties.Settings.Default.Save();
Directory.CreateDirectory(file.DirectoryName);

return Task.CompletedTask;
var fullJson = JsonConvert.SerializeObject(_allSettings);
await File.WriteAllTextAsync(file.FullName, fullJson).ConfigureAwait(false);
}
}
}
6 changes: 3 additions & 3 deletions TimVinkemeier.AzureDevOpsToolkit/Setup.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using System.Windows.Controls;

using MvvmCross;
using MvvmCross;
using MvvmCross.Platforms.Wpf.Core;
using MvvmCross.Platforms.Wpf.Presenters;

using System.Windows.Controls;

using TimVinkemeier.AzureDevOpsToolkit.Core.Services;
using TimVinkemeier.AzureDevOpsToolkit.Services;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
</PropertyGroup>

<ItemGroup>
<None Remove="Assets\FindAndReplaceScreenshot.png" />
<None Remove="Assets\Logo.png" />
</ItemGroup>

Expand All @@ -23,6 +24,7 @@
</ItemGroup>

<ItemGroup>
<Resource Include="Assets\FindAndReplaceScreenshot.png" />
<Resource Include="Assets\Logo.png" />
</ItemGroup>

Expand Down
51 changes: 33 additions & 18 deletions TimVinkemeier.AzureDevOpsToolkit/Views/MainMenuView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,27 @@
d:DesignWidth="800"
mc:Ignorable="d">
<mvx:MvxWpfView.Resources>
<Style x:Key="MainMenuItemButtonStyle" TargetType="Button">
<Style.Setters>
<Setter Property="FontSize" Value="16" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Padding" Value="12" />
</Style.Setters>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="DodgerBlue" />
</Trigger>
</Style.Triggers>
</Style>
<DataTemplate x:Key="MainMenuItemTemplate" DataType="{x:Type vms:MainMenuItemViewModel}">
<Button
Padding="12"
HorizontalContentAlignment="Left"
Background="Transparent"
BorderThickness="0"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type mvx:MvxWpfView}}, Path=DataContext.NavigateCommand}"
CommandParameter="{Binding}"
Content="{Binding DisplayName}"
FontSize="16"
IsEnabled="{Binding IsSelected, Converter={c:BooleanInversionConverter}}">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="DodgerBlue" />
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
IsEnabled="{Binding IsSelected, Converter={c:BooleanInversionConverter}}"
Style="{StaticResource MainMenuItemButtonStyle}" />
</DataTemplate>
</mvx:MvxWpfView.Resources>
<Grid Background="LightGray">
Expand All @@ -42,9 +42,24 @@
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal">
<Image Height="48" Source="/TimVinkemeier.AzureDevOpsToolkit;component/Assets/Logo.png" />
</StackPanel>
<Button
Grid.Row="0"
HorizontalContentAlignment="Stretch"
Command="{Binding NavigateCommand}"
CommandParameter="{Binding StartPageItem}"
IsEnabled="{Binding StartPageItem.IsSelected, Converter={c:BooleanInversionConverter}}"
Style="{StaticResource MainMenuItemButtonStyle}">
<StackPanel Orientation="Horizontal">
<Image Height="48" Source="/TimVinkemeier.AzureDevOpsToolkit;component/Assets/Logo.png" />
<TextBlock
MaxWidth="120"
Margin="6,0,0,0"
VerticalAlignment="Center"
FontSize="14"
Text="Toolkit&#10;for Azure DevOps"
TextWrapping="Wrap" />
</StackPanel>
</Button>
<ItemsControl
Grid.Row="1"
ItemTemplate="{StaticResource MainMenuItemTemplate}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.Header>
<CheckBox Content="" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:SearchAndReplaceView}}, Path=DataContext.AreAllResultsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<CheckBox Content="" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=DataContext.AreAllResultsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
Expand Down
Loading

0 comments on commit 715e8af

Please sign in to comment.