new startup process UI for Windows

This commit is contained in:
Grant Limberg 2017-09-22 15:33:09 -07:00
parent eb42ef68ee
commit c666f92e35
29 changed files with 1443 additions and 14 deletions

View File

@ -26,6 +26,8 @@ namespace WinUI
public delegate void NetworkListCallback(List<ZeroTierNetwork> networks);
public delegate void StatusCallback(ZeroTierStatus status);
private string ZeroTierAddress = "";
public static APIHandler Instance
{
get
@ -170,6 +172,11 @@ namespace WinUI
try
{
status = JsonConvert.DeserializeObject<ZeroTierStatus>(responseText);
if (ZeroTierAddress != status.Address)
{
ZeroTierAddress = status.Address;
}
}
catch (JsonReaderException e)
{
@ -442,5 +449,10 @@ namespace WinUI
}
}
}
public string NodeAddress()
{
return ZeroTierAddress;
}
}
}

245
windows/WinUI/CentralAPI.cs Normal file
View File

@ -0,0 +1,245 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace WinUI
{
class CentralAPI
{
private static volatile CentralAPI instance;
private static object syncRoot = new Object();
private CookieContainer cookieContainer;
private HttpClientHandler clientHandler;
private HttpClient client;
private CentralServer server;
public CentralServer Central
{
get
{
return this.server;
}
set
{
this.server = value;
WriteCentralConfig();
UpdateRequestHeaders();
}
}
public static CentralAPI Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
{
instance = new CentralAPI();
}
}
}
return instance;
}
}
private CentralAPI()
{
#if DEBUG
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
#endif
cookieContainer = new CookieContainer();
clientHandler = new HttpClientHandler
{
AllowAutoRedirect = true,
UseCookies = true,
CookieContainer = cookieContainer
};
client = new HttpClient(clientHandler);
string centralConfigPath = CentralConfigFile();
if (File.Exists(centralConfigPath))
{
byte[] tmp = File.ReadAllBytes(centralConfigPath);
string json = Encoding.UTF8.GetString(tmp).Trim();
Central = JsonConvert.DeserializeObject<CentralServer>(json);
}
else
{
Central = new CentralServer();
}
}
public bool HasAccessToken()
{
if (Central == null)
return false;
return !string.IsNullOrEmpty(Central.APIKey);
}
private string ZeroTierDir()
{
return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\ZeroTier\\One";
}
private string CentralConfigFile()
{
return ZeroTierDir() + "\\central.conf";
}
public void WriteCentralConfig()
{
string json = JsonConvert.SerializeObject(Central);
byte[] tmp = Encoding.UTF8.GetBytes(json);
File.WriteAllBytes(CentralConfigFile(), tmp);
}
private void UpdateRequestHeaders()
{
if (client.DefaultRequestHeaders.Contains("Authorization"))
{
client.DefaultRequestHeaders.Remove("Authorization");
}
if (!string.IsNullOrEmpty(Central.APIKey))
{
client.DefaultRequestHeaders.Add("Authorization", "bearer " + Central.APIKey);
}
}
public async Task<bool> Login(string email, string password, bool isNewUser)
{
string postURL = Central.ServerURL + "/api/_auth/local";
CentralLogin login = new CentralLogin(email, password, isNewUser);
var content = new StringContent(JsonConvert.SerializeObject(login), Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.PostAsync(postURL, content);
if (!response.IsSuccessStatusCode)
{
return false;
}
string resContent = await response.Content.ReadAsStringAsync();
CentralUser user = JsonConvert.DeserializeObject<CentralUser>(resContent);
if (user.Tokens.Count == 0)
{
// create token
user = await CreateAuthToken(user);
}
Central.APIKey = user.Tokens[0];
UpdateRequestHeaders();
WriteCentralConfig();
return true;
}
public async Task<CentralUser> CreateAuthToken(CentralUser user)
{
string randomTokenURL = Central.ServerURL + "/api/randomToken";
HttpResponseMessage response = await client.GetAsync(randomTokenURL);
if (!response.IsSuccessStatusCode)
{
// TODO: throw an error
return null;
}
string resContent = await response.Content.ReadAsStringAsync();
CentralToken t = JsonConvert.DeserializeObject<CentralToken>(resContent);
user.Tokens.Add(t.Token);
string tokenObj = "{ \"tokens\": " + JsonConvert.SerializeObject(user.Tokens) + " } ";
string postURL = Central.ServerURL + "/api/user/" + user.Id;
var postContent = new StringContent(tokenObj, Encoding.UTF8, "application/json");
response = await client.PostAsync(postURL, postContent);
if (!response.IsSuccessStatusCode)
{
// TODO: thrown an error
return null;
}
resContent = await response.Content.ReadAsStringAsync();
user = JsonConvert.DeserializeObject<CentralUser>(resContent);
return user;
}
public async Task<List<CentralNetwork>> GetNetworkList()
{
string networkURL = Central.ServerURL + "/api/network";
HttpResponseMessage response = await client.GetAsync(networkURL);
if (!response.IsSuccessStatusCode)
{
// TODO: Throw Error
return new List<CentralNetwork>();
}
string resContent = await response.Content.ReadAsStringAsync();
List<CentralNetwork> networkList = JsonConvert.DeserializeObject<List<CentralNetwork>>(resContent);
return networkList;
}
public async Task<CentralNetwork> CreateNewNetwork()
{
string networkURL = Central.ServerURL + "/api/network/";
CentralNetwork network = new CentralNetwork();
network.Config = new CentralNetwork.CentralNetworkConfig();
network.Config.Name = NetworkNameGenerator.GenerateName();
string jsonNetwork = JsonConvert.SerializeObject(network);
var postContent = new StringContent(jsonNetwork, Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.PostAsync(networkURL, postContent);
if (!response.IsSuccessStatusCode)
{
return null;
}
string resContent = await response.Content.ReadAsStringAsync();
CentralNetwork newNetwork = JsonConvert.DeserializeObject<CentralNetwork>(resContent);
return newNetwork;
}
public async Task<bool> AuthorizeNode(string nodeAddress, string networkId)
{
string json = "{ \"config\": { \"authorized\": true } }";
string postURL = Central.ServerURL + "/api/network/" + networkId + "/member/" + nodeAddress;
var postContent = new StringContent(json, Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.PostAsync(postURL, postContent);
if (response.IsSuccessStatusCode)
{
return true;
}
return false;
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace WinUI
{
class CentralLogin
{
public CentralLogin(string email, string password, bool isNew)
{
Login = email;
Password = password;
IsNew = isNew;
}
[JsonProperty("login")]
public string Login { get; set; }
[JsonProperty("password")]
public string Password { get; set; }
[JsonProperty("register")]
public bool IsNew { get; set; }
}
}

View File

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace WinUI
{
class CentralNetwork
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("clock")]
public UInt64 Clock { get; set; }
[JsonProperty("rulesSource")]
public string RulesSource { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("ownerId")]
public string OwnerID { get; set; }
[JsonProperty("onlineMemberCount")]
public int OnlineMemberCount { get; set; }
[JsonProperty("config")]
public CentralNetworkConfig Config { get; set; }
public class CentralNetworkConfig
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("nwid")]
public string NetworkID { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace WinUI
{
class CentralServer
{
public CentralServer()
{
ServerURL = "https://my.zerotier.com";
}
[JsonProperty("server_url")]
public string ServerURL { get; set; }
[JsonProperty("api_key")]
public string APIKey { get; set; }
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace WinUI
{
class CentralToken
{
[JsonProperty("token")]
public string Token { get; set; }
[JsonProperty("clock")]
public UInt64 Clock { get; set; }
[JsonProperty("raw")]
public string Raw { get; set; }
}
}

View File

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace WinUI
{
class CentralUser
{
public class CentralGlobalPermissions
{
[JsonProperty("a")]
public bool Administrator { get; set; }
[JsonProperty("d")]
public bool Delete { get; set; }
[JsonProperty("m")]
public bool Modify { get; set; }
[JsonProperty("r")]
public bool Read { get; set; }
}
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("clock")]
public UInt64 Clock { get; set; }
[JsonProperty("globalPermissions")]
public CentralGlobalPermissions GlobalPermissions { get; set; }
[JsonProperty("displayName")]
public string DisplayName { get; set; }
[JsonProperty("email")]
public string Email { get; set; }
[JsonProperty("smsNumber")]
public string SmsNumber { get; set; }
[JsonProperty("tokens")]
public List<string> Tokens { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WinUI
{
interface ISwitchable
{
void UtilizeState(object state);
}
}

View File

@ -0,0 +1,201 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WinUI
{
class NetworkNameGenerator
{
public static string GenerateName()
{
Random r = new Random(DateTime.Now.Millisecond);
int firstIndex = r.Next(0, FIRST.Length);
int secondIndex = r.Next(0, SECOND.Length);
return FIRST[firstIndex] + "_" + SECOND[secondIndex];
}
private static string[] FIRST =
{
"admiring",
"adoring",
"agitated",
"amazing",
"angry",
"awesome",
"berserk",
"big",
"clever",
"compassionate",
"cranky",
"crazy",
"desperate",
"determined",
"distracted",
"dreamy",
"ecstatic",
"elated",
"elegant",
"fervent",
"focused",
"furious",
"gigantic",
"gloomy",
"goofy",
"grave",
"happy",
"high",
"hopeful",
"hungry",
"insane",
"jolly",
"jovial",
"lonely",
"loving",
"modest",
"nostalgic",
"pedantic",
"pensive",
"prickly",
"reverent",
"romantic",
"sad",
"serene",
"sharp",
"silly",
"sleepy",
"stoic",
"stupefied",
"suspicious",
"tender",
"thirsty",
"tiny",
"trusting"
};
private static string[] SECOND =
{
// constructed telephone-like devices in 1854
"meucci",
// prototype make-or-break telephones in 1860
"reis",
// Alexander Graham Bell
"bell",
// designed telephone using water microphone in 1876
"gray",
// Tivadar Puskás invented the telephone switchboard exchange in 1876.
"puskas",
// Thomas Edison, invented the carbon microphone which produced a strong telephone signal.
"edison",
// 1950s, Paul Baran developed the concept Distributed Adaptive Message Block Switching
"baran",
// Donald Davies coined the phrase 'packet switching network'
"davies",
// Robert Licklider helped get ARPANET funded
"licklider",
// Robert Taylor, ARPANET pioneer
"taylor",
// Lawrence Roberts, ARPANET
"roberts",
// Vint Cerf, TCP
"cerf",
// Bob Kahn, TCP
"kahn",
// David P Reed, UDP
"reed",
// Community Memory was created by Efrem Lipkin, Mark Szpakowski, and Lee Felsenstein, acting as The Community Memory Project within the Resource One computer center at Project One in San Francisco.
"lipkin",
"szpakowski",
"felsenstein",
// The first public dial-up BBS was developed by Ward Christensen and Randy Suess.
"christensen",
"suess",
// Joybubbles (May 25, 1949 August 8, 2007), born Josef Carl Engressia, Jr. in Richmond, Virginia, USA, was an early phone phreak.
"engressia",
"joybubbles",
// John Thomas Draper (born 1943), also known as Captain Crunch, Crunch or Crunchman (after Cap'n Crunch breakfast cereal mascot), is an American computer programmer and former phone phreak
"draper",
// Dennis C. Hayes, founder of Hayes Microcomputer Products
// "The Modem of Dennis Hayes and Dale Heatherington."
"hayes",
"heatherington",
// "Ethernet was developed at Xerox PARC between 1973 and 1974.[7][8] It was inspired by ALOHAnet, which Robert Metcalfe had studied as part of his PhD dissertation."
"metcalfe",
// William Bradford Shockley Jr. (February 13, 1910 August 12, 1989) was an American physicist and inventor. Shockley was the manager of a research group that included John Bardeen and Walter Brattain. The three scientists invented the point contact transistor in 1947
"shockley",
"bardeen",
"brattain",
// "Randall Erck invented the modern modem as we know it today. There were devices similar to modems used by the military, but they were designed more for the purpose of sending encripted nuclear launch codes to various bases around the world."
"erck",
// Leonard Kleinrock, packet switching network pioneer
"kleinrock",
// Tim Berners-Lee, WWW
"berners_lee",
// Steve Wozniak, early phone phreak
"wozniak",
// James Fields Smathers of Kansas City invented what is considered the first practical power-operated typewriter in 1914.
"smathers",
// The teleprinter evolved through a series of inventions by a number of engineers, including Royal Earl House, David Edward Hughes, Emile Baudot, Donald Murray, Charles L. Krum, Edward Kleinschmidt and Frederick G. Creed.
"house",
"hughes",
"baudot",
"murray",
"krum",
"kleinschmidt",
"creed",
// Ron Rosenbaum, author of "Secrets of the Little Blue Box" which mainstreamed phone phreaking
"rosenbaum",
// Bram Cohen. Bram Cohen (born October 12, 1975) is an American computer programmer, best known as the author of the peer-to-peer (P2P) BitTorrent protocol,
"cohen",
// Jarkko Oikarinen (born 16 August 1967, in Kuusamo, Finland) is the inventor of the first Internet chat network, called Internet Relay Chat (IRC), where he is known as WiZ.
"oikarinen",
// "What you probably didn't know is that the author of Trumpet Winsock — Peter Tattam from Tasmania, Australia — didn't see much money for his efforts."
"tattam",
// Satoshi Nakamoto
"nakamoto",
// Philo Farnsworth, inventor of the first practical TV tube
"farnsworth",
// Scottish inventor John Logie Baird employed the Nipkow disk in his prototype video systems. On 25 March 1925, Baird gave the first public demonstration of televised silhouette images in motion, at Selfridge's Department Store in London.
"baird",
// Beginning in 1836, the American artist Samuel F. B. Morse, the American physicist Joseph Henry, and Alfred Vail developed an electrical telegraph system.
"morse",
"henry",
"vail"
};
}
}

View File

@ -0,0 +1,38 @@
<UserControl x:Class="WinUI.OnboardProcess.CreateAccount"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WinUI.OnboardProcess"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Grid.Row="0" HorizontalAlignment="Right" Margin="10">Email Address:</Label>
<TextBox x:Name="EmailAddressTextBox" Grid.Column="1" Grid.Row="0" Width="150" Margin="10"></TextBox>
<Label Grid.Column="0" Grid.Row="1" HorizontalAlignment="Right" Margin="10">Password:</Label>
<PasswordBox x:Name="PasswordTextBox1" Grid.Column="1" Grid.Row="1" PasswordChar="*" Margin="10"/>
<Label Grid.Column="0" Grid.Row="2" HorizontalAlignment="Right" Margin="10">Repeat Password:</Label>
<PasswordBox x:Name="PasswordTextBox2" Grid.Column="1" Grid.Row="2" PasswordChar="*" Margin="10"/>
<Button Grid.Column="1" Grid.Row="3" Click="CreateAccount_Click" Margin="10" Content="Create Account" Background="#FFFFB354" Width="90" HorizontalAlignment="Right"/>
<Label x:Name="ErrorText" Grid.Row="4" Grid.ColumnSpan="2" HorizontalAlignment="Center"></Label>
<Label Grid.Row="5"></Label>
<Button Grid.Column="0" Grid.Row="6" Background="#FFFFB354" Click="BackButton_Click">Go Back</Button>
</Grid>
</UserControl>

View File

@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WinUI.OnboardProcess
{
/// <summary>
/// Interaction logic for CreateAccount.xaml
/// </summary>
public partial class CreateAccount : UserControl, ISwitchable
{
public CreateAccount()
{
InitializeComponent();
}
public void UtilizeState(object state)
{
throw new NotImplementedException();
}
public void CreateAccount_Click(object sender, RoutedEventArgs e)
{
DoCreateAccount();
}
public void BackButton_Click(object sender, RoutedEventArgs e)
{
Switcher.Switch(new RegisterOrLogIn());
}
public async void DoCreateAccount()
{
if (PasswordTextBox1.Password.ToString() != PasswordTextBox2.Password.ToString())
{
ErrorText.Content = "Passwords do not match!";
}
else
{
CentralAPI api = CentralAPI.Instance;
bool accountCreated = await api.Login(EmailAddressTextBox.Text,
PasswordTextBox1.Password.ToString(), true);
if (accountCreated)
{
Switcher.Switch(new CreateOrJoin());
}
else
{
ErrorText.Content = "An error ocurred while creating your account.";
}
}
}
}
}

View File

@ -0,0 +1,49 @@
<UserControl x:Class="WinUI.OnboardProcess.CreateOrJoin"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WinUI.OnboardProcess"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid HorizontalAlignment="Stretch" Margin="15">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="1" x:Name="CreateButton" Content="Create a Network" Background="#FFFFB354" Click="OnCreateButtonClick"/>
<Label Grid.Column="1" Grid.Row="1" Content="Or" HorizontalAlignment="Center"/>
<Label Grid.Column="1" Grid.Row="2" Content="Join a Network:" HorizontalAlignment="Center"/>
<ListBox Grid.ColumnSpan="3" Grid.Column="0" Grid.Row="3" Name="listViewDataBinding" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Margin="5" HorizontalAlignment="Left" Text="{Binding Id}"/>
<TextBlock Grid.Column="1" Grid.Row="0" Margin="5" HorizontalAlignment="Center" Text="{Binding Config.Name}"/>
<Button Grid.Column="2" Grid.Row="0" Margin="5" Content="Join" HorizontalAlignment="Right" Background="#FFFFB354" Tag="{Binding Id}" Click="OnJoinButtonClick"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>

View File

@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WinUI.OnboardProcess
{
/// <summary>
/// Interaction logic for CreateOrJoin.xaml
/// </summary>
public partial class CreateOrJoin : UserControl, ISwitchable
{
private List<CentralNetwork> networkList = new List<CentralNetwork>();
public CreateOrJoin()
{
InitializeComponent();
listViewDataBinding.ItemsSource = networkList;
GetAvailableNetworks();
}
public void UtilizeState(object state)
{
throw new NotImplementedException();
}
private async void GetAvailableNetworks()
{
CentralAPI api = CentralAPI.Instance;
List<CentralNetwork> networks = await api.GetNetworkList();
foreach (CentralNetwork n in networks)
{
networkList.Add(n);
}
listViewDataBinding.Items.Refresh();
}
public void OnJoinButtonClick(object sender, RoutedEventArgs e)
{
Button button = sender as Button;
string networkId = button.Tag as string;
APIHandler handler = APIHandler.Instance;
handler.JoinNetwork(this.Dispatcher, networkId);
AuthorizeNetworkMember(networkId);
}
public void OnCreateButtonClick(object sender, RoutedEventArgs e)
{
CreateNewNetwork();
}
private async void CreateNewNetwork()
{
CentralAPI api = CentralAPI.Instance;
CentralNetwork newNetwork = await api.CreateNewNetwork();
APIHandler handler = APIHandler.Instance;
handler.JoinNetwork(this.Dispatcher, newNetwork.Id);
AuthorizeNetworkMember(newNetwork.Id);
}
private async void AuthorizeNetworkMember(string networkId)
{
string nodeId = APIHandler.Instance.NodeAddress();
bool authorized = await CentralAPI.Instance.AuthorizeNode(nodeId, networkId);
if (authorized)
{
Switcher.Switch(new Finished());
}
else
{
}
}
}
}

View File

@ -0,0 +1,29 @@
<UserControl x:Class="WinUI.OnboardProcess.EnterToken"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WinUI.OnboardProcess"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Margin="10" TextWrapping="Wrap" HorizontalAlignment="Center" Text="Your API Token can be found or created on https://my.zerotier.com"/>
<TextBlock Grid.Column="0" Grid.Row="1" Text="API Token:" Margin="10"/>
<TextBox x:Name="APITokenInput" Grid.Column="1" Grid.Row="1" Margin="10"/>
<Button Grid.Column="1" Grid.Row="2" Background="#FFFFB354" Content="Next" HorizontalAlignment="Right" Click="Next_Click" Margin="10"/>
<Label Grid.Row="3"/>
<Button Grid.Column="0" Grid.Row="4" Background="#FFFFB354" Content="Go Back" Click="BackButton_Click" />
</Grid>
</UserControl>

View File

@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WinUI.OnboardProcess
{
/// <summary>
/// Interaction logic for EnterToken.xaml
/// </summary>
public partial class EnterToken : UserControl, ISwitchable
{
public EnterToken()
{
InitializeComponent();
if (!string.IsNullOrEmpty(CentralAPI.Instance.Central.APIKey))
{
APITokenInput.Text = CentralAPI.Instance.Central.APIKey;
}
}
public void UtilizeState(object staqte)
{
}
private void Next_Click(object sender, RoutedEventArgs e)
{
CentralAPI api = CentralAPI.Instance;
if (api.Central.APIKey != APITokenInput.Text)
{
CentralServer server = new CentralServer();
server.APIKey = APITokenInput.Text;
api.Central = server;
}
Switcher.Switch(new CreateOrJoin());
}
private void BackButton_Click(object sender, RoutedEventArgs e)
{
Switcher.Switch(new RegisterOrLogIn());
}
}
}

View File

@ -0,0 +1,27 @@
<UserControl x:Class="WinUI.OnboardProcess.Finished"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WinUI.OnboardProcess"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid VerticalAlignment="Center" HorizontalAlignment="Stretch" Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Text="All Finished!" HorizontalAlignment="Center"/>
<TextBlock Grid.Column="0" Grid.Row="1" HorizontalAlignment="Center" Text=""/>
<TextBlock Grid.Column="0" Grid.ColumnSpan="1" Grid.Row="2" MaxWidth="270" HorizontalAlignment="Center" Margin="10" Text="You've now joined your first ZeroTier network. Now go to add your other machines!" TextWrapping="Wrap"/>
<TextBlock Grid.Column="0" Grid.Row="3" HorizontalAlignment="Center" Text=""/>
<Button Grid.Column="0" Grid.Row="4" MaxWidth="100" Click="DoneButton_Click" Background="#FFFFB354" Margin="10">Done!</Button>
</Grid>
</UserControl>

View File

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WinUI.OnboardProcess
{
/// <summary>
/// Interaction logic for Finished.xaml
/// </summary>
public partial class Finished : UserControl, ISwitchable
{
public Finished()
{
InitializeComponent();
}
public void UtilizeState(object state)
{
throw new NotImplementedException();
}
private void DoneButton_Click(object sender, RoutedEventArgs e)
{
Window.GetWindow(this).Close();
}
}
}

View File

@ -0,0 +1,35 @@
<UserControl x:Class="WinUI.OnboardProcess.LogIn"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WinUI.OnboardProcess"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*" />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Grid.Row="0" HorizontalAlignment="Right" Margin="10">Email Address:</Label>
<TextBox x:Name="EmailAddressTextBox" Grid.Column="1" Grid.Row="0" MinWidth="150" Margin="10"></TextBox>
<Label Grid.Column="0" Grid.Row="1" HorizontalAlignment="Right" Margin="10">Password:</Label>
<PasswordBox x:Name="PasswordTextBox" Grid.Column="1" Grid.Row="1" PasswordChar="*" Margin="10"/>
<Button Grid.Column="1" Grid.Row="2" Click="LoginButton_Click" Content="LogIn" Background="#FFFFB354" Width="90" Margin="10" HorizontalAlignment="Right"/>
<Label x:Name="ErrorText" Grid.Row="3" Grid.ColumnSpan="2" HorizontalAlignment="Center"></Label>
<Label Grid.Row="4"></Label>
<Button Grid.Column="0" Grid.Row="5" Background="#FFFFB354" Click="BackButton_Click">Go Back</Button>
</Grid>
</UserControl>

View File

@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WinUI.OnboardProcess
{
/// <summary>
/// Interaction logic for LogIn.xaml
/// </summary>
public partial class LogIn : UserControl, ISwitchable
{
public LogIn()
{
InitializeComponent();
}
public void UtilizeState(object state)
{
throw new NotImplementedException();
}
public void LoginButton_Click(object sender, RoutedEventArgs e)
{
DoLogin();
}
public void BackButton_Click(object sender, RoutedEventArgs e)
{
Switcher.Switch(new RegisterOrLogIn());
}
private async void DoLogin()
{
CentralAPI api = CentralAPI.Instance;
bool didLogIn = await api.Login(EmailAddressTextBox.Text, PasswordTextBox.Password.ToString(), false);
if (didLogIn)
{
Switcher.Switch(new CreateOrJoin());
}
else
{
ErrorText.Content = "Invalid username or password";
}
}
}
}

View File

@ -0,0 +1,27 @@
<UserControl x:Class="WinUI.OnboardProcess.RegisterOrLogIn"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WinUI.OnboardProcess"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel>
<TextBlock HorizontalAlignment="Center" FontSize="20" FontWeight="Bold" FontFamily="Segoe UI">Welcome to ZeroTier</TextBlock>
<TextBlock HorizontalAlignment="Center" FontSize="16">Let's get started!</TextBlock>
<TextBlock HorizontalAlignment="Center"> </TextBlock>
<TextBlock HorizontalAlignment="Center" TextWrapping="Wrap">If you haven't yet created an account, click "Create Account" below. If you have an account, you can log in with your username/password, or simply enter an API key.</TextBlock>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<StackPanel.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Margin" Value="10,10,0,0"/>
</Style>
</StackPanel.Resources>
<Button x:Name="CreateAccountButton" Margin="10,5" Content="Create Account" Background="#FFFFB354" Click="CreateAccountButton_Click"/>
<Button x:Name="LogInButton" Margin="10,5" Content="Log In" Background="#FFFFB354" Click="LogInButton_Click"/>
<Button x:Name="TokenButton" Margin="10,5" Content="API Token" Background="#FFFFB354" Click="APIToken_Click"/>
</StackPanel>
</StackPanel>
</UserControl>

View File

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WinUI.OnboardProcess
{
/// <summary>
/// Interaction logic for RegisterOrLogIn.xaml
/// </summary>
public partial class RegisterOrLogIn : UserControl, ISwitchable
{
public RegisterOrLogIn()
{
InitializeComponent();
}
public void UtilizeState(object state)
{
throw new NotImplementedException();
}
public void CreateAccountButton_Click(object sender, System.Windows.RoutedEventArgs e)
{
Switcher.Switch(new CreateAccount());
}
private void LogInButton_Click(object sender, RoutedEventArgs e)
{
Switcher.Switch(new LogIn());
}
public void APIToken_Click(object sender, RoutedEventArgs e)
{
Switcher.Switch(new EnterToken());
}
}
}

View File

@ -0,0 +1,13 @@
<Window x:Class="WinUI.PageSwitcher"
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:WinUI"
mc:Ignorable="d"
Icon="ZeroTierIcon.ico"
Title="ZeroTier One" Height="300" Width="400">
<Grid>
</Grid>
</Window>

View File

@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace WinUI
{
/// <summary>
/// Interaction logic for PageSwitcher.xaml
/// </summary>
public partial class PageSwitcher : Window
{
public PageSwitcher()
{
InitializeComponent();
Switcher.pageSwitcher = this;
CentralAPI api = CentralAPI.Instance;
if (api.HasAccessToken())
{
Switcher.Switch(new OnboardProcess.CreateOrJoin());
}
else
{
Switcher.Switch(new OnboardProcess.RegisterOrLogIn());
}
}
public void Navigate(UserControl nextPage)
{
this.Content = nextPage;
}
public void Navigate(UserControl nextPage, object state)
{
this.Content = nextPage;
ISwitchable s = nextPage as ISwitchable;
if (s != null)
s.UtilizeState(state);
else
throw new ArgumentException("NextPage is not ISwitchable! "
+ nextPage.Name.ToString());
}
}
}

View File

@ -6,8 +6,25 @@
xmlns:local="clr-namespace:WinUI"
mc:Ignorable="d"
Title="PreferencesView" SizeToContent="WidthAndHeight" Height="Auto" Width="Auto" Icon="ZeroTierIcon.ico">
<Grid>
<CheckBox x:Name="startupCheckbox" Content="Launch ZeroTier On Startup" HorizontalAlignment="Left" Margin="10,10,10,10" VerticalAlignment="Top" Checked="startupCheckbox_Checked" Unchecked="startupCheckbox_Unchecked"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<CheckBox x:Name="startupCheckbox" Content="Launch ZeroTier On Startup" HorizontalAlignment="Left" Margin="10" VerticalAlignment="Top" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0"/>
<TextBlock Text="Central Instance:" Grid.Row="1" Grid.Column="0" Margin="10"/>
<TextBox x:Name="CentralInstanceTextBox" Grid.Row="1" Grid.Column="1" MinWidth="200" Margin="10"/>
</Grid>
<TextBlock Text="API Key:" Grid.Row="2" Grid.Column="0" Margin="10"/>
<TextBox x:Name="APIKeyTextBox" Grid.Row="2" Grid.Column="1" MinWidth="200" Margin="10"/>
<Button x:Name="OKButton" Grid.Row="3" Grid.Column="1" Background="#FFFFB354" Content="OK" Margin="10" Width="90" HorizontalAlignment="Right" Click="OKButton_Clicked"/>
</Grid>
</Window>

View File

@ -22,7 +22,7 @@ namespace WinUI
{
public static string AppName = "ZeroTier One";
private RegistryKey rk = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);
private string AppLocation = System.Reflection.Assembly.GetExecutingAssembly().Location;
public PreferencesView()
{
InitializeComponent();
@ -30,21 +30,45 @@ namespace WinUI
string keyValue = rk.GetValue(AppName) as string;
if (keyValue != null && keyValue.Equals(System.Reflection.Assembly.GetExecutingAssembly().Location))
if (keyValue != null && keyValue.Equals(AppLocation))
{
startupCheckbox.IsChecked = true;
}
CentralAPI api = CentralAPI.Instance;
CentralInstanceTextBox.Text = api.Central.ServerURL;
APIKeyTextBox.Text = api.Central.APIKey;
}
private void startupCheckbox_Checked(object sender, RoutedEventArgs e)
private void OKButton_Clicked(object sender, RoutedEventArgs e)
{
rk.SetValue(AppName, System.Reflection.Assembly.GetExecutingAssembly().Location);
}
CentralAPI api = CentralAPI.Instance;
private void startupCheckbox_Unchecked(object sender, RoutedEventArgs e)
{
rk.DeleteValue(AppName);
}
if (api.Central.ServerURL != CentralInstanceTextBox.Text ||
api.Central.APIKey != APIKeyTextBox.Text)
{
CentralServer newServer = new CentralServer();
newServer.ServerURL = CentralInstanceTextBox.Text;
newServer.APIKey = APIKeyTextBox.Text;
api.Central = newServer;
}
if (startupCheckbox.IsChecked.HasValue && (bool)startupCheckbox.IsChecked)
{
rk.SetValue(AppName, AppLocation);
}
else
{
string keyValue = rk.GetValue(AppName) as string;
if (keyValue != null && keyValue.Equals(AppLocation))
{
rk.DeleteValue(AppName);
}
}
Close();
}
}
}

24
windows/WinUI/Switcher.cs Normal file
View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Threading.Tasks;
namespace WinUI
{
public static class Switcher
{
public static PageSwitcher pageSwitcher;
public static void Switch(UserControl newPage)
{
pageSwitcher.Navigate(newPage);
}
public static void Switch(UserControl newPage, object state)
{
pageSwitcher.Navigate(newPage, state);
}
}
}

View File

@ -3,5 +3,4 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WinUI">
</ResourceDictionary>

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
@ -45,6 +46,9 @@ namespace WinUI
private ObservableCollection<MenuItem> _networkCollection = new ObservableCollection<MenuItem>();
private static Boolean shouldShowOnboardProcess = true;
#if DEBUG
private static bool isFirstRun = true;
#endif
public ObservableCollection<MenuItem> NetworkCollection
{
@ -83,7 +87,19 @@ namespace WinUI
{
if (networks.Count > 0)
{
#if DEBUG
if (isFirstRun)
{
shouldShowOnboardProcess = true;
isFirstRun = false;
}
else
{
shouldShowOnboardProcess = false;
}
#else
shouldShowOnboardProcess = false;
#endif
}
Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
@ -103,7 +119,12 @@ namespace WinUI
if (shouldShowOnboardProcess)
{
// TODO: Show onboarding process window (on main thread)
// TODO: Show onboarding process window (on main thread
Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() =>
{
PageSwitcher ps = new PageSwitcher();
ps.Show();
}));
shouldShowOnboardProcess = false;
}

View File

@ -80,6 +80,7 @@
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Printing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
@ -104,20 +105,50 @@
<Compile Include="AboutView.xaml.cs">
<DependentUpon>AboutView.xaml</DependentUpon>
</Compile>
<Compile Include="CentralAPI.cs" />
<Compile Include="CentralLogin.cs" />
<Compile Include="CentralNetwork.cs" />
<Compile Include="CentralServer.cs" />
<Compile Include="CentralToken.cs" />
<Compile Include="CentralUser.cs" />
<Compile Include="ISwitchable.cs" />
<Compile Include="JoinNetworkView.xaml.cs">
<DependentUpon>JoinNetworkView.xaml</DependentUpon>
</Compile>
<Compile Include="NetworkMonitor.cs" />
<Compile Include="NetworkNameGenerator.cs" />
<Compile Include="NetworkRoute.cs" />
<Compile Include="NetworksPage.xaml.cs">
<DependentUpon>NetworksPage.xaml</DependentUpon>
</Compile>
<Compile Include="OnboardProcess\CreateAccount.xaml.cs">
<DependentUpon>CreateAccount.xaml</DependentUpon>
</Compile>
<Compile Include="OnboardProcess\CreateOrJoin.xaml.cs">
<DependentUpon>CreateOrJoin.xaml</DependentUpon>
</Compile>
<Compile Include="OnboardProcess\EnterToken.xaml.cs">
<DependentUpon>EnterToken.xaml</DependentUpon>
</Compile>
<Compile Include="OnboardProcess\Finished.xaml.cs">
<DependentUpon>Finished.xaml</DependentUpon>
</Compile>
<Compile Include="OnboardProcess\LogIn.xaml.cs">
<DependentUpon>LogIn.xaml</DependentUpon>
</Compile>
<Compile Include="OnboardProcess\RegisterOrLogIn.xaml.cs">
<DependentUpon>RegisterOrLogIn.xaml</DependentUpon>
</Compile>
<Compile Include="PageSwitcher.xaml.cs">
<DependentUpon>PageSwitcher.xaml</DependentUpon>
</Compile>
<Compile Include="PeersPage.xaml.cs">
<DependentUpon>PeersPage.xaml</DependentUpon>
</Compile>
<Compile Include="PreferencesView.xaml.cs">
<DependentUpon>PreferencesView.xaml</DependentUpon>
</Compile>
<Compile Include="Switcher.cs" />
<Compile Include="ToolbarItem.xaml.cs">
<DependentUpon>ToolbarItem.xaml</DependentUpon>
</Compile>
@ -129,6 +160,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Done.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="JoinNetworkView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
@ -154,6 +189,34 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="OnboardProcess\CreateAccount.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="OnboardProcess\CreateOrJoin.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="OnboardProcess\EnterToken.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="OnboardProcess\Finished.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="OnboardProcess\LogIn.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="OnboardProcess\RegisterOrLogIn.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="PageSwitcher.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="PeersPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>