зеркало из https://github.com/github/VisualStudio.git
Merge pull request #48 from shana/shana/dialogs
Dialog infrastructure and stubs, plus loads of refactoring
This commit is contained in:
Коммит
6b1fdd83a7
12
GitHubVS.sln
12
GitHubVS.sln
|
@ -49,6 +49,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitHub.Exports", "src\GitHu
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitHub.Api", "src\GitHub.Api\GitHub.Api.csproj", "{B389ADAF-62CC-486E-85B4-2D8B078DF763}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitHub.Exports.Reactive", "src\GitHub.Exports.Reactive\GitHub.Exports.Reactive.csproj", "{E4ED0537-D1D9-44B6-9212-3096D7C3F7A1}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DesignTimeStyleHelper", "src\DesignTimeStyleHelper\DesignTimeStyleHelper.csproj", "{B1F5C227-456F-437D-BD5F-4C11B7A8D1A0}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -95,6 +99,14 @@ Global
|
|||
{B389ADAF-62CC-486E-85B4-2D8B078DF763}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B389ADAF-62CC-486E-85B4-2D8B078DF763}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B389ADAF-62CC-486E-85B4-2D8B078DF763}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E4ED0537-D1D9-44B6-9212-3096D7C3F7A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E4ED0537-D1D9-44B6-9212-3096D7C3F7A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E4ED0537-D1D9-44B6-9212-3096D7C3F7A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E4ED0537-D1D9-44B6-9212-3096D7C3F7A1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B1F5C227-456F-437D-BD5F-4C11B7A8D1A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B1F5C227-456F-437D-BD5F-4C11B7A8D1A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B1F5C227-456F-437D-BD5F-4C11B7A8D1A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B1F5C227-456F-437D-BD5F-4C11B7A8D1A0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
@ -1 +1 @@
|
|||
powershell.exe .\build\cibuild.ps1
|
||||
powershell.exe .\script\cibuild.ps1
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
|
||||
</startup>
|
||||
</configuration>
|
|
@ -0,0 +1,14 @@
|
|||
<Application x:Class="DesignTimeStyleHelper.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:DesignTimeStyleHelper"
|
||||
StartupUri="MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/GitHub.UI;component/SharedDictionary.xaml" />
|
||||
<ResourceDictionary Source="/GitHub.UI.Reactive;component/SharedDictionary.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
|
@ -0,0 +1,145 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.ComponentModel.Composition.Hosting;
|
||||
using System.ComponentModel.Composition.Primitives;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using GitHub.VisualStudio.TeamExplorerConnect;
|
||||
using Microsoft.VisualStudio.ComponentModelHost;
|
||||
using Microsoft.VisualStudio.Shell;
|
||||
using Moq;
|
||||
using GitHub.Services;
|
||||
using GitHub.VisualStudio;
|
||||
using GitHub.ViewModels;
|
||||
using GitHub.Exports;
|
||||
|
||||
namespace DesignTimeStyleHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
public static Holder ExportHolder { get; set; }
|
||||
public static CustomServiceProvider ServiceProvider { get { return (CustomServiceProvider) ExportHolder.ServiceProvider; } }
|
||||
|
||||
|
||||
public App()
|
||||
{
|
||||
var s = new CustomServiceProvider();
|
||||
ExportHolder = new Holder(s.DefaultCompositionService);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class Holder
|
||||
{
|
||||
[Import]
|
||||
public SVsServiceProvider ServiceProvider;
|
||||
|
||||
[Import]
|
||||
SComponentModel sc;
|
||||
|
||||
[Import]
|
||||
public IBrowser Browser;
|
||||
|
||||
[Import]
|
||||
public ExportFactoryProvider ExportFactoryProvider;
|
||||
|
||||
[Import]
|
||||
public IUIProvider UIProvider;
|
||||
|
||||
public Holder(ICompositionService cc)
|
||||
{
|
||||
cc.SatisfyImportsOnce(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Export(typeof(SVsServiceProvider))]
|
||||
[Export(typeof(SComponentModel))]
|
||||
[PartCreationPolicy(CreationPolicy.Shared)]
|
||||
public class CustomServiceProvider : SVsServiceProvider, IServiceProvider,
|
||||
SComponentModel, IComponentModel
|
||||
{
|
||||
readonly CompositionContainer container;
|
||||
public CompositionContainer Container { get { return container; } }
|
||||
AggregateCatalog catalog;
|
||||
|
||||
public CustomServiceProvider()
|
||||
{
|
||||
catalog = new AggregateCatalog(
|
||||
new AssemblyCatalog(typeof(CustomServiceProvider).Assembly),
|
||||
new AssemblyCatalog(typeof(GitHub.VisualStudio.Services).Assembly), // GitHub.VisualStudio
|
||||
new AssemblyCatalog(typeof(GitHub.Api.ApiClient).Assembly), // GitHub.App
|
||||
new AssemblyCatalog(typeof(GitHub.Api.SimpleApiClient).Assembly), // GitHub.Api
|
||||
new AssemblyCatalog(typeof(Rothko.Environment).Assembly), // Rothko
|
||||
new AssemblyCatalog(typeof(GitHub.Services.EnterpriseProbeTask).Assembly) // GitHub.Exports
|
||||
);
|
||||
container = new CompositionContainer(catalog, CompositionOptions.IsThreadSafe | CompositionOptions.DisableSilentRejection);
|
||||
|
||||
DefaultCatalog = catalog;
|
||||
DefaultExportProvider = container;
|
||||
DefaultCompositionService = DefaultCatalog.CreateCompositionService();
|
||||
|
||||
var batch = new CompositionBatch();
|
||||
batch.AddExportedValue<SVsServiceProvider>(this);
|
||||
batch.AddExportedValue<SComponentModel>(this);
|
||||
batch.AddExportedValue<ICompositionService>(DefaultCompositionService);
|
||||
container.Compose(batch);
|
||||
}
|
||||
|
||||
|
||||
public object GetService(Type serviceType)
|
||||
{
|
||||
string contract = AttributedModelServices.GetContractName(serviceType);
|
||||
var instance = container.GetExportedValues<object>(contract).FirstOrDefault();
|
||||
|
||||
if (instance != null)
|
||||
return instance;
|
||||
|
||||
instance = Create(serviceType);
|
||||
|
||||
if (instance != null)
|
||||
return instance;
|
||||
|
||||
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture,
|
||||
"Could not locate any instances of contract {0}.", contract));
|
||||
}
|
||||
|
||||
T Create<T>() where T : class
|
||||
{
|
||||
return new Mock<T>().Object;
|
||||
}
|
||||
|
||||
object Create(Type t)
|
||||
{
|
||||
var moq = typeof(Mock<>).MakeGenericType(t);
|
||||
var ctor = moq.GetConstructor(new Type[] { });
|
||||
var m = ctor.Invoke(new object[] { }) as Mock;
|
||||
return m.Object;
|
||||
}
|
||||
|
||||
|
||||
public ExportProvider DefaultExportProvider { get; set; }
|
||||
public ComposablePartCatalog DefaultCatalog { get; set; }
|
||||
public ICompositionService DefaultCompositionService { get; set; }
|
||||
|
||||
public ComposablePartCatalog GetCatalog(string catalogName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<T> GetExtensions<T>() where T : class
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public T GetService<T>() where T : class
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{B1F5C227-456F-437D-BD5F-4C11B7A8D1A0}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>DesignTimeStyleHelper</RootNamespace>
|
||||
<AssemblyName>DesignTimeStyleHelper</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.VisualStudio.ComponentModelHost, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
|
||||
<Reference Include="Microsoft.VisualStudio.Shell.Immutable.10.0, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
|
||||
<Reference Include="Moq">
|
||||
<HintPath>..\..\packages\Moq.4.2.1312.1319\lib\net40\Moq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.Composition" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Reactive.Core, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Reactive.Interfaces, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Reactive.Linq, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Reactive.PlatformServices, Version=2.2.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Xaml">
|
||||
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="WindowsBase" />
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="App.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</ApplicationDefinition>
|
||||
<Compile Include="WindowController.xaml.cs">
|
||||
<DependentUpon>WindowController.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Page Include="MainWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Compile Include="App.xaml.cs">
|
||||
<DependentUpon>App.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="MainWindow.xaml.cs">
|
||||
<DependentUpon>MainWindow.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Page Include="WindowController.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<None Include="packages.config" />
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
<AppDesigner Include="Properties\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\submodules\Rothko\src\Rothko.csproj">
|
||||
<Project>{4a84e568-ca86-4510-8cd0-90d3ef9b65f9}</Project>
|
||||
<Name>Rothko</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\GitHub.Api\GitHub.Api.csproj">
|
||||
<Project>{b389adaf-62cc-486e-85b4-2d8b078df763}</Project>
|
||||
<Name>GitHub.Api</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\GitHub.App\GitHub.App.csproj">
|
||||
<Project>{1a1da411-8d1f-4578-80a6-04576bea2dc5}</Project>
|
||||
<Name>GitHub.App</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\GitHub.Exports\GitHub.Exports.csproj">
|
||||
<Project>{9aea02db-02b5-409c-b0ca-115d05331a6b}</Project>
|
||||
<Name>GitHub.Exports</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\GitHub.Exports.Reactive\GitHub.Exports.Reactive.csproj">
|
||||
<Project>{e4ed0537-d1d9-44b6-9212-3096d7c3f7a1}</Project>
|
||||
<Name>GitHub.Exports.Reactive</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\GitHub.UI.Reactive\GitHub.UI.Reactive.csproj">
|
||||
<Project>{158b05e8-fdbc-4d71-b871-c96e28d5adf5}</Project>
|
||||
<Name>GitHub.UI.Reactive</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\GitHub.UI\GitHub.UI.csproj">
|
||||
<Project>{346384dd-2445-4a28-af22-b45f3957bd89}</Project>
|
||||
<Name>GitHub.UI</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\GitHub.VisualStudio\GitHub.VisualStudio.csproj">
|
||||
<Project>{11569514-5ae5-4b5b-92a2-f10b0967de5f}</Project>
|
||||
<Name>GitHub.VisualStudio</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,35 @@
|
|||
<Window x:Class="DesignTimeStyleHelper.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:DesignTimeStyleHelper"
|
||||
|
||||
mc:Ignorable="d"
|
||||
Title="MainWindow" Height="350" Width="525">
|
||||
|
||||
<Window.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/GitHub.UI;component/Assets/Styles.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/GitHub.UI;component/Assets/Controls.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/GitHub.UI;component/Assets/TextBlocks.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/GitHub.UI.Reactive;component/Assets/Controls.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
|
||||
<StackPanel>
|
||||
<Grid>
|
||||
<WrapPanel Orientation="Horizontal" Grid.Row="0" Margin="0,0,0,6">
|
||||
<TextBlock><Hyperlink x:Name="cloneLink" Click="cloneLink_Click">Clone</Hyperlink></TextBlock>
|
||||
<Label Margin="6 0 6 0"></Label>
|
||||
<TextBlock><Hyperlink x:Name="createLink" Click="createLink_Click">Create</Hyperlink></TextBlock>
|
||||
</WrapPanel>
|
||||
</Grid>
|
||||
|
||||
<StackPanel x:Name="container">
|
||||
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Window>
|
|
@ -0,0 +1,49 @@
|
|||
using System;
|
||||
using System.Windows;
|
||||
using GitHub.VisualStudio.TeamExplorerConnect;
|
||||
using GitHub.VisualStudio;
|
||||
using GitHub.Services;
|
||||
using GitHub.UI;
|
||||
using System.ComponentModel.Composition;
|
||||
|
||||
namespace DesignTimeStyleHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void cloneLink_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var ui = App.ServiceProvider.GetExportedValue<IUIProvider>();
|
||||
|
||||
var factory = ui.GetService<ExportFactoryProvider>();
|
||||
var d = factory.UIControllerFactory.CreateExport();
|
||||
var creation = d.Value.SelectFlow(UIControllerFlow.Clone);
|
||||
var x = new WindowController(creation);
|
||||
creation.Subscribe(_ => { }, _ => x.Close());
|
||||
x.Show();
|
||||
d.Value.Start();
|
||||
|
||||
}
|
||||
|
||||
private void createLink_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var ui = App.ServiceProvider.GetExportedValue<IUIProvider>();
|
||||
|
||||
var factory = ui.GetService<ExportFactoryProvider>();
|
||||
var d = factory.UIControllerFactory.CreateExport();
|
||||
var creation = d.Value.SelectFlow(UIControllerFlow.Create);
|
||||
var x = new WindowController(creation);
|
||||
creation.Subscribe(_ => { }, _ => x.Close());
|
||||
x.Show();
|
||||
d.Value.Start();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
using System.Reflection;
|
||||
using System.Resources;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("DesignTimeStyleHelper")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("DesignTimeStyleHelper")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2015")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
//In order to begin building localizable applications, set
|
||||
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
|
||||
//inside a <PropertyGroup>. For example, if you are using US english
|
||||
//in your source files, set the <UICulture> to en-US. Then uncomment
|
||||
//the NeutralResourceLanguage attribute below. Update the "en-US" in
|
||||
//the line below to match the UICulture setting in the project file.
|
||||
|
||||
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
|
||||
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
@ -0,0 +1,71 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.0
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace DesignTimeStyleHelper.Properties
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources
|
||||
{
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((resourceMan == null))
|
||||
{
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DesignTimeStyleHelper.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
return resourceCulture;
|
||||
}
|
||||
set
|
||||
{
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
|
@ -0,0 +1,30 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.0
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace DesignTimeStyleHelper.Properties
|
||||
{
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
|
||||
{
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default
|
||||
{
|
||||
get
|
||||
{
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
|
||||
<Profiles>
|
||||
<Profile Name="(Default)" />
|
||||
</Profiles>
|
||||
<Settings />
|
||||
</SettingsFile>
|
|
@ -0,0 +1,12 @@
|
|||
<Window x:Class="DesignTimeStyleHelper.WindowController"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:DesignTimeStyleHelper"
|
||||
mc:Ignorable="d"
|
||||
Title="WindowController" Height="300" Width="300">
|
||||
<Grid x:Name="Container">
|
||||
|
||||
</Grid>
|
||||
</Window>
|
|
@ -0,0 +1,35 @@
|
|||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace DesignTimeStyleHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for WindowController.xaml
|
||||
/// </summary>
|
||||
public partial class WindowController : Window
|
||||
{
|
||||
IDisposable disposable;
|
||||
|
||||
public WindowController(IObservable<UserControl> controls)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
disposable = controls.Subscribe(c => Load(c),
|
||||
Close
|
||||
);
|
||||
}
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
disposable.Dispose();
|
||||
base.OnClosed(e);
|
||||
}
|
||||
|
||||
public void Load(UserControl control)
|
||||
{
|
||||
Container.Children.Clear();
|
||||
Container.Children.Add(control);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Moq" version="4.2.1312.1319" targetFramework="net45" />
|
||||
<package id="Rx-Core" version="2.2.5" targetFramework="net45" />
|
||||
<package id="Rx-Interfaces" version="2.2.5" targetFramework="net45" />
|
||||
<package id="Rx-Linq" version="2.2.5" targetFramework="net45" />
|
||||
<package id="Rx-Main" version="2.2.5" targetFramework="net45" />
|
||||
<package id="Rx-PlatformServices" version="2.2.5" targetFramework="net45" />
|
||||
</packages>
|
|
@ -1,11 +1,10 @@
|
|||
using GitHub.Exports;
|
||||
using GitHub.Extensions;
|
||||
using GitHub.Services;
|
||||
using Octokit;
|
||||
using System;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using GitHub.Extensions;
|
||||
using GitHub.Primitives;
|
||||
using GitHub.Services;
|
||||
using Octokit;
|
||||
|
||||
namespace GitHub.Api
|
||||
{
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
using GitHub.Models;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using GitHub.Models;
|
||||
using GitHub.Primitives;
|
||||
using GitHub.Services;
|
||||
using Octokit;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GitHub.Api
|
||||
{
|
||||
|
|
|
@ -5,12 +5,12 @@ using System.Net;
|
|||
using System.Reactive.Linq;
|
||||
using GitHub.Authentication;
|
||||
using GitHub.Extensions;
|
||||
using GitHub.Primitives;
|
||||
using NLog;
|
||||
using Octokit;
|
||||
using Octokit.Reactive;
|
||||
using ReactiveUI;
|
||||
using Authorization = Octokit.Authorization;
|
||||
using LogManager = NLog.LogManager;
|
||||
|
||||
namespace GitHub.Api
|
||||
{
|
|
@ -2,7 +2,6 @@
|
|||
using System.ComponentModel.Composition;
|
||||
using System.Reactive.Linq;
|
||||
using GitHub.ViewModels;
|
||||
using Microsoft.VisualStudio.PlatformUI;
|
||||
using Octokit;
|
||||
using ReactiveUI;
|
||||
|
||||
|
@ -11,23 +10,25 @@ namespace GitHub.Authentication
|
|||
[Export(typeof(ITwoFactorChallengeHandler))]
|
||||
public class TwoFactorChallengeHandler : ITwoFactorChallengeHandler
|
||||
{
|
||||
readonly IServiceProvider serviceProvider;
|
||||
//readonly IServiceProvider serviceProvider;
|
||||
readonly Lazy<ITwoFactorViewModel> lazyTwoFactorDialog;
|
||||
|
||||
[ImportingConstructor]
|
||||
public TwoFactorChallengeHandler(IServiceProvider serviceProvider)
|
||||
public TwoFactorChallengeHandler(Lazy<ITwoFactorViewModel> twoFactorDialog)
|
||||
{
|
||||
this.serviceProvider = serviceProvider;
|
||||
//this.serviceProvider = serviceProvider;
|
||||
this.lazyTwoFactorDialog = twoFactorDialog;
|
||||
}
|
||||
|
||||
public IObservable<TwoFactorChallengeResult> HandleTwoFactorException(TwoFactorRequiredException exception)
|
||||
{
|
||||
var twoFactorDialog = (TwoFactorDialogViewModel)serviceProvider.GetService(typeof(TwoFactorDialogViewModel));
|
||||
var twoFactorView = (IViewFor<TwoFactorDialogViewModel>)serviceProvider.GetService(typeof(IViewFor<TwoFactorDialogViewModel>));
|
||||
var twoFactorDialog = lazyTwoFactorDialog.Value as TwoFactorDialogViewModel;
|
||||
//var twoFactorView = (IViewFor<TwoFactorDialogViewModel>)serviceProvider.GetService(typeof(IViewFor<TwoFactorDialogViewModel>));
|
||||
|
||||
return Observable.Start(() =>
|
||||
{
|
||||
twoFactorView.ViewModel = twoFactorDialog;
|
||||
((DialogWindow)twoFactorView).Show();
|
||||
//twoFactorView.ViewModel = twoFactorDialog;
|
||||
//((DialogWindow)twoFactorView).Show();
|
||||
|
||||
var userError = new TwoFactorRequiredUserError(exception);
|
||||
return twoFactorDialog.Show(userError)
|
||||
|
@ -37,8 +38,9 @@ namespace GitHub.Authentication
|
|||
: Observable.Throw<TwoFactorChallengeResult>(exception));
|
||||
}, RxApp.MainThreadScheduler)
|
||||
.SelectMany(x => x)
|
||||
.Finally(() =>
|
||||
((DialogWindow)twoFactorView).Hide());
|
||||
//.Finally(() =>
|
||||
// ((DialogWindow)twoFactorView).Hide());
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,9 +6,10 @@ using System.Reactive.Linq;
|
|||
using Akavache;
|
||||
using GitHub.Extensions;
|
||||
using GitHub.Extensions.Reactive;
|
||||
using GitHub.Models;
|
||||
using Octokit;
|
||||
|
||||
namespace GitHub
|
||||
namespace GitHub.Caches
|
||||
{
|
||||
public class HostCache : IHostCache
|
||||
{
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
using System;
|
||||
using System.Reactive;
|
||||
using Akavache;
|
||||
using GitHub.Primitives;
|
||||
|
||||
namespace GitHub
|
||||
namespace GitHub.Caches
|
||||
{
|
||||
public interface ILoginCache : IDisposable
|
||||
{
|
||||
|
|
|
@ -3,7 +3,7 @@ using System.Linq.Expressions;
|
|||
using System.Reactive;
|
||||
using Akavache;
|
||||
|
||||
namespace GitHub
|
||||
namespace GitHub.Caches
|
||||
{
|
||||
/// <summary>
|
||||
/// A cache for data that's not host specific
|
||||
|
|
|
@ -5,9 +5,10 @@ using System.Reactive;
|
|||
using System.Reactive.Linq;
|
||||
using Akavache;
|
||||
using GitHub.Extensions;
|
||||
using GitHub.Primitives;
|
||||
using NLog;
|
||||
|
||||
namespace GitHub
|
||||
namespace GitHub.Caches
|
||||
{
|
||||
[Export(typeof(ILoginCache))]
|
||||
public sealed class LoginCache : ILoginCache
|
||||
|
|
|
@ -8,7 +8,7 @@ using System.Reflection;
|
|||
using Akavache;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace GitHub
|
||||
namespace GitHub.Caches
|
||||
{
|
||||
/// <summary>
|
||||
/// A cache for data that's not host specific
|
||||
|
@ -108,9 +108,9 @@ namespace GitHub
|
|||
TSource source)
|
||||
{
|
||||
var member = expression.Body as MemberExpression;
|
||||
Debug.Assert(member != null, "Expression should be a property and not method or some other shit.");
|
||||
Debug.Assert(member != null, "Expression should be a property and not method or some other thing.");
|
||||
var property = member.Member as PropertyInfo;
|
||||
Debug.Assert(property != null, "Expression should be a property and not field or some other shit.");
|
||||
Debug.Assert(property != null, "Expression should be a property and not field or some other thing.");
|
||||
var propertySetterAction = new Action<TProperty>(value => property.SetValue(source, value));
|
||||
return Tuple.Create(property.Name, propertySetterAction);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,181 @@
|
|||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Diagnostics;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using GitHub.Authentication;
|
||||
using GitHub.Exports;
|
||||
using GitHub.Models;
|
||||
using GitHub.Services;
|
||||
using GitHub.UI;
|
||||
using GitHub.ViewModels;
|
||||
using ReactiveUI;
|
||||
using Stateless;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace GitHub.Controllers
|
||||
{
|
||||
[Export(typeof(IUIController))]
|
||||
public class UIController : IUIController, IDisposable
|
||||
{
|
||||
enum Trigger { Auth = 1, Create = 2, Clone = 3, Next, Previous }
|
||||
|
||||
readonly ExportFactoryProvider factory;
|
||||
readonly IUIProvider uiProvider;
|
||||
|
||||
CompositeDisposable disposables = new CompositeDisposable();
|
||||
Subject<UserControl> transition;
|
||||
UIControllerFlow currentFlow;
|
||||
StateMachine<UIViewType, Trigger> machine;
|
||||
|
||||
[ImportingConstructor]
|
||||
public UIController(IUIProvider uiProvider, IRepositoryHosts hosts,
|
||||
ExportFactoryProvider factory)
|
||||
{
|
||||
this.factory = factory;
|
||||
this.uiProvider = uiProvider;
|
||||
|
||||
machine = new StateMachine<UIViewType, Trigger>(UIViewType.None);
|
||||
|
||||
machine.Configure(UIViewType.None)
|
||||
.Permit(Trigger.Auth, UIViewType.Login)
|
||||
.PermitIf(Trigger.Create, UIViewType.Create, () => hosts.IsLoggedInToAnyHost)
|
||||
.PermitIf(Trigger.Create, UIViewType.Login, () => !hosts.IsLoggedInToAnyHost)
|
||||
.PermitIf(Trigger.Clone, UIViewType.Clone, () => hosts.IsLoggedInToAnyHost)
|
||||
.PermitIf(Trigger.Clone, UIViewType.Login, () => !hosts.IsLoggedInToAnyHost);
|
||||
|
||||
machine.Configure(UIViewType.Login)
|
||||
.OnEntry(() =>
|
||||
{
|
||||
var dvm = factory.GetViewModel(UIViewType.Login);
|
||||
disposables.Add(dvm);
|
||||
var viewModel = dvm.Value as ILoginViewModel;
|
||||
|
||||
viewModel.AuthenticationResults.Subscribe(result =>
|
||||
{
|
||||
if (result == AuthenticationResult.Success)
|
||||
Fire(Trigger.Next);
|
||||
});
|
||||
|
||||
var dv = factory.GetView(UIViewType.Login);
|
||||
disposables.Add(dv);
|
||||
var view = dv.Value;
|
||||
view.ViewModel = viewModel;
|
||||
|
||||
var twofa = uiProvider.GetService<ITwoFactorViewModel>();
|
||||
twofa.WhenAny(x => x.IsShowing, x => x.Value)
|
||||
.Where(x => x)
|
||||
.Subscribe(_ =>
|
||||
{
|
||||
Fire(Trigger.Next);
|
||||
});
|
||||
LoadView(view);
|
||||
})
|
||||
.Permit(Trigger.Next, UIViewType.TwoFactor);
|
||||
|
||||
machine.Configure(UIViewType.TwoFactor)
|
||||
.SubstateOf(UIViewType.Login)
|
||||
.OnEntry(() =>
|
||||
{
|
||||
var view = SetupView(UIViewType.TwoFactor);
|
||||
LoadView(view);
|
||||
})
|
||||
.PermitIf(Trigger.Next, UIViewType.End, () => currentFlow == UIControllerFlow.Authentication)
|
||||
.PermitIf(Trigger.Next, UIViewType.Create, () => currentFlow == UIControllerFlow.Create)
|
||||
.PermitIf(Trigger.Next, UIViewType.Clone, () => currentFlow == UIControllerFlow.Clone);
|
||||
|
||||
machine.Configure(UIViewType.Create)
|
||||
.OnEntry(() =>
|
||||
{
|
||||
var view = SetupView(UIViewType.Create);
|
||||
LoadView(view);
|
||||
})
|
||||
.Permit(Trigger.Next, UIViewType.End);
|
||||
|
||||
machine.Configure(UIViewType.Clone)
|
||||
.OnEntry(() =>
|
||||
{
|
||||
var view = SetupView(UIViewType.Clone);
|
||||
LoadView(view);
|
||||
})
|
||||
.Permit(Trigger.Next, UIViewType.End);
|
||||
|
||||
machine.Configure(UIViewType.End)
|
||||
.OnEntry(() =>
|
||||
{
|
||||
transition.OnCompleted();
|
||||
transition.Dispose();
|
||||
})
|
||||
.Permit(Trigger.Next, UIViewType.None);
|
||||
}
|
||||
|
||||
private void LoadView(IView view)
|
||||
{
|
||||
transition.OnNext(view as UserControl);
|
||||
}
|
||||
|
||||
IView SetupView(UIViewType viewType)
|
||||
{
|
||||
IViewModel viewModel;
|
||||
if (viewType == UIViewType.TwoFactor)
|
||||
viewModel = uiProvider.GetService<ITwoFactorViewModel>();
|
||||
else
|
||||
{
|
||||
var dvm = factory.GetViewModel(viewType);
|
||||
disposables.Add(dvm);
|
||||
viewModel = dvm.Value;
|
||||
}
|
||||
|
||||
var dv = factory.GetView(viewType);
|
||||
disposables.Add(dv);
|
||||
var view = dv.Value;
|
||||
|
||||
view.ViewModel = viewModel;
|
||||
return view;
|
||||
}
|
||||
|
||||
void Fire(Trigger next)
|
||||
{
|
||||
Debug.WriteLine("Firing {0}", next);
|
||||
machine.Fire(next);
|
||||
}
|
||||
|
||||
public IObservable<UserControl> SelectFlow(UIControllerFlow choice)
|
||||
{
|
||||
currentFlow = choice;
|
||||
transition = new Subject<UserControl>();
|
||||
transition.Subscribe((o) => { }, _ => Fire(Trigger.Next));
|
||||
return transition;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
Fire((Trigger)(int)currentFlow);
|
||||
}
|
||||
|
||||
#region IDisposable Support
|
||||
private bool disposedValue = false; // To detect redundant calls
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
disposables.Dispose();
|
||||
transition.Dispose();
|
||||
}
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
|
@ -1,22 +1,23 @@
|
|||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.ComponentModel.Composition;
|
||||
using GitHub.Models;
|
||||
using Octokit;
|
||||
using Account = GitHub.Models.Account;
|
||||
|
||||
namespace GitHub
|
||||
namespace GitHub.Factories
|
||||
{
|
||||
[Export(typeof(IAccountFactory))]
|
||||
public class AccountFactory : IAccountFactory
|
||||
{
|
||||
public IAccount CreateAccount(
|
||||
IRepositoryHost repositoryHost,
|
||||
Octokit.User user)
|
||||
User user)
|
||||
{
|
||||
return new Account(repositoryHost, user);
|
||||
}
|
||||
|
||||
public IAccount CreateAccount(
|
||||
IRepositoryHost repositoryHost,
|
||||
Octokit.Organization organization)
|
||||
Organization organization)
|
||||
{
|
||||
return new Account(repositoryHost, organization);
|
||||
}
|
|
@ -1,11 +1,15 @@
|
|||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.ComponentModel.Composition;
|
||||
using GitHub.Api;
|
||||
using GitHub.Authentication;
|
||||
using GitHub.Caches;
|
||||
using GitHub.Models;
|
||||
using GitHub.Primitives;
|
||||
using GitHub.Services;
|
||||
using Octokit;
|
||||
using Octokit.Reactive;
|
||||
using ApiClient = GitHub.Api.ApiClient;
|
||||
|
||||
namespace GitHub.Api
|
||||
namespace GitHub.Factories
|
||||
{
|
||||
[Export(typeof(IApiClientFactory))]
|
||||
[PartCreationPolicy(CreationPolicy.Shared)]
|
|
@ -1,9 +1,12 @@
|
|||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using GitHub.Caches;
|
||||
using GitHub.Info;
|
||||
using GitHub.Primitives;
|
||||
using Rothko;
|
||||
|
||||
namespace GitHub
|
||||
namespace GitHub.Factories
|
||||
{
|
||||
[Export(typeof(IHostCacheFactory))]
|
||||
[PartCreationPolicy(CreationPolicy.Shared)]
|
|
@ -1,6 +1,6 @@
|
|||
using Akavache;
|
||||
|
||||
namespace GitHub
|
||||
namespace GitHub.Factories
|
||||
{
|
||||
public interface IBlobCacheFactory
|
||||
{
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using GitHub.Caches;
|
||||
using GitHub.Primitives;
|
||||
|
||||
namespace GitHub
|
||||
namespace GitHub.Factories
|
||||
{
|
||||
public interface IHostCacheFactory
|
||||
{
|
|
@ -1,7 +1,9 @@
|
|||
using System.ComponentModel.Composition;
|
||||
using GitHub.Caches;
|
||||
using GitHub.Models;
|
||||
using GitHub.Primitives;
|
||||
|
||||
namespace GitHub
|
||||
namespace GitHub.Factories
|
||||
{
|
||||
[Export(typeof(IRepositoryHostFactory))]
|
||||
[PartCreationPolicy(CreationPolicy.Shared)]
|
|
@ -1,9 +1,8 @@
|
|||
using System.ComponentModel.Composition;
|
||||
using Akavache;
|
||||
using Akavache.Sqlite3;
|
||||
using GitHub.Extensions;
|
||||
|
||||
namespace GitHub
|
||||
namespace GitHub.Factories
|
||||
{
|
||||
[Export(typeof(IBlobCacheFactory))]
|
||||
[PartCreationPolicy(CreationPolicy.Shared)]
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Weavers>
|
||||
<NullGuard />
|
||||
<NullGuard ExcludeRegex="^GitHub.SampleData.*$" />
|
||||
</Weavers>
|
|
@ -11,6 +11,7 @@
|
|||
<AssemblyName>GitHub.App</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<NuGetPackageImportStamp>5547d2f4</NuGetPackageImportStamp>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<RunCodeAnalysis>true</RunCodeAnalysis>
|
||||
|
@ -41,7 +42,9 @@
|
|||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\packages\akavache.sqlite3.4.1.0\lib\Portable-Net45+Win8+WP8+Wpa81\Akavache.Sqlite3.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.ComponentModelHost, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
|
||||
<Reference Include="Microsoft.VisualStudio.Shell.12.0, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
|
||||
<Reference Include="Microsoft.VisualStudio.Shell.Immutable.10.0, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
|
||||
<Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
|
@ -74,6 +77,9 @@
|
|||
<Reference Include="SQLitePCL.raw">
|
||||
<HintPath>..\..\packages\SQLitePCL.raw_basic.0.7.1\lib\net45\SQLitePCL.raw.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Stateless">
|
||||
<HintPath>..\..\packages\Stateless.2.5.11.0\lib\portable-net40+sl50+win+wp80\Stateless.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.Composition" />
|
||||
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||
|
@ -107,20 +113,18 @@
|
|||
<Link>Properties\SolutionInfo.cs</Link>
|
||||
</Compile>
|
||||
<None Include="AkavacheSqliteLinkerOverride.cs" />
|
||||
<Compile Include="Authentication\IAccount.cs" />
|
||||
<Compile Include="Authentication\ITwoFactorChallengeHandler.cs" />
|
||||
<Compile Include="Authentication\TwoFactorChallengeHandler.cs" />
|
||||
<Compile Include="Authentication\TwoFactorRequiredUserError.cs" />
|
||||
<Compile Include="Caches\HostCache.cs" />
|
||||
<Compile Include="Caches\HostCacheFactory.cs" />
|
||||
<Compile Include="Caches\IBlobCacheFactory.cs" />
|
||||
<Compile Include="Caches\IHostCache.cs" />
|
||||
<Compile Include="Caches\IHostCacheFactory.cs" />
|
||||
<Compile Include="Factories\HostCacheFactory.cs" />
|
||||
<Compile Include="Factories\IBlobCacheFactory.cs" />
|
||||
<Compile Include="Factories\IHostCacheFactory.cs" />
|
||||
<Compile Include="Caches\ILoginCache.cs" />
|
||||
<Compile Include="Caches\ISharedCache.cs" />
|
||||
<Compile Include="Caches\LoginCache.cs" />
|
||||
<Compile Include="Caches\SharedCache.cs" />
|
||||
<Compile Include="Caches\SqlitePersistentBlobCacheFactory.cs" />
|
||||
<Compile Include="Factories\SqlitePersistentBlobCacheFactory.cs" />
|
||||
<Compile Include="GlobalSuppressions.cs" />
|
||||
<Compile Include="Info\EnvironmentExtensions.cs" />
|
||||
<Compile Include="Info\GitHubUrls.cs" />
|
||||
<Compile Include="Infrastructure\AppModeDetector.cs" />
|
||||
|
@ -131,33 +135,45 @@
|
|||
<Compile Include="Models\LocalRepositoriesHost.cs" />
|
||||
<Compile Include="Models\RepositoryHost.cs" />
|
||||
<Compile Include="Models\RepositoryHosts.cs" />
|
||||
<Compile Include="Models\IRepositoryHost.cs" />
|
||||
<Compile Include="Models\IRepositoryHosts.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Services\AccountFactory.cs" />
|
||||
<Compile Include="Services\ApiClient.cs" />
|
||||
<Compile Include="Services\ApiClientFactory.cs" />
|
||||
<Compile Include="SampleData\SampleViewModels.cs" />
|
||||
<Compile Include="Factories\AccountFactory.cs" />
|
||||
<Compile Include="Api\ApiClient.cs" />
|
||||
<Compile Include="Factories\ApiClientFactory.cs" />
|
||||
<None Include="Services\Browser.cs" />
|
||||
<Compile Include="Services\EnterpriseProbe.cs" />
|
||||
<Compile Include="Services\ErrorMap.cs" />
|
||||
<Compile Include="Services\ErrorMessage.cs" />
|
||||
<Compile Include="Services\ErrorMessageTranslator.cs" />
|
||||
<Compile Include="Services\GitHubCredentialStore.cs" />
|
||||
<Compile Include="Services\IAccountFactory.cs" />
|
||||
<Compile Include="Services\IApiClient.cs" />
|
||||
<Compile Include="Services\IApiClientFactory.cs" />
|
||||
<Compile Include="Services\IEnterpriseProbe.cs" />
|
||||
<Compile Include="Services\IRepositoryHostFactory.cs" />
|
||||
<Compile Include="Services\RepositoryHostFactory.cs" />
|
||||
<Compile Include="Factories\RepositoryHostFactory.cs" />
|
||||
<Compile Include="Services\StandardUserErrors.cs" />
|
||||
<Compile Include="Controllers\UIController.cs" />
|
||||
<Compile Include="Services\Translation.cs" />
|
||||
<Compile Include="UserErrors\PublishRepositoryUserError.cs" />
|
||||
<Compile Include="UserErrors\PrivateRepositoryOnFreeAccountUserError.cs" />
|
||||
<Compile Include="UserErrors\PrivateRepositoryQuotaExceededUserError.cs" />
|
||||
<Compile Include="ViewModels\CreateRepoViewModel.cs" />
|
||||
<Compile Include="ViewModels\CloneRepoViewModel.cs" />
|
||||
<Compile Include="ViewModels\LoginControlViewModel.cs" />
|
||||
<Compile Include="ViewModels\TwoFactorDialogViewModel.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="FodyWeavers.xml" />
|
||||
<Resource Include="Images\default_org_avatar.png" />
|
||||
<Resource Include="Images\default_user_avatar.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GitHub.Exports.Reactive\GitHub.Exports.Reactive.csproj">
|
||||
<Project>{e4ed0537-d1d9-44b6-9212-3096d7c3f7a1}</Project>
|
||||
<Name>GitHub.Exports.Reactive</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\GitHub.Exports\GitHub.Exports.csproj">
|
||||
<Project>{9aea02db-02b5-409c-b0ca-115d05331a6b}</Project>
|
||||
<Name>GitHub.Exports</Name>
|
||||
|
@ -179,7 +195,6 @@
|
|||
<Name>Rothko</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
|
|
Двоичный файл не отображается.
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 1.7 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 1.9 KiB |
|
@ -1,25 +1,24 @@
|
|||
using System.IO;
|
||||
using Rothko;
|
||||
using Environment = System.Environment;
|
||||
using System;
|
||||
|
||||
namespace GitHub
|
||||
namespace GitHub.Info
|
||||
{
|
||||
public static class EnvironmentExtensions
|
||||
{
|
||||
const string applicationName = "GitHub";
|
||||
|
||||
public static string GetLocalGitHubApplicationDataPath(this IEnvironment environment)
|
||||
public static string GetLocalGitHubApplicationDataPath(this Rothko.IEnvironment environment)
|
||||
{
|
||||
return Path.Combine(environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||
applicationName);
|
||||
}
|
||||
|
||||
public static string GetApplicationDataPath(this IEnvironment environment)
|
||||
public static string GetApplicationDataPath(this Rothko.IEnvironment environment)
|
||||
{
|
||||
return Path.Combine(environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), applicationName);
|
||||
}
|
||||
|
||||
public static string GetProgramFilesPath(this IEnvironment environment)
|
||||
public static string GetProgramFilesPath(this Rothko.IEnvironment environment)
|
||||
{
|
||||
return environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using GitHub.Models;
|
||||
|
||||
namespace GitHub.Info
|
||||
{
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
using Akavache;
|
||||
using GitHub.Api;
|
||||
using GitHub.Authentication;
|
||||
using GitHub.Caches;
|
||||
using GitHub.Primitives;
|
||||
using NullGuard;
|
||||
using ReactiveUI;
|
||||
using GitHub.Exports;
|
||||
|
||||
namespace GitHub.Models
|
||||
{
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
using System;
|
||||
using GitHub.Authentication;
|
||||
using GitHub.Factories;
|
||||
using GitHub.Primitives;
|
||||
using ReactiveUI;
|
||||
using GitHub.Exports;
|
||||
|
||||
namespace GitHub.Models
|
||||
{
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
using System;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
using GitHub.Api;
|
||||
using GitHub.Authentication;
|
||||
using GitHub.Caches;
|
||||
using GitHub.Primitives;
|
||||
using ReactiveUI;
|
||||
using GitHub.Exports;
|
||||
|
||||
namespace GitHub.Models
|
||||
{
|
||||
|
|
|
@ -7,15 +7,18 @@ using System.Linq;
|
|||
using System.Net;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
using GitHub.Api;
|
||||
using GitHub.Authentication;
|
||||
using GitHub.Caches;
|
||||
using GitHub.Extensions;
|
||||
using GitHub.Extensions.Reactive;
|
||||
using GitHub.Factories;
|
||||
using GitHub.Primitives;
|
||||
using GitHub.Services;
|
||||
using NLog;
|
||||
using Octokit;
|
||||
using ReactiveUI;
|
||||
using Authorization = Octokit.Authorization;
|
||||
using GitHub.Exports;
|
||||
|
||||
namespace GitHub.Models
|
||||
{
|
||||
|
|
|
@ -5,9 +5,11 @@ using System.Reactive;
|
|||
using System.Reactive.Linq;
|
||||
using Akavache;
|
||||
using GitHub.Authentication;
|
||||
using GitHub.Caches;
|
||||
using GitHub.Extensions.Reactive;
|
||||
using GitHub.Factories;
|
||||
using GitHub.Primitives;
|
||||
using ReactiveUI;
|
||||
using GitHub.Exports;
|
||||
|
||||
namespace GitHub.Models
|
||||
{
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reactive;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media.Imaging;
|
||||
using GitHub.Helpers;
|
||||
using GitHub.Models;
|
||||
using GitHub.Primitives;
|
||||
using GitHub.Services;
|
||||
using GitHub.Validation;
|
||||
using GitHub.ViewModels;
|
||||
using Octokit;
|
||||
using ReactiveUI;
|
||||
using Account = Octokit.Account;
|
||||
|
||||
namespace GitHub.SampleData
|
||||
{
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
public class CreateRepoViewModelDesigner : ReactiveObject, ICreateRepoViewModel
|
||||
{
|
||||
public CreateRepoViewModelDesigner()
|
||||
{
|
||||
RepositoryName = "Hello-World";
|
||||
Description = "A description";
|
||||
KeepPrivate = true;
|
||||
Accounts = new ReactiveList<IAccount> { new AccountDesigner("GitHub") };
|
||||
}
|
||||
|
||||
public string RepositoryName { get; private set; }
|
||||
public string SafeRepositoryName { get; private set; }
|
||||
public bool ShowRepositoryNameWarning { get; private set; }
|
||||
public string RepositoryNameWarningText { get; private set; }
|
||||
public ReactivePropertyValidator<string> RepositoryNameValidator { get; private set; }
|
||||
public string Description { get; set; }
|
||||
public ReactivePropertyValidator<IAccount> SelectedAccountValidator { get; private set; }
|
||||
public bool KeepPrivate { get; set; }
|
||||
public bool CanKeepPrivate { get; private set; }
|
||||
public bool ShowUpgradeToMicroPlanWarning { get; private set; }
|
||||
public bool ShowUpgradePlanWarning { get; private set; }
|
||||
public ReactiveCommand<Unit> CreateRepository { get; private set; }
|
||||
public bool IsPublishing { get; private set; }
|
||||
public ReactiveCommand<Object> UpgradeAccountPlan { get; private set; }
|
||||
public ReactiveCommand<Object> Reset { get; private set; }
|
||||
public ReactiveList<IAccount> Accounts { get; private set; }
|
||||
public IAccount SelectedAccount { get; private set; }
|
||||
|
||||
public ICommand OkCmd { get; private set; }
|
||||
public ICommand CancelCmd { get; private set; }
|
||||
}
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
public sealed class AccountDesigner : ReactiveObject, IAccount
|
||||
{
|
||||
public AccountDesigner()
|
||||
{
|
||||
}
|
||||
|
||||
public AccountDesigner(string name)
|
||||
{
|
||||
Name = name;
|
||||
Avatar = new AvatarProviderDesigner().DefaultOrgBitmapImage;
|
||||
IsGitHubStaff = false;
|
||||
IsSiteAdmin = false;
|
||||
}
|
||||
|
||||
public object Avatar { get; set; }
|
||||
public string Email { get; set; }
|
||||
public int Id { get; set; }
|
||||
public bool IsEnterprise { get; set; }
|
||||
public bool IsGitHub { get; set; }
|
||||
public bool IsLocal { get; set; }
|
||||
public bool IsOnFreePlan { get; set; }
|
||||
public bool HasMaximumPrivateRepositories { get; private set; }
|
||||
public bool IsSelected { get; set; }
|
||||
public bool IsUser { get; set; }
|
||||
public bool IsSiteAdmin { get; private set; }
|
||||
public bool IsGitHubStaff { get; private set; }
|
||||
public IRepositoryHost Host { get; set; }
|
||||
public string Login { get; set; }
|
||||
public string Name { get; set; }
|
||||
public int OwnedPrivateRepos { get; set; }
|
||||
public long PrivateReposInPlan { get; set; }
|
||||
|
||||
public void Update(User ghUser)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Update(Organization org)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
public class AvatarProviderDesigner : IAvatarProvider
|
||||
{
|
||||
public AvatarProviderDesigner()
|
||||
{
|
||||
DefaultUserBitmapImage = ImageHelper.CreateBitmapImage("pack://application:,,,/GitHub.App;component/Images/default_user_avatar.png");
|
||||
DefaultOrgBitmapImage = ImageHelper.CreateBitmapImage("pack://application:,,,/GitHub.App;component/Images/default_org_avatar.png");
|
||||
}
|
||||
|
||||
public BitmapImage DefaultUserBitmapImage { get; private set; }
|
||||
public BitmapImage DefaultOrgBitmapImage { get; private set; }
|
||||
|
||||
public IObservable<BitmapSource> GetAvatar(Account apiAccount)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IObservable<Unit> InvalidateAvatar(Account apiAccount)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IObservable<BitmapSource> GetAvatar(string email)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
[ExcludeFromCodeCoverage]
|
||||
public class RepositoryModelDesigner : ReactiveObject, IRepositoryModel
|
||||
{
|
||||
public RepositoryModelDesigner()
|
||||
{
|
||||
}
|
||||
|
||||
public RepositoryModelDesigner(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public RepositoryModelDesigner(string name, string owner)
|
||||
{
|
||||
Owner = owner;
|
||||
Name = name;
|
||||
OwnerWithSlash = owner + "/";
|
||||
NameWithOwner = OwnerWithSlash + name;
|
||||
HasRemote = IsHosted = true;
|
||||
AdditionalClones = new HashSet<string>();
|
||||
ToolTip = "Repo Tooltip";
|
||||
IsPrivate = true;
|
||||
CanViewOnHost = true;
|
||||
}
|
||||
|
||||
public DateTimeOffset? LastShadowBackupTime { get; set; }
|
||||
public string LastShadowBackupSha1 { get; set; }
|
||||
public bool IsSelected { get; set; }
|
||||
public HostAddress HostAddress { get; private set; }
|
||||
public int? Id { get; set; }
|
||||
public bool IsLostOnDisk { get; set; }
|
||||
public string LocalWorkingDirectory { get; set; }
|
||||
public string LocalDotGitPath { get; set; }
|
||||
public UriString CloneUrl { get; set; }
|
||||
public UriString HttpsUrl { get; set; }
|
||||
public UriString SshUrl { get; set; }
|
||||
public UriString UpstreamCloneUrl { get; set; }
|
||||
public bool HasRemote { get; set; }
|
||||
public bool IsHosted { get; set; }
|
||||
public bool HasLocal { get; set; }
|
||||
public bool CanViewOnHost { get; private set; }
|
||||
public IRepositoryHost RepositoryHost { get; set; }
|
||||
|
||||
string IRepositoryModel.Owner
|
||||
{
|
||||
get { return Owner; }
|
||||
set { Owner = value; }
|
||||
}
|
||||
|
||||
public int? OwnerId { get; set; }
|
||||
|
||||
public string Owner { get; set; }
|
||||
public string OwnerWithSlash { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string NameWithOwner { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string ToolTip { get; set; }
|
||||
public Uri HostUri { get; set; }
|
||||
public bool IsCollaborator { get; set; }
|
||||
public bool IsCloning { get; set; }
|
||||
public bool IsFork { get; private set; }
|
||||
public bool HasDeployedGitIgnore { get; set; }
|
||||
public string NonGitHubRemoteHost { get; set; }
|
||||
public HashSet<string> AdditionalClones { get; private set; }
|
||||
|
||||
public bool IsPrivate { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,9 +1,8 @@
|
|||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Reactive.Threading.Tasks;
|
||||
using GitHub.Models;
|
||||
using Octokit.Internal;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Threading.Tasks;
|
||||
|
||||
namespace GitHub.Services
|
||||
{
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace GitHub.Services
|
||||
{
|
||||
public class ErrorMap
|
||||
{
|
||||
public ErrorMap(ErrorMessage defaultMessage) : this(defaultMessage, null, null)
|
||||
{
|
||||
}
|
||||
|
||||
public ErrorMap(ErrorMessage defaultMessage, IEnumerable<Translation> translations, IEnumerable<IRecoveryCommand> recoveryCommands)
|
||||
{
|
||||
this.defaultMessage = defaultMessage;
|
||||
this.translations = translations;
|
||||
RecoveryCommands = recoveryCommands;
|
||||
}
|
||||
|
||||
readonly ErrorMessage defaultMessage;
|
||||
readonly IEnumerable<Translation> translations;
|
||||
public IEnumerable<IRecoveryCommand> RecoveryCommands { get; private set; }
|
||||
|
||||
public ErrorMessage GetErrorInfo(Exception exception)
|
||||
{
|
||||
if (exception != null && translations != null)
|
||||
{
|
||||
var translated = (from t in translations
|
||||
let result = t.Translate(exception)
|
||||
where result != null
|
||||
select result).FirstOrDefault();
|
||||
|
||||
if (translated != null)
|
||||
return translated;
|
||||
}
|
||||
return defaultMessage;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace GitHub.Services
|
||||
{
|
||||
public class ErrorMessage
|
||||
{
|
||||
static readonly Regex placeholderRegex = new Regex(@"\$\d", RegexOptions.Compiled);
|
||||
readonly Lazy<bool> headingHasPlaceholders;
|
||||
readonly Lazy<bool> messageHasPlaceholders;
|
||||
|
||||
public ErrorMessage(string heading, string message)
|
||||
{
|
||||
Heading = heading;
|
||||
headingHasPlaceholders = new Lazy<bool>(() => placeholderRegex.IsMatch(heading));
|
||||
Message = message;
|
||||
messageHasPlaceholders = new Lazy<bool>(() => placeholderRegex.IsMatch(message));
|
||||
}
|
||||
|
||||
public string Heading { get; private set; }
|
||||
public string Message { get; private set; }
|
||||
|
||||
public bool HeadingHasPlaceholders
|
||||
{
|
||||
get { return headingHasPlaceholders.Value; }
|
||||
}
|
||||
|
||||
public bool MessageHasPlaceholders
|
||||
{
|
||||
get { return messageHasPlaceholders.Value; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace GitHub.Services
|
||||
{
|
||||
public class ErrorMessageTranslator
|
||||
{
|
||||
readonly IDictionary<ErrorType, ErrorMap> userErrorMappings;
|
||||
|
||||
public ErrorMessageTranslator(IDictionary<ErrorType, ErrorMap> userErrors)
|
||||
{
|
||||
userErrorMappings = userErrors;
|
||||
}
|
||||
|
||||
public UserError GetUserError(ErrorType errorType, Exception exception, params object[] context)
|
||||
{
|
||||
var translation = GetUserErrorTranslation(errorType, exception, context);
|
||||
return new UserError(translation.ErrorMessage, translation.CauseOrResolution, translation.RecoveryCommands, null, exception)
|
||||
{
|
||||
UserErrorIcon = StockUserErrorIcon.Error
|
||||
};
|
||||
}
|
||||
|
||||
public UserErrorTranslation GetUserErrorTranslation(ErrorType errorType, Exception exception, params object[] context)
|
||||
{
|
||||
ErrorMessage errorMessage = null;
|
||||
ErrorMap errorMap;
|
||||
if (userErrorMappings.TryGetValue(ErrorType.Global, out errorMap) && errorMap != null)
|
||||
{
|
||||
errorMessage = errorMap.GetErrorInfo(exception);
|
||||
}
|
||||
|
||||
if (errorMessage == null)
|
||||
{
|
||||
if (!userErrorMappings.TryGetValue(errorType, out errorMap) || errorMap == null)
|
||||
throw new InvalidOperationException("This should never happen!");
|
||||
}
|
||||
|
||||
errorMessage = errorMap.GetErrorInfo(exception) ?? new ErrorMessage("error", "Unknown error occurred");
|
||||
string details = errorMessage.Message;
|
||||
string heading = errorMessage.Heading;
|
||||
if (context != null && context.Any())
|
||||
{
|
||||
heading = String.Format(CultureInfo.InvariantCulture, heading, context);
|
||||
details = String.Format(CultureInfo.InvariantCulture, details, context);
|
||||
}
|
||||
|
||||
return new UserErrorTranslation(heading, details, errorMap.RecoveryCommands);
|
||||
}
|
||||
|
||||
public class UserErrorTranslation
|
||||
{
|
||||
public UserErrorTranslation(string errorMessage, string causeOrResolution, IEnumerable<IRecoveryCommand> recoveryCommands)
|
||||
{
|
||||
ErrorMessage = errorMessage;
|
||||
CauseOrResolution = causeOrResolution;
|
||||
RecoveryCommands = recoveryCommands;
|
||||
}
|
||||
|
||||
public string ErrorMessage { get; private set; }
|
||||
public string CauseOrResolution { get; private set; }
|
||||
public IEnumerable<IRecoveryCommand> RecoveryCommands { get; private set; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -2,12 +2,13 @@
|
|||
using System.Reactive.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Akavache;
|
||||
using GitHub.Caches;
|
||||
using GitHub.Extensions.Reactive;
|
||||
using GitHub.Primitives;
|
||||
using NLog;
|
||||
using Octokit;
|
||||
using LogManager = NLog.LogManager;
|
||||
|
||||
namespace GitHub
|
||||
namespace GitHub.Services
|
||||
{
|
||||
public class GitHubCredentialStore : ICredentialStore
|
||||
{
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GitHub.Services
|
||||
{
|
||||
|
|
|
@ -1,7 +1,17 @@
|
|||
using System;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using GitHub.Extensions;
|
||||
using GitHub.Info;
|
||||
using GitHub.Models;
|
||||
using NLog;
|
||||
using Octokit;
|
||||
using ReactiveUI;
|
||||
using ApiClient = GitHub.Api.ApiClient;
|
||||
|
||||
namespace GitHub.Services
|
||||
{
|
||||
|
@ -14,6 +24,7 @@ namespace GitHub.Services
|
|||
BranchUnpublishFailed,
|
||||
BranchListFailed,
|
||||
CannotDropFolder,
|
||||
CannotDropFolderUnauthorizedAccess,
|
||||
ClipboardFailed,
|
||||
ClonedFailed,
|
||||
CloneFailedNotLoggedIn,
|
||||
|
@ -44,6 +55,7 @@ namespace GitHub.Services
|
|||
ShellFailed,
|
||||
CustomShellFailed,
|
||||
WorkingDirectoryDoesNotExist,
|
||||
DefaultClonePathInvalid,
|
||||
MergeFailed,
|
||||
PowerShellNotFound,
|
||||
LoadingCommitsFailed,
|
||||
|
@ -56,14 +68,227 @@ namespace GitHub.Services
|
|||
|
||||
public static class StandardUserErrors
|
||||
{
|
||||
public static IObservable<RecoveryOptionResult> ShowUserErrorMessage(
|
||||
this Exception ex, ErrorType errorType, params object[] messageArgs)
|
||||
internal static readonly Lazy<ErrorMessageTranslator> Translator = new Lazy<ErrorMessageTranslator>(() => new ErrorMessageTranslator(new Dictionary<ErrorType, ErrorMap>
|
||||
{
|
||||
// TODO: Fix this. This is just placeholder logic. -@haacked
|
||||
Console.WriteLine(errorType);
|
||||
Console.WriteLine(messageArgs);
|
||||
MessageBox.Show(ex.Message);
|
||||
return Observable.Return(new RecoveryOptionResult());
|
||||
{
|
||||
// Exceptions are matched against global *FIRST* before they are matched against
|
||||
// specific operations.
|
||||
ErrorType.Global, Map(null,
|
||||
new Translation("Cannot find config file '(.*?)'", "The Git configuration file is missing.", "The global Git configuration file '$1' could not be found. Please open the options menu from the dashboard and update your name and email settings to create a new one."),
|
||||
new Translation<AuthorizationException>("Authentication failed", "Your credentials may be out of date. Please log out of the application and then log back in before retrying the operation."),
|
||||
new Translation<LoginAttemptsExceededException>("Maximum login attempts exceeded", "Please log out of the application and then log back in before retrying the operation. You may need to wait a few minutes."),
|
||||
new Translation("fatal: Authentication failed", "Authentication failed", "Your credentials may be out of date. Please log out of the application and then log back in before retrying the operation."),
|
||||
new Translation(@"({""message"":""Bad credentials.?""}|Bad credentials.?)", "Authentication failed", "Your credentials may be out of date. Please log out of the application and then log back in before retrying the operation."),
|
||||
new Translation<DirectoryNotFoundException>("Directory not found", "The directory does not exist"),
|
||||
new Translation<UnauthorizedAccessException>("Access denied", "You do not have permissions to access this file or folder"),
|
||||
new Translation("EPOLICYKEYAGE", "Unverified SSH Key", "{0}", ParseUnverifiedSshKeyMessageFromExceptionMessage))
|
||||
},
|
||||
{
|
||||
ErrorType.BranchCreateFailed, Map(Defaults("Failed to create new branch"),
|
||||
new Translation("No valid git object identified by '.+?' exists in the repository.", "Failed to create branch", "The current branch doesn’t have any commits."))
|
||||
},
|
||||
{
|
||||
ErrorType.BranchDeleteFailed, Map(Defaults("error", "Failed to delete the branch."),
|
||||
new Translation(@"fatal: .*? not found: did you run git update-server-info on the server\?",
|
||||
"Failed to delete the branch",
|
||||
"Please make sure the repository exists and that you have permissions to change it."),
|
||||
new Translation("error: The requested URL returned error: 403 while accessing .*",
|
||||
"Failed to delete the branch",
|
||||
"Please make sure the repository exists and that you have permissions to change it."),
|
||||
new Translation("fatal: Could not read from remote repository.",
|
||||
"Failed to delete the branch",
|
||||
"Please make sure the repository exists and that you have permissions to change it."),
|
||||
new Translation(@".*?\(deletion of the current branch prohibited\).*",
|
||||
"Cannot delete the default branch",
|
||||
"To delete this branch, log in to " + ApiClient.GitHubDotComHostName + " and change " +
|
||||
"the repository’s default branch to another branch first."))
|
||||
},
|
||||
{
|
||||
ErrorType.BranchUnpublishFailed, Map(Defaults("error", "Failed to unpublish the branch."),
|
||||
new Translation(@"fatal: .*? not found: did you run git update-server-info on the server\?",
|
||||
"Failed to unpublish the branch",
|
||||
"Please make sure the repository exists and that you have permissions to change it."),
|
||||
new Translation("error: The requested URL returned error: 403 while accessing .*",
|
||||
"Failed to unpublish the branch",
|
||||
"Please make sure the repository exists and that you have permissions to change it."),
|
||||
new Translation("fatal: Could not read from remote repository.",
|
||||
"Failed to unpublish the branch",
|
||||
"Please make sure the repository exists and that you have permissions to change it."),
|
||||
new Translation(@".*?\(deletion of the current branch prohibited\).*",
|
||||
"Cannot unpublish the default branch",
|
||||
"To unpublish this branch, log in to " + ApiClient.GitHubDotComHostName + " and change " +
|
||||
"the repository’s default branch to another branch first."))
|
||||
},
|
||||
{ ErrorType.ClipboardFailed, Map(Defaults("Failed to copy text to the clipboard.")) },
|
||||
{
|
||||
ErrorType.ClonedFailed, Map(Defaults("Failed to clone the repository '{0}'", "Please check your log file for more details, or email support if you are still having problems."),
|
||||
new[]
|
||||
{
|
||||
new Translation(@"fatal: bad config file line (\d+) in (.+)", "Failed to clone the repository '{0}'", @"The config file '$2' is corrupted at line $1. You may need to open the file and try to fix any errors."),
|
||||
new Translation("Process timed out", "Failed to clone the repository '{0}'", "The process timed out. The repository is in an unknown state and likely corrupted. Try deleting it and cloning again."),
|
||||
new Translation("Local inaccessible repositories already exist.", "Failed to clone the repository '{0}'", "Local directories with this repository’s name already exist but can’t be accessed. Try deleting them and trying again."),
|
||||
new Translation("Repo directory '(.*?)' already exists.", "Failed to clone the repository '{0}'", "Could not clone the repository '{0}' because the directory\n'$1' already exists and isn’t empty."),
|
||||
new Translation("Local clone at '(.*?)' is corrupted", "Failed to clone the repository '{0}'", "Failed to clone the repository because a local one exists already at '$1', but is corrupted. You will need to open a shell to debug the state of this repo."),
|
||||
new Translation("Your local changes to the following files would be overwritten by checkout", "Failed to check out branch", "Failed to check out branch because because local changes would be overwritten. Try committing changes and then checking out the branch again")
|
||||
})
|
||||
},
|
||||
{ ErrorType.CloneFailedNotLoggedIn, Map(Defaults("Clone failed", "Please login to your account before attempting to clone this repository.")) },
|
||||
{ ErrorType.EnterpriseConnectFailed, Map(Defaults("Connecting to GitHub Enterprise instance failed", "Could not find a GitHub Enterprise instance at '{0}'. Double check the URL and your internet/intranet connection.")) },
|
||||
{ ErrorType.LaunchEnterpriseConnectionFailed, Map(Defaults("Failed to launch the enterprise connection.")) },
|
||||
{ ErrorType.LogFileError, Map(Defaults("Could not open the log file", "Could not find or open the log file.")) },
|
||||
{ ErrorType.LoginFailed, Map(Defaults("login failed", "Unable to retrieve your user info from the server. A proxy server might be interfering with the request.")) },
|
||||
{ ErrorType.RepoCreationAsPrivateNotAvailableForFreePlan, Map(Defaults("Failed to create private repository", "You are currently on a free plan and unable to create private repositories. Either make the repository public or upgrade your account on the website to a plan that allows for private repositories.")) },
|
||||
{ ErrorType.RepoCreationFailed, Map(Defaults("Failed to create repository", "An error occurred while creating the repository. You might need to open a shell and debug the state of this repo.")) },
|
||||
{ ErrorType.RepoExistsOnDisk, Map(Defaults("Failed to create repository", "There is already a repository named '{0}' in the directory\n'{1}'.")) },
|
||||
{ ErrorType.RepositoryNotFoundOnDisk, Map(Defaults("Repository not found", "Could not find the repository '{0}' in the location '{1}'.\nDid you move it somewhere else on your filesystem?")) },
|
||||
{ ErrorType.RepoExistsForUser, Map(Defaults("Failed to create repository", "There is already a repository named '{0}' in your GitHub account.")) },
|
||||
{ ErrorType.RepoExistsInOrganization, Map(Defaults("Failed to create repository", "There is already a repository named '{0}' in the organization '{1}'.")) },
|
||||
{ ErrorType.LoadingWorkingDirectoryFailed, Map(Defaults("Failed to refresh the working directory", "You might need to open a shell and debug the state of this repo.")) },
|
||||
{
|
||||
ErrorType.RefreshFailed, Map(Defaults("Refresh failed", "Refresh failed unexpectedly. Please email support@github.com if this error persists."),
|
||||
new Translation<HttpRequestException>("Refresh failed", "Could not connect to the remote server. The server or your internect connection could be down")) },
|
||||
}));
|
||||
[SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
|
||||
static readonly Logger log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public static IObservable<RecoveryOptionResult> ShowUserErrorMessage(this Exception exception, ErrorType errorType, params object[] messageArgs)
|
||||
{
|
||||
return exception.DisplayErrorMessage(errorType, messageArgs, null);
|
||||
}
|
||||
|
||||
public static IObservable<RecoveryOptionResult> ShowUserErrorMessage(ErrorType errorType, params object[] messageArgs)
|
||||
{
|
||||
return DisplayErrorMessage(null, errorType, messageArgs, null);
|
||||
}
|
||||
|
||||
public static IObservable<RecoveryOptionResult> ShowUserThatRepoAlreadyExists(string repositoryName, string fullPath)
|
||||
{
|
||||
return DisplayErrorMessage(ErrorType.RepoExistsOnDisk, new object[] { repositoryName, fullPath }, new[] { OpenPathInExplorer(fullPath), Cancel });
|
||||
}
|
||||
|
||||
public static IObservable<RecoveryOptionResult> ShowCloneError(this Exception exception,
|
||||
ErrorType errorType,
|
||||
string displayName,
|
||||
string repositoryLocalWorkingDirectory)
|
||||
{
|
||||
return exception.DisplayErrorMessage(errorType, new object[] { displayName }, null);
|
||||
}
|
||||
|
||||
public static IObservable<RecoveryOptionResult> ShowUserErrorThatRequiresNavigatingToBilling(
|
||||
this Exception exception,
|
||||
IAccount account)
|
||||
{
|
||||
var errorType = (exception is PrivateRepositoryQuotaExceededException && account.IsOnFreePlan)
|
||||
? ErrorType.RepoCreationAsPrivateNotAvailableForFreePlan
|
||||
: ErrorType.RepoCreationOnGitHubFailed;
|
||||
|
||||
return exception.DisplayErrorMessage(
|
||||
errorType,
|
||||
new object[] { },
|
||||
new[] { OpenBrowser("View Plans", account.Billing()), Cancel });
|
||||
}
|
||||
|
||||
static IObservable<RecoveryOptionResult> DisplayErrorMessage(ErrorType errorType, object[] messageArgs, IEnumerable<IRecoveryCommand> recoveryOptions)
|
||||
{
|
||||
return DisplayErrorMessage(null, errorType, messageArgs, recoveryOptions);
|
||||
}
|
||||
|
||||
static IObservable<RecoveryOptionResult> DisplayErrorMessage(this Exception exception, ErrorType errorType, object[] messageArgs, IEnumerable<IRecoveryCommand> recoveryOptions)
|
||||
{
|
||||
var userError = Translator.Value.GetUserError(errorType, exception, messageArgs);
|
||||
|
||||
if (recoveryOptions != null)
|
||||
{
|
||||
userError.RecoveryOptions.AddRange(recoveryOptions);
|
||||
}
|
||||
if (!userError.RecoveryOptions.Any())
|
||||
userError.RecoveryOptions.Add(Ok);
|
||||
|
||||
return userError.Throw();
|
||||
}
|
||||
|
||||
public static string ParseUnverifiedSshKeyMessageFromExceptionMessage(Exception exception)
|
||||
{
|
||||
var index = exception.Message.IndexOf("[EPOLICYKEYAGE]", StringComparison.OrdinalIgnoreCase);
|
||||
return index != -1 ?
|
||||
exception.Message.Remove(index).Trim()
|
||||
: exception.Message;
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
|
||||
static IRecoveryCommand Ok
|
||||
{
|
||||
get
|
||||
{
|
||||
return new RecoveryCommandWithIcon("OK", "check", x => RecoveryOptionResult.CancelOperation)
|
||||
{
|
||||
IsDefault = true,
|
||||
IsCancel = true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
|
||||
static IRecoveryCommand Cancel
|
||||
{
|
||||
get
|
||||
{
|
||||
return new RecoveryCommandWithIcon("Cancel", "x", x => RecoveryOptionResult.CancelOperation)
|
||||
{
|
||||
IsCancel = true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
|
||||
static IRecoveryCommand OpenPathInExplorer(string path)
|
||||
{
|
||||
return new RecoveryCommandWithIcon("Open in Explorer", "file_directory", x =>
|
||||
{
|
||||
Process.Start(path);
|
||||
return RecoveryOptionResult.CancelOperation;
|
||||
});
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "url")]
|
||||
[SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
|
||||
static IRecoveryCommand OpenBrowser(string text, string url)
|
||||
{
|
||||
return new RecoveryCommandWithIcon(text, "link_external", x =>
|
||||
{
|
||||
//IoC.Get<IBrowser>().OpenUrl(url);
|
||||
return RecoveryOptionResult.CancelOperation;
|
||||
})
|
||||
{
|
||||
IsDefault = true,
|
||||
};
|
||||
}
|
||||
|
||||
static IObservable<RecoveryOptionResult> Throw(this UserError error)
|
||||
{
|
||||
//log.WarnException("Showing user error " + error.ErrorCauseOrResolution, error.InnerException);
|
||||
|
||||
return UserError.Throw(error);
|
||||
}
|
||||
|
||||
static ErrorMessage Defaults(string heading, string description)
|
||||
{
|
||||
return new ErrorMessage(heading, description);
|
||||
}
|
||||
|
||||
static ErrorMessage Defaults(string description)
|
||||
{
|
||||
return new ErrorMessage("error", description);
|
||||
}
|
||||
|
||||
static ErrorMap Map(ErrorMessage defaultMessage, params Translation[] translations)
|
||||
{
|
||||
return new ErrorMap(defaultMessage, translations, null);
|
||||
}
|
||||
|
||||
static ErrorMap Map(ErrorMessage defaultMessage, IEnumerable<IRecoveryCommand> recoveryCommands)
|
||||
{
|
||||
return new ErrorMap(defaultMessage, null, recoveryCommands);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using GitHub.Extensions;
|
||||
using Newtonsoft.Json;
|
||||
using Octokit;
|
||||
|
||||
namespace GitHub.Services
|
||||
{
|
||||
public class Translation
|
||||
{
|
||||
readonly ErrorMessage defaultMessage;
|
||||
readonly Func<Exception, ErrorMessage> translator;
|
||||
|
||||
public Translation(string original, string heading, string message)
|
||||
{
|
||||
Original = original;
|
||||
defaultMessage = new ErrorMessage(heading, message);
|
||||
}
|
||||
|
||||
public Translation(string original, Func<Exception, ErrorMessage> translator)
|
||||
{
|
||||
Original = original;
|
||||
this.translator = translator;
|
||||
}
|
||||
|
||||
public Translation(string original, string heading, string messageFormatString, Func<Exception, string> translator) : this(original, heading, messageFormatString)
|
||||
{
|
||||
this.translator = e => new ErrorMessage(heading, String.Format(CultureInfo.InvariantCulture, messageFormatString, translator(e)));
|
||||
}
|
||||
|
||||
public string Original { get; private set; }
|
||||
|
||||
public ErrorMessage Translate(Exception exception)
|
||||
{
|
||||
if (exception == null) throw new ArgumentNullException("exception");
|
||||
|
||||
var match = Match(exception);
|
||||
if (match == null) return null;
|
||||
|
||||
if (translator == null)
|
||||
{
|
||||
var exceptionMessageLine = match.Item2;
|
||||
if (exceptionMessageLine != null)
|
||||
{
|
||||
var heading = defaultMessage.HeadingHasPlaceholders
|
||||
? Regex.Replace(exceptionMessageLine, Original, defaultMessage.Heading)
|
||||
: defaultMessage.Heading;
|
||||
|
||||
var message = defaultMessage.MessageHasPlaceholders
|
||||
? Regex.Replace(exceptionMessageLine, Original, defaultMessage.Message)
|
||||
: defaultMessage.Message;
|
||||
return new ErrorMessage(heading, message);
|
||||
}
|
||||
|
||||
return defaultMessage;
|
||||
}
|
||||
|
||||
return translator(exception);
|
||||
}
|
||||
|
||||
// Returns a tuple indicating whether this translation is a match for the exception and the regex line if existing.
|
||||
protected virtual Tuple<Translation, string> Match(Exception exception)
|
||||
{
|
||||
string exceptionMessage = exception.Message;
|
||||
|
||||
var apiException = exception as ApiValidationException;
|
||||
if (apiException != null && apiException.ApiError != null && apiException.ApiError.Errors != null)
|
||||
{
|
||||
var error = apiException.ApiError.Errors.FirstOrDefault();
|
||||
if (error != null)
|
||||
exceptionMessage = error.Message ?? exceptionMessage;
|
||||
}
|
||||
|
||||
if (Original == exceptionMessage) return new Tuple<Translation, string>(this, null);
|
||||
|
||||
var exceptionMessageLines = exceptionMessage.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (exceptionMessageLines.Any(l => l == Original)) return new Tuple<Translation, string>(this, null);
|
||||
|
||||
try
|
||||
{
|
||||
// If the response is a JSON response from the API, let's
|
||||
// look at all the messages for an exact match.
|
||||
var githubError =
|
||||
exceptionMessage.StartsWith('{') || exceptionMessage.StartsWith('[')
|
||||
? JsonConvert.DeserializeObject<ApiError>(exceptionMessage)
|
||||
: null;
|
||||
if (githubError != null
|
||||
&& (githubError.Message == Original
|
||||
|| (githubError.Errors != null && githubError.Errors.Any(e => e.Message == Original))))
|
||||
{
|
||||
return new Tuple<Translation, string>(this, null);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Ignore. We were probably wrong about this being a proper GitHubError API response.
|
||||
}
|
||||
|
||||
var regexMatchingLine = exceptionMessageLines.FirstOrDefault(l => Regex.IsMatch(l, Original, RegexOptions.IgnoreCase));
|
||||
return regexMatchingLine != null ? new Tuple<Translation, string>(this, regexMatchingLine) : null;
|
||||
}
|
||||
}
|
||||
|
||||
public class Translation<TException> : Translation where TException : Exception
|
||||
{
|
||||
public Translation(string heading, string message) : base(null, heading, message)
|
||||
{
|
||||
}
|
||||
|
||||
public Translation(string heading) : base(null, e => new ErrorMessage(heading, e.Message))
|
||||
{
|
||||
}
|
||||
|
||||
protected override Tuple<Translation, string> Match(Exception exception)
|
||||
{
|
||||
if (exception is TException) return new Tuple<Translation, string>(null, null);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using ReactiveUI;
|
||||
|
||||
namespace GitHub.UserErrors
|
||||
{
|
||||
public class PrivateRepositoryOnFreeAccountUserError : PublishRepositoryUserError
|
||||
{
|
||||
public PrivateRepositoryOnFreeAccountUserError(string errorMessage, string errorCauseOrResolution = null)
|
||||
: base(errorMessage, errorCauseOrResolution)
|
||||
{
|
||||
UserErrorIcon = StockUserErrorIcon.Error;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using GitHub.Models;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace GitHub.UserErrors
|
||||
{
|
||||
public class PrivateRepositoryQuotaExceededUserError : PublishRepositoryUserError
|
||||
{
|
||||
public PrivateRepositoryQuotaExceededUserError(IAccount account, string errorMessage, string errorCauseOrResolution = null)
|
||||
: base(errorMessage, errorCauseOrResolution)
|
||||
{
|
||||
UserErrorIcon = StockUserErrorIcon.Error;
|
||||
UsedPrivateSlots = account.OwnedPrivateRepos;
|
||||
AvaliblePrivateSlots = account.PrivateReposInPlan;
|
||||
}
|
||||
|
||||
public long AvaliblePrivateSlots { get; set; }
|
||||
|
||||
public int UsedPrivateSlots { get; set; }
|
||||
|
||||
public static IObservable<RecoveryOptionResult> Throw(IAccount account)
|
||||
{
|
||||
var errorMessage = string.Format(CultureInfo.InvariantCulture,
|
||||
"You are using {0} out of {1} private repositories.", account.OwnedPrivateRepos, account.PrivateReposInPlan);
|
||||
|
||||
return Throw(new PrivateRepositoryQuotaExceededUserError(account, errorMessage));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using GitHub.Models;
|
||||
using GitHub.Services;
|
||||
using Octokit;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace GitHub.UserErrors
|
||||
{
|
||||
public class PublishRepositoryUserError : UserError
|
||||
{
|
||||
public PublishRepositoryUserError(string errorMessage, string errorCauseOrResolution = null)
|
||||
: base(errorMessage, errorCauseOrResolution)
|
||||
{
|
||||
UserErrorIcon = StockUserErrorIcon.Error;
|
||||
}
|
||||
|
||||
public static IObservable<RecoveryOptionResult> Throw(Exception innerException = null)
|
||||
{
|
||||
var translation = StandardUserErrors.Translator.Value.GetUserErrorTranslation(ErrorType.RepoCreationOnGitHubFailed, innerException);
|
||||
return Throw(new PublishRepositoryUserError(translation.ErrorMessage, translation.CauseOrResolution));
|
||||
}
|
||||
|
||||
public static IObservable<RecoveryOptionResult> Throw(string errorMessage, string errorCauseOrResolution = null)
|
||||
{
|
||||
return Throw(new PublishRepositoryUserError(errorMessage, errorCauseOrResolution));
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Because no. It's only for that kind of exception")]
|
||||
public static void ThrowPrivateQuotaExceeded(PrivateRepositoryQuotaExceededException exception, IAccount account)
|
||||
{
|
||||
if (account.IsOnFreePlan)
|
||||
{
|
||||
var translation = StandardUserErrors.Translator.Value.GetUserErrorTranslation(ErrorType.RepoCreationOnGitHubFailed, exception);
|
||||
Throw(new PrivateRepositoryOnFreeAccountUserError(translation.ErrorMessage, translation.CauseOrResolution));
|
||||
}
|
||||
else
|
||||
{
|
||||
PrivateRepositoryQuotaExceededUserError.Throw(account);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Windows.Input;
|
||||
using GitHub.Exports;
|
||||
using GitHub.Models;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace GitHub.ViewModels
|
||||
{
|
||||
[ExportViewModel(ViewType=UIViewType.Clone)]
|
||||
public class CloneRepoViewModel : ICloneRepoViewModel
|
||||
{
|
||||
public ReactiveCommand<object> CancelCommand { get; private set; }
|
||||
public ICommand CancelCmd { get { return CancelCommand; } }
|
||||
public IObservable<object> Cancelling { get { return CancelCommand; } }
|
||||
|
||||
public ReactiveCommand<object> OkCommand { get; private set; }
|
||||
public ICommand OkCmd { get { return OkCommand; } }
|
||||
|
||||
[ImportingConstructor]
|
||||
public CloneRepoViewModel(IRepositoryHosts hosts)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
using System.Reactive;
|
||||
using System.Windows.Input;
|
||||
using GitHub.Models;
|
||||
using GitHub.Validation;
|
||||
using ReactiveUI;
|
||||
using GitHub.Exports;
|
||||
|
||||
namespace GitHub.ViewModels
|
||||
{
|
||||
[ExportViewModel(ViewType=UIViewType.Create)]
|
||||
public class CreateRepoViewModel : ICreateRepoViewModel
|
||||
{
|
||||
public ReactiveCommand<object> CancelCommand { get; private set; }
|
||||
public ICommand CancelCmd { get { return CancelCommand; } }
|
||||
|
||||
public ReactiveCommand<object> OkCommand { get; private set; }
|
||||
public ICommand OkCmd { get { return OkCommand; } }
|
||||
|
||||
public string RepositoryName { get; private set; }
|
||||
public string SafeRepositoryName { get; private set; }
|
||||
public bool ShowRepositoryNameWarning { get; private set; }
|
||||
public string RepositoryNameWarningText { get; private set; }
|
||||
public ReactivePropertyValidator<string> RepositoryNameValidator { get; private set; }
|
||||
public string Description { get; set; }
|
||||
public ReactivePropertyValidator<IAccount> SelectedAccountValidator { get; private set; }
|
||||
public bool KeepPrivate { get; set; }
|
||||
public bool CanKeepPrivate { get; private set; }
|
||||
public bool ShowUpgradeToMicroPlanWarning { get; private set; }
|
||||
public bool ShowUpgradePlanWarning { get; private set; }
|
||||
public ReactiveCommand<Unit> CreateRepository { get; private set; }
|
||||
public bool IsPublishing { get; private set; }
|
||||
public ReactiveCommand<Object> UpgradeAccountPlan { get; private set; }
|
||||
public ReactiveCommand<Object> Reset { get; private set; }
|
||||
public ReactiveList<IAccount> Accounts { get; private set; }
|
||||
public IAccount SelectedAccount { get; private set; }
|
||||
|
||||
}
|
||||
}
|
|
@ -5,23 +5,24 @@ using System.Diagnostics.CodeAnalysis;
|
|||
using System.Globalization;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
using System.Windows.Input;
|
||||
using GitHub.Authentication;
|
||||
using GitHub.Exports;
|
||||
using GitHub.Extensions;
|
||||
using GitHub.Info;
|
||||
using GitHub.Models;
|
||||
using GitHub.Primitives;
|
||||
using GitHub.Services;
|
||||
using GitHub.Validation;
|
||||
using GitHub.Exports;
|
||||
using NullGuard;
|
||||
using ReactiveUI;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace GitHub.ViewModels
|
||||
{
|
||||
[SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable")]
|
||||
[Export(typeof(ILoginDialog))]
|
||||
[ExportViewModel(ViewType=UIViewType.Login)]
|
||||
[PartCreationPolicy(CreationPolicy.NonShared)]
|
||||
public class LoginControlViewModel : ReactiveValidatableObject, ILoginDialog, IDisposable
|
||||
public class LoginControlViewModel : ReactiveValidatableObject, ILoginViewModel, IDisposable
|
||||
{
|
||||
readonly Lazy<IEnterpriseProbe> lazyEnterpriseProbe;
|
||||
const string notEnterpriseServerError = "Not an Enterprise server. Please enter an Enterprise URL";
|
||||
|
@ -30,7 +31,7 @@ namespace GitHub.ViewModels
|
|||
public ICommand LoginCmd { get { return LoginCommand; } }
|
||||
public ReactiveCommand<object> CancelCommand { get; private set; }
|
||||
public ICommand CancelCmd { get { return CancelCommand; } }
|
||||
public IObservable<object> CancelEvt { get { return CancelCommand; } }
|
||||
public IObservable<object> Cancelling { get { return CancelCommand; } }
|
||||
|
||||
public ReactiveCommand<object> ForgotPasswordCommand { get; private set; }
|
||||
public ReactiveCommand<object> ShowDotComLoginCommand { get; set; }
|
||||
|
|
|
@ -2,19 +2,20 @@
|
|||
using System.ComponentModel.Composition;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Reactive.Linq;
|
||||
using System.Windows.Input;
|
||||
using GitHub.Authentication;
|
||||
using GitHub.Exports;
|
||||
using GitHub.Services;
|
||||
using GitHub.Validation;
|
||||
using NullGuard;
|
||||
using Octokit;
|
||||
using ReactiveUI;
|
||||
using GitHub.Exports;
|
||||
|
||||
namespace GitHub.ViewModels
|
||||
{
|
||||
[Export(typeof(ITwoFactorDialog))]
|
||||
[Export(typeof(ITwoFactorViewModel))]
|
||||
[PartCreationPolicy(CreationPolicy.Shared)]
|
||||
public class TwoFactorDialogViewModel : ReactiveValidatableObject, ITwoFactorDialog
|
||||
public class TwoFactorDialogViewModel : ReactiveValidatableObject, ITwoFactorViewModel
|
||||
{
|
||||
bool isAuthenticationCodeSent;
|
||||
string authenticationCode;
|
||||
|
@ -106,6 +107,11 @@ namespace GitHub.ViewModels
|
|||
public ReactiveCommand<RecoveryOptionResult> ShowHelpCommand { get; private set; }
|
||||
public ReactiveCommand<RecoveryOptionResult> ResendCodeCommand { get; private set; }
|
||||
|
||||
public ICommand OkCmd { get { return OkCommand; } }
|
||||
public ICommand CancelCmd { get { return CancelCommand; } }
|
||||
public ICommand ShowHelpCmd { get { return ShowHelpCommand; } }
|
||||
public ICommand ResendCodeCmd { get { return ResendCodeCommand; } }
|
||||
|
||||
public IObservable<RecoveryOptionResult> Show(TwoFactorRequiredUserError error)
|
||||
{
|
||||
TwoFactorType = error.TwoFactorType;
|
||||
|
|
|
@ -19,4 +19,5 @@
|
|||
<package id="Rx-XAML" version="2.2.5" targetFramework="net45" />
|
||||
<package id="Splat" version="1.6.0" targetFramework="net45" />
|
||||
<package id="SQLitePCL.raw_basic" version="0.7.1" targetFramework="net45" />
|
||||
<package id="Stateless" version="2.5.11.0" targetFramework="net45" />
|
||||
</packages>
|
|
@ -1,10 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using GitHub.Authentication;
|
||||
using GitHub.Primitives;
|
||||
using Octokit;
|
||||
|
||||
namespace GitHub
|
||||
namespace GitHub.Api
|
||||
{
|
||||
public interface IApiClient
|
||||
{
|
|
@ -1,9 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reactive;
|
||||
using GitHub.Models;
|
||||
using Octokit;
|
||||
|
||||
namespace GitHub
|
||||
namespace GitHub.Caches
|
||||
{
|
||||
/// <summary>
|
||||
/// Per host cache data.
|
|
@ -1,15 +1,16 @@
|
|||
using GitHub.Models;
|
||||
using Octokit;
|
||||
|
||||
namespace GitHub
|
||||
namespace GitHub.Factories
|
||||
{
|
||||
public interface IAccountFactory
|
||||
{
|
||||
IAccount CreateAccount(
|
||||
IRepositoryHost repositoryHost,
|
||||
Octokit.User user);
|
||||
User user);
|
||||
|
||||
IAccount CreateAccount(
|
||||
IRepositoryHost repositoryHost,
|
||||
Octokit.Organization organization);
|
||||
Organization organization);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,7 @@
|
|||
namespace GitHub
|
||||
using GitHub.Api;
|
||||
using GitHub.Primitives;
|
||||
|
||||
namespace GitHub.Factories
|
||||
{
|
||||
public interface IApiClientFactory
|
||||
{
|
|
@ -1,6 +1,7 @@
|
|||
using GitHub.Models;
|
||||
using GitHub.Primitives;
|
||||
|
||||
namespace GitHub
|
||||
namespace GitHub.Factories
|
||||
{
|
||||
public interface IRepositoryHostFactory
|
||||
{
|
|
@ -0,0 +1,112 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{E4ED0537-D1D9-44B6-9212-3096D7C3F7A1}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>GitHub</RootNamespace>
|
||||
<AssemblyName>GitHub.Exports.Reactive</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Octokit">
|
||||
<HintPath>..\..\packages\Octokit.0.6.2\lib\net45\Octokit.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="ReactiveUI">
|
||||
<HintPath>..\..\packages\reactiveui-core.6.3.1\lib\Net45\ReactiveUI.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Splat">
|
||||
<HintPath>..\..\packages\Splat.1.6.0\lib\Net45\Splat.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Reactive.Core">
|
||||
<HintPath>..\..\packages\Rx-Core.2.2.5\lib\net45\System.Reactive.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Reactive.Interfaces">
|
||||
<HintPath>..\..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Reactive.Linq">
|
||||
<HintPath>..\..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Reactive.PlatformServices">
|
||||
<HintPath>..\..\packages\Rx-PlatformServices.2.2.5\lib\net45\System.Reactive.PlatformServices.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Reactive.Windows.Threading">
|
||||
<HintPath>..\..\packages\Rx-XAML.2.2.5\lib\net45\System.Reactive.Windows.Threading.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.TeamFoundation.Controls, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer\Microsoft.TeamFoundation.Controls.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xaml" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="WindowsBase" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\..\script\SolutionInfo.cs">
|
||||
<Link>Properties\SolutionInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Factories\IApiClientFactory.cs" />
|
||||
<Compile Include="Factories\IAccountFactory.cs" />
|
||||
<Compile Include="Authentication\ITwoFactorChallengeHandler.cs" />
|
||||
<Compile Include="Caches\IHostCache.cs" />
|
||||
<Compile Include="Factories\IRepositoryHostFactory.cs" />
|
||||
<Compile Include="Models\IAccount.cs" />
|
||||
<Compile Include="Models\IRepositoryHost.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Api\IApiClient.cs" />
|
||||
<Compile Include="Services\IAvatarProvider.cs" />
|
||||
<Compile Include="ViewModels\ICreateRepoViewModel.cs" />
|
||||
<Compile Include="Validation\ReactivePropertyValidator.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GitHub.Exports\GitHub.Exports.csproj">
|
||||
<Project>{9aea02db-02b5-409c-b0ca-115d05331a6b}</Project>
|
||||
<Name>GitHub.Exports</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\GitHub.Extensions\GitHub.Extensions.csproj">
|
||||
<Project>{6afe2e2d-6db0-4430-a2ea-f5f5388d2f78}</Project>
|
||||
<Name>GitHub.Extensions</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -1,10 +1,8 @@
|
|||
using GitHub.Models;
|
||||
using Octokit;
|
||||
using ReactiveUI;
|
||||
using Octokit;
|
||||
|
||||
namespace GitHub
|
||||
namespace GitHub.Models
|
||||
{
|
||||
public interface IAccount : IReactiveObject
|
||||
public interface IAccount
|
||||
{
|
||||
string Email { get; }
|
||||
int Id { get; }
|
|
@ -1,11 +1,14 @@
|
|||
using System;
|
||||
using System.Reactive;
|
||||
using GitHub.Api;
|
||||
using GitHub.Authentication;
|
||||
using GitHub.Caches;
|
||||
using GitHub.Primitives;
|
||||
using ReactiveUI;
|
||||
using GitHub.Exports;
|
||||
|
||||
namespace GitHub.Models
|
||||
{
|
||||
public interface IRepositoryHost : IReactiveObject
|
||||
public interface IRepositoryHost
|
||||
{
|
||||
HostAddress Address { get; }
|
||||
IApiClient ApiClient { get; }
|
|
@ -0,0 +1,6 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("GitHub.Exports.Reactive")]
|
||||
[assembly: AssemblyDescription("GitHub interfaces for mef exports with reactive dependencies")]
|
||||
[assembly: Guid("e4ed0537-d1d9-44b6-9212-3096d7c3f7a1")]
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
using System.Reactive;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Octokit;
|
||||
|
||||
namespace GitHub.Services
|
||||
{
|
||||
public interface IHostAvatarProvider
|
||||
{
|
||||
IAvatarProvider Get(string gitHubBaseUrl);
|
||||
}
|
||||
|
||||
public interface IAvatarProvider
|
||||
{
|
||||
BitmapImage DefaultUserBitmapImage { get; }
|
||||
BitmapImage DefaultOrgBitmapImage { get; }
|
||||
IObservable<BitmapSource> GetAvatar(Account apiAccount);
|
||||
IObservable<Unit> InvalidateAvatar(Account apiAccount);
|
||||
IObservable<BitmapSource> GetAvatar(string email);
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ using System.Threading.Tasks;
|
|||
using GitHub.Extensions;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace GitHub.UI
|
||||
namespace GitHub.Validation
|
||||
{
|
||||
public class ReactivePropertyValidationResult
|
||||
{
|
|
@ -0,0 +1,36 @@
|
|||
using System;
|
||||
using System.Reactive;
|
||||
using System.Windows.Input;
|
||||
using GitHub.Models;
|
||||
using GitHub.Validation;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace GitHub.ViewModels
|
||||
{
|
||||
public interface ICreateRepoViewModel : IViewModel
|
||||
{
|
||||
string RepositoryName { get; }
|
||||
string SafeRepositoryName { get; }
|
||||
bool ShowRepositoryNameWarning { get; }
|
||||
string RepositoryNameWarningText { get; }
|
||||
ReactivePropertyValidator<string> RepositoryNameValidator { get; }
|
||||
|
||||
string Description { get; set; }
|
||||
|
||||
ReactiveList<IAccount> Accounts { get; }
|
||||
IAccount SelectedAccount { get; }
|
||||
|
||||
bool KeepPrivate { get; set; }
|
||||
bool CanKeepPrivate { get; }
|
||||
bool ShowUpgradeToMicroPlanWarning { get; }
|
||||
bool ShowUpgradePlanWarning { get; }
|
||||
|
||||
bool IsPublishing { get; }
|
||||
ReactiveCommand<Unit> CreateRepository { get; }
|
||||
ReactiveCommand<Object> UpgradeAccountPlan { get; }
|
||||
ReactiveCommand<Object> Reset { get; }
|
||||
|
||||
ICommand OkCmd { get; }
|
||||
ICommand CancelCmd { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Octokit" version="0.6.2" targetFramework="net45" />
|
||||
<package id="reactiveui" version="6.3.1" targetFramework="net45" />
|
||||
<package id="reactiveui-core" version="6.3.1" targetFramework="net45" />
|
||||
<package id="Rx-Core" version="2.2.5" targetFramework="net45" />
|
||||
<package id="Rx-Interfaces" version="2.2.5" targetFramework="net45" />
|
||||
<package id="Rx-Linq" version="2.2.5" targetFramework="net45" />
|
||||
<package id="Rx-Main" version="2.2.5" targetFramework="net45" />
|
||||
<package id="Rx-PlatformServices" version="2.2.5" targetFramework="net45" />
|
||||
<package id="Rx-XAML" version="2.2.5" targetFramework="net45" />
|
||||
<package id="Splat" version="1.6.0" targetFramework="net45" />
|
||||
</packages>
|
|
@ -1,10 +1,8 @@
|
|||
using GitHub.Services;
|
||||
using Octokit;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.Primitives;
|
||||
using GitHub.Services;
|
||||
using Octokit;
|
||||
|
||||
namespace GitHub.Api
|
||||
{
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GitHub.Api
|
||||
{
|
||||
|
|
|
@ -1,10 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GitHub.Exports
|
||||
namespace GitHub.Authentication
|
||||
{
|
||||
public enum AuthenticationResult
|
||||
{
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
using GitHub.Exports;
|
||||
|
||||
namespace GitHub.Authentication
|
||||
namespace GitHub.Authentication
|
||||
{
|
||||
|
||||
public static class AuthenticationResultExtensions
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using GitHub.UI;
|
||||
using GitHub.ViewModels;
|
||||
|
||||
namespace GitHub.Exports {
|
||||
|
||||
public enum UIViewType {
|
||||
None,
|
||||
Login,
|
||||
TwoFactor,
|
||||
Create,
|
||||
Clone,
|
||||
End = 100
|
||||
}
|
||||
|
||||
[MetadataAttribute]
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
|
||||
public sealed class ExportViewModelAttribute : ExportAttribute
|
||||
{
|
||||
public ExportViewModelAttribute() : base(typeof(IViewModel))
|
||||
{
|
||||
}
|
||||
|
||||
public UIViewType ViewType { get; set; }
|
||||
}
|
||||
|
||||
[MetadataAttribute]
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
|
||||
public sealed class ExportViewAttribute : ExportAttribute
|
||||
{
|
||||
public ExportViewAttribute() : base(typeof(IView))
|
||||
{
|
||||
}
|
||||
|
||||
public UIViewType ViewType { get; set; }
|
||||
}
|
||||
|
||||
public interface IViewModelMetadata
|
||||
{
|
||||
UIViewType ViewType { get; }
|
||||
}
|
||||
}
|
|
@ -42,31 +42,45 @@
|
|||
<Reference Include="Octokit">
|
||||
<HintPath>..\..\packages\Octokit.0.6.2\lib\net45\Octokit.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.Composition" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xaml" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="WindowsBase" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Authentication\AuthenticationResultExtensions.cs" />
|
||||
<Compile Include="Helpers\ImageHelper.cs" />
|
||||
<Compile Include="Models\IProgram.cs" />
|
||||
<Compile Include="Api\ISimpleApiClient.cs" />
|
||||
<Compile Include="Api\ISimpleApiClientFactory.cs" />
|
||||
<Compile Include="Authentication\AuthenticationResult.cs" />
|
||||
<Compile Include="Models\IRepositoryModel.cs" />
|
||||
<Compile Include="Exports\ExportMetadata.cs" />
|
||||
<Compile Include="Primitives\StringEquivalent.cs" />
|
||||
<Compile Include="Primitives\UriString.cs" />
|
||||
<Compile Include="Services\EnterpriseProbeTask.cs" />
|
||||
<Compile Include="Services\ExportFactoryProvider.cs" />
|
||||
<Compile Include="Services\IBrowser.cs" />
|
||||
<Compile Include="Services\IEnterpriseProbeTask.cs" />
|
||||
<Compile Include="Services\IUIProvider.cs" />
|
||||
<Compile Include="Services\IWikiProbe.cs" />
|
||||
<Compile Include="Services\WikiProbe.cs" />
|
||||
<Compile Include="UI\ICloneDialog.cs" />
|
||||
<Compile Include="UI\ILoginDialog.cs" />
|
||||
<Compile Include="UI\ITwoFactorDialog.cs" />
|
||||
<Compile Include="UI\IView.cs" />
|
||||
<Compile Include="ViewModels\ICloneRepoViewModel.cs" />
|
||||
<Compile Include="ViewModels\ILoginViewModel.cs" />
|
||||
<Compile Include="ViewModels\ITwoFactorViewModel.cs" />
|
||||
<Compile Include="Primitives\HostAddress.cs" />
|
||||
<Compile Include="UI\IUIController.cs" />
|
||||
<Compile Include="ViewModels\IViewModel.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="GlobalSuppressions.cs" />
|
||||
|
|
Двоичные данные
src/GitHub.Exports/GlobalSuppressions.cs
Двоичные данные
src/GitHub.Exports/GlobalSuppressions.cs
Двоичный файл не отображается.
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace GitHub.Helpers
|
||||
{
|
||||
public static class ImageHelper
|
||||
{
|
||||
public static BitmapImage CreateBitmapImage(string packUrl)
|
||||
{
|
||||
var bitmap = new BitmapImage(new Uri(packUrl));
|
||||
bitmap.Freeze();
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
using GitHub.Primitives;
|
||||
|
||||
namespace GitHub.Models
|
||||
{
|
||||
public interface IRepositoryModel
|
||||
{
|
||||
HostAddress HostAddress { get; }
|
||||
int? Id { get; }
|
||||
string Owner { get; set; }
|
||||
string Name { get; }
|
||||
string NameWithOwner { get; }
|
||||
string Description { get; }
|
||||
Uri HostUri { get; }
|
||||
bool IsPrivate { get; }
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
using System.Globalization;
|
||||
using GitHub.Extensions;
|
||||
|
||||
namespace GitHub
|
||||
namespace GitHub.Primitives
|
||||
{
|
||||
public class HostAddress
|
||||
{
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Xml;
|
||||
using System.Xml.Schema;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace GitHub.Primitives
|
||||
{
|
||||
[Serializable]
|
||||
public abstract class StringEquivalent<T> : ISerializable, IXmlSerializable where T : StringEquivalent<T>
|
||||
{
|
||||
protected string Value;
|
||||
|
||||
protected StringEquivalent(string value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
protected StringEquivalent()
|
||||
{
|
||||
}
|
||||
|
||||
public abstract T Combine(string addition);
|
||||
|
||||
[SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates", Justification = "Add doesn't make sense in the case of a string equivalent")]
|
||||
public static T operator +(StringEquivalent<T> a, string b)
|
||||
{
|
||||
return a.Combine(b);
|
||||
}
|
||||
|
||||
public static bool operator ==(StringEquivalent<T> a, StringEquivalent<T> b)
|
||||
{
|
||||
// If both are null, or both are same instance, return true.
|
||||
if (ReferenceEquals(a, b))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If one is null, but not both, return false.
|
||||
if (((object)a == null) || ((object)b == null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return true if the fields match:
|
||||
return a.Value.Equals(b.Value, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static bool operator !=(StringEquivalent<T> a, StringEquivalent<T> b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
public override bool Equals(Object obj)
|
||||
{
|
||||
return obj != null && Equals(obj as T) || Equals(obj as string);
|
||||
}
|
||||
|
||||
public bool Equals(T stringEquivalent)
|
||||
{
|
||||
return this == stringEquivalent;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return (Value ?? "").GetHashCode();
|
||||
}
|
||||
|
||||
public virtual bool Equals(string other)
|
||||
{
|
||||
return other != null && Value == other;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
|
||||
protected StringEquivalent(SerializationInfo info) : this(info.GetValue("Value", typeof(string)) as string)
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
info.AddValue("Value", Value);
|
||||
}
|
||||
|
||||
public XmlSchema GetSchema()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public void ReadXml(XmlReader reader)
|
||||
{
|
||||
Value = reader.ReadString();
|
||||
}
|
||||
|
||||
public void WriteXml(XmlWriter writer)
|
||||
{
|
||||
writer.WriteString(Value);
|
||||
}
|
||||
|
||||
public int Length
|
||||
{
|
||||
get { return Value != null ? Value.Length : 0; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,275 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text.RegularExpressions;
|
||||
using GitHub.Extensions;
|
||||
|
||||
namespace GitHub.Primitives
|
||||
{
|
||||
/// <summary>
|
||||
/// This class represents a URI given to us as a string and is implicitly
|
||||
/// convertible to and from string.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This typically represents a URI from an external source such as user input, a
|
||||
/// Git Repo Remote, or an API URL. We try to preserve the original form and let
|
||||
/// downstream clients validate the URL. This class doesn't validate the URL. It just
|
||||
/// performs a best-effort to parse the URI into bits important to us. For example,
|
||||
/// we need to know the HOST so we can compare against GitHub.com, GH:E instances, etc.
|
||||
/// </remarks>
|
||||
[SuppressMessage("Microsoft.Usage", "CA2240:ImplementISerializableCorrectly", Justification = "GetObjectData is implemented in the base class")]
|
||||
[Serializable]
|
||||
public class UriString : StringEquivalent<UriString>, IEquatable<UriString>
|
||||
{
|
||||
//static readonly NLog.Logger log = NLog.LogManager.GetCurrentClassLogger();
|
||||
static readonly Regex sshRegex = new Regex(@"^.+@(?<host>(\[.*?\]|[a-z0-9-.]+?))(:(?<owner>.*?))?(/(?<repo>.*)(\.git)?)?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
readonly Uri url;
|
||||
|
||||
public UriString(string uriString) : base(NormalizePath(uriString))
|
||||
{
|
||||
if (uriString.Length == 0) return;
|
||||
if (Uri.TryCreate(uriString, UriKind.Absolute, out url))
|
||||
{
|
||||
if (!url.IsFile)
|
||||
SetUri(url);
|
||||
else
|
||||
SetFilePath(url);
|
||||
}
|
||||
else if (!ParseScpSyntax(uriString))
|
||||
{
|
||||
SetFilePath(uriString);
|
||||
}
|
||||
|
||||
if (RepositoryName != null)
|
||||
{
|
||||
NameWithOwner = Owner != null
|
||||
? string.Format(CultureInfo.InvariantCulture, "{0}/{1}", Owner, RepositoryName)
|
||||
: RepositoryName;
|
||||
}
|
||||
}
|
||||
|
||||
static UriString ToUriString(Uri uri)
|
||||
{
|
||||
return uri == null ? null : new UriString(uri.ToString());
|
||||
}
|
||||
|
||||
void SetUri(Uri uri)
|
||||
{
|
||||
Host = uri.Host;
|
||||
if (uri.Segments.Any())
|
||||
{
|
||||
RepositoryName = GetRepositoryName(uri.Segments.Last());
|
||||
}
|
||||
|
||||
if (uri.Segments.Length > 2)
|
||||
{
|
||||
Owner = (uri.Segments[uri.Segments.Length - 2] ?? "").TrimEnd('/').ToNullIfEmpty();
|
||||
}
|
||||
|
||||
IsHypertextTransferProtocol = uri.IsHypertextTransferProtocol();
|
||||
|
||||
if (String.IsNullOrEmpty(uri.Query)) return;
|
||||
|
||||
try
|
||||
{
|
||||
var query = ParseQueryString(uri);
|
||||
|
||||
if (query.ContainsKey("branch") && !String.IsNullOrEmpty(query["branch"]))
|
||||
{
|
||||
Branch = query["branch"].Replace("%2F", "/");
|
||||
}
|
||||
|
||||
if (query.ContainsKey("pr") && !String.IsNullOrEmpty(query["pr"]))
|
||||
{
|
||||
PullRequest = query["pr"].Replace("%2F", "/");
|
||||
}
|
||||
|
||||
if (query.ContainsKey("filepath") && !String.IsNullOrEmpty(query["filepath"]))
|
||||
{
|
||||
RelativePathToOpen = query["filepath"].Replace("%2F", "/").Replace('/', '\\');
|
||||
}
|
||||
}
|
||||
catch //(Exception ex)
|
||||
{
|
||||
//log.WarnException("Failed to read URI query", ex);
|
||||
}
|
||||
}
|
||||
|
||||
// This is not a complete query string parsing algorithm, but it's good enough for our needs.
|
||||
static IDictionary<string, string> ParseQueryString(Uri uri)
|
||||
{
|
||||
Debug.Assert(uri.Query.StartsWith('?'),
|
||||
String.Format(CultureInfo.InvariantCulture, "Uri.Query doesn't start with '?': '{0}'", uri.Query));
|
||||
return uri.Query.Substring(1).Split(new[] {'&'}, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(pair => pair.Split('='))
|
||||
.ToDictionary(pair => pair.First(), pair => pair.Length > 1 ? pair[1] : null,
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
void SetFilePath(Uri uri)
|
||||
{
|
||||
Host = "";
|
||||
Owner = "";
|
||||
RepositoryName = GetRepositoryName(uri.Segments.Last());
|
||||
IsFileUri = true;
|
||||
}
|
||||
|
||||
void SetFilePath(string path)
|
||||
{
|
||||
Host = "";
|
||||
Owner = "";
|
||||
RepositoryName = GetRepositoryName(path.Replace("/", @"\").RightAfterLast(@"\"));
|
||||
IsFileUri = true;
|
||||
}
|
||||
|
||||
// For xml serialization
|
||||
protected UriString()
|
||||
{
|
||||
}
|
||||
|
||||
bool ParseScpSyntax(string scpString)
|
||||
{
|
||||
var match = sshRegex.Match(scpString);
|
||||
if (match.Success)
|
||||
{
|
||||
Host = match.Groups["host"].Value.ToNullIfEmpty();
|
||||
Owner = match.Groups["owner"].Value.ToNullIfEmpty();
|
||||
RepositoryName = GetRepositoryName(match.Groups["repo"].Value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public string Branch { get; private set; }
|
||||
public string PullRequest { get; set; }
|
||||
public string Host { get; private set; }
|
||||
|
||||
public string Owner { get; private set; }
|
||||
|
||||
public string RepositoryName { get; private set; }
|
||||
|
||||
public string NameWithOwner { get; private set; }
|
||||
|
||||
public string RelativePathToOpen { get; private set; }
|
||||
|
||||
public bool IsFileUri { get; private set; }
|
||||
|
||||
public bool IsValidUri
|
||||
{
|
||||
get { return url != null; }
|
||||
}
|
||||
|
||||
public Uri ToUri()
|
||||
{
|
||||
if (url == null)
|
||||
throw new InvalidOperationException("This Uri String is not a valid Uri");
|
||||
return url;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if the URL is HTTP or HTTPS
|
||||
/// </summary>
|
||||
public bool IsHypertextTransferProtocol { get; private set; }
|
||||
|
||||
[SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates")]
|
||||
public static implicit operator UriString(string value)
|
||||
{
|
||||
if (value == null) return null;
|
||||
|
||||
return new UriString(value);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates")]
|
||||
public static implicit operator string(UriString uriString)
|
||||
{
|
||||
if (uriString == null) return null;
|
||||
|
||||
return uriString.Value;
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "No.")]
|
||||
public override UriString Combine(string addition)
|
||||
{
|
||||
if (url != null)
|
||||
{
|
||||
var urlBuilder = new UriBuilder(url);
|
||||
if (!String.IsNullOrEmpty(urlBuilder.Query))
|
||||
{
|
||||
var query = urlBuilder.Query;
|
||||
if (query.StartsWith("?", StringComparison.Ordinal))
|
||||
{
|
||||
query = query.Substring(1);
|
||||
}
|
||||
|
||||
if (!addition.StartsWith("&", StringComparison.Ordinal) && query.Length > 0)
|
||||
{
|
||||
addition = "&" + addition;
|
||||
}
|
||||
urlBuilder.Query = query + addition;
|
||||
}
|
||||
else
|
||||
{
|
||||
var path = url.AbsolutePath;
|
||||
if (path == "/") path = "";
|
||||
if (!addition.StartsWith("/", StringComparison.Ordinal)) addition = "/" + addition;
|
||||
|
||||
urlBuilder.Path = path + addition;
|
||||
}
|
||||
return ToUriString(urlBuilder.Uri);
|
||||
}
|
||||
return String.Concat(Value, addition);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
// Makes this look better in the debugger.
|
||||
return Value;
|
||||
}
|
||||
|
||||
protected UriString(SerializationInfo info, StreamingContext context)
|
||||
: this(GetSerializedValue(info))
|
||||
{
|
||||
}
|
||||
|
||||
static string GetSerializedValue(SerializationInfo info)
|
||||
{
|
||||
// First try to get the current way it's serialized, then fall back to the older way it's serialized.
|
||||
string value;
|
||||
try
|
||||
{
|
||||
value = info.GetValue("Value", typeof(string)) as string;
|
||||
}
|
||||
catch (SerializationException)
|
||||
{
|
||||
value = info.GetValue("uriString", typeof(string)) as string;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static string NormalizePath(string path)
|
||||
{
|
||||
return path == null ? null : path.Replace('\\', '/');
|
||||
}
|
||||
|
||||
static string GetRepositoryName(string repositoryNameSegment)
|
||||
{
|
||||
if (String.IsNullOrEmpty(repositoryNameSegment)
|
||||
|| repositoryNameSegment.Equals("/", StringComparison.Ordinal))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return repositoryNameSegment.TrimEnd('/').TrimEnd(".git");
|
||||
}
|
||||
|
||||
bool IEquatable<UriString>.Equals(UriString other)
|
||||
{
|
||||
return other != null && ToString().Equals(other.ToString());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,13 +1,13 @@
|
|||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using GitHub.Extensions;
|
||||
using GitHub.Models;
|
||||
using Octokit;
|
||||
using Octokit.Internal;
|
||||
using System.Threading.Tasks;
|
||||
using System.Net.Http;
|
||||
using GitHub.Extensions;
|
||||
using System.Threading;
|
||||
using System.Net;
|
||||
|
||||
namespace GitHub.Services
|
||||
{
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using GitHub.Exports;
|
||||
using GitHub.UI;
|
||||
using GitHub.ViewModels;
|
||||
|
||||
namespace GitHub.Services
|
||||
{
|
||||
[Export]
|
||||
[PartCreationPolicy(CreationPolicy.Shared)]
|
||||
public class ExportFactoryProvider
|
||||
{
|
||||
[ImportingConstructor]
|
||||
public ExportFactoryProvider(ICompositionService cc)
|
||||
{
|
||||
cc.SatisfyImportsOnce(this);
|
||||
}
|
||||
|
||||
[Import(AllowRecomposition = true)]
|
||||
public ExportFactory<IUIController> UIControllerFactory { get; set; }
|
||||
|
||||
[ImportMany(AllowRecomposition = true)]
|
||||
public IEnumerable<ExportFactory<IViewModel, IViewModelMetadata>> ViewModelFactory { get; set; }
|
||||
|
||||
[ImportMany(AllowRecomposition = true)]
|
||||
public IEnumerable<ExportFactory<IView, IViewModelMetadata>> ViewFactory { get; set; }
|
||||
|
||||
public ExportLifetimeContext<IViewModel> GetViewModel(UIViewType viewType)
|
||||
{
|
||||
var f = ViewModelFactory.FirstOrDefault(x => x.Metadata.ViewType == viewType);
|
||||
Debug.Assert(f != null, string.Format(CultureInfo.InvariantCulture, "Could not locate view model for {0}.", viewType));
|
||||
return f.CreateExport();
|
||||
}
|
||||
|
||||
public ExportLifetimeContext<IView> GetView(UIViewType viewType)
|
||||
{
|
||||
var f = ViewFactory.FirstOrDefault(x => x.Metadata.ViewType == viewType);
|
||||
Debug.Assert(f != null, string.Format(CultureInfo.InvariantCulture, "Could not locate view for {0}.", viewType));
|
||||
return f.CreateExport();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
using Octokit;
|
||||
using System;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GitHub.Services
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
using System.ComponentModel.Composition.Hosting;
|
||||
|
||||
namespace GitHub.Services
|
||||
{
|
||||
public interface IUIProvider
|
||||
{
|
||||
ExportProvider ExportProvider { get; }
|
||||
object GetService(Type t);
|
||||
T GetService<T>();
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
|
||||
Ret GetService<T, Ret>() where Ret : class;
|
||||
}
|
||||
}
|
|
@ -1,9 +1,5 @@
|
|||
using Octokit;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using Octokit;
|
||||
|
||||
namespace GitHub.Services
|
||||
{
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace GitHub.Services
|
|||
|
||||
var ret = await httpClient
|
||||
.Send<object>(request, CancellationToken.None)
|
||||
.Catch(ex => null);
|
||||
.Catch();
|
||||
|
||||
if (ret == null)
|
||||
return WikiProbeResult.Failed;
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GitHub.Exports
|
||||
{
|
||||
public interface ICloneDialog
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace GitHub.UI
|
||||
{
|
||||
public interface IUIController
|
||||
{
|
||||
//IObservable<object> Transition { get; }
|
||||
IObservable<UserControl> SelectFlow(UIControllerFlow choice);
|
||||
void Start();
|
||||
}
|
||||
|
||||
public enum UIControllerFlow
|
||||
{
|
||||
None = 0,
|
||||
Authentication = 1,
|
||||
Create = 2,
|
||||
Clone = 3
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
namespace GitHub.UI
|
||||
{
|
||||
public interface IView
|
||||
{
|
||||
object ViewModel { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace GitHub.ViewModels
|
||||
{
|
||||
public interface ICloneRepoViewModel : IViewModel
|
||||
{
|
||||
ICommand OkCmd { get; }
|
||||
ICommand CancelCmd { get; }
|
||||
IObservable<object> Cancelling { get; }
|
||||
}
|
||||
}
|
|
@ -1,15 +1,16 @@
|
|||
using System;
|
||||
using System.Windows.Input;
|
||||
using GitHub.Authentication;
|
||||
|
||||
namespace GitHub.Exports
|
||||
namespace GitHub.ViewModels
|
||||
{
|
||||
public interface ILoginDialog
|
||||
public interface ILoginViewModel : IViewModel
|
||||
{
|
||||
string UsernameOrEmail { get; set; }
|
||||
string Password { get; set; }
|
||||
ICommand LoginCmd { get; }
|
||||
ICommand CancelCmd { get; }
|
||||
IObservable<object> CancelEvt { get; }
|
||||
IObservable<object> Cancelling { get; }
|
||||
IObservable<AuthenticationResult> AuthenticationResults { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
using System.Windows.Input;
|
||||
|
||||
namespace GitHub.ViewModels
|
||||
{
|
||||
public interface ITwoFactorViewModel : IViewModel
|
||||
{
|
||||
ICommand OkCmd { get; }
|
||||
ICommand CancelCmd { get; }
|
||||
ICommand ShowHelpCmd { get; }
|
||||
ICommand ResendCodeCmd { get; }
|
||||
|
||||
bool IsShowing { get; }
|
||||
bool IsSms { get; }
|
||||
bool IsAuthenticationCodeSent { get; }
|
||||
string Description { get; }
|
||||
string AuthenticationCode { get; set; }
|
||||
}
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче