diff options
-rw-r--r-- | Juick/App.xaml.cs | 25 | ||||
-rw-r--r-- | Juick/Classes/AccountManager.cs | 62 | ||||
-rw-r--r-- | Juick/Controls/MessageList.xaml | 2 | ||||
-rw-r--r-- | Juick/Juick.csproj | 2 | ||||
-rw-r--r-- | Juick/LoginView.xaml | 36 | ||||
-rw-r--r-- | Juick/LoginView.xaml.cs | 22 | ||||
-rw-r--r-- | Juick/MainPage.xaml | 19 | ||||
-rw-r--r-- | Juick/MainPage.xaml.cs | 55 | ||||
-rw-r--r-- | Juick/NewPostView.xaml.cs | 2 | ||||
-rw-r--r-- | Juick/ViewModels/LoginViewModel.cs | 72 | ||||
-rw-r--r-- | Juick/ViewModels/PageViewModel.cs | 4 | ||||
-rw-r--r-- | Juick/ViewModels/Validation/DataViewModelBase.cs | 44 |
12 files changed, 245 insertions, 100 deletions
diff --git a/Juick/App.xaml.cs b/Juick/App.xaml.cs index 98dc31c..03e0d10 100644 --- a/Juick/App.xaml.cs +++ b/Juick/App.xaml.cs @@ -10,22 +10,31 @@ namespace Juick {
public partial class App : Application
{
- public void NavigateTo(Uri param) {
- RootFrame.Navigate(param);
+ public void NavigateTo(Uri param, bool removeFromStack = false)
+ {
+ var page = (PhoneApplicationPage) RootFrame.Content;
+ var service = page.NavigationService;
+ service.Navigate(param);
+ if (removeFromStack)
+ {
+ page.Dispatcher.BeginInvoke(() => service.RemoveBackEntry());
+ }
}
- private static AppViewModel context = null;
+ private static AppViewModel _context = null;
public static AppViewModel AppContext
{
get
{
// Delay creation of the view model until necessary
- if (context == null) {
- context = new AppViewModel();
- context.Pages.Add(new PageViewModel(context) { RestUri = "/home?1=1" });
- context.Pages.Add(new PageViewModel(context) { RestUri = "/messages?1=1" });
+ if (_context == null) {
+ _context = new AppViewModel();
+ _context.Pages.Add(new PageViewModel(_context) { RestUri = "/home?1=1", Caption = "My feed"});
+ _context.Pages.Add(new PageViewModel(_context) { RestUri = "/messages?1=1", Caption = "Last"});
+ _context.Pages.Add(new PageViewModel(_context) { RestUri = "/messages?1=1&popular=1", Caption = "Top"});
+ _context.Pages.Add(new PageViewModel(_context) { RestUri = "/messages?1=1&media=all", Caption = "Images" });
};
- return context;
+ return _context;
}
}
/// <summary>
diff --git a/Juick/Classes/AccountManager.cs b/Juick/Classes/AccountManager.cs index 8f8e366..4741992 100644 --- a/Juick/Classes/AccountManager.cs +++ b/Juick/Classes/AccountManager.cs @@ -1,53 +1,63 @@ using System;
using System.IO.IsolatedStorage;
using System.Net;
+using System.Windows;
using System.Windows.Controls;
namespace Juick.Classes
{
public class AccountManager
{
- const string FileName = "account.xml";
- public NetworkCredential Credentials
+ private string _userName;
+ private string _password;
+
+ public string UserName
{
get
{
- string userName;
- string password;
- IsolatedStorageSettings.ApplicationSettings.TryGetValue<string>("user", out userName);
- IsolatedStorageSettings.ApplicationSettings.TryGetValue<string>("password", out password);
- return new NetworkCredential
- {
- UserName = userName,
- Password = password,
- };
+ if (_userName == null)
+ {
+ IsolatedStorageSettings.ApplicationSettings.TryGetValue<string>("user", out _userName);
+ }
+ return _userName;
}
set
{
- var creds = value;
- if (creds != null)
- {
- IsolatedStorageSettings.ApplicationSettings["user"] = value.UserName;
- IsolatedStorageSettings.ApplicationSettings["password"] = value.Password;
- }
- else
+ _userName = value;
+ IsolatedStorageSettings.ApplicationSettings["user"] = _userName;
+ }
+ }
+
+ public string Password
+ {
+ get
+ {
+ if (_password == null)
{
- IsolatedStorageSettings.ApplicationSettings["user"] = null;
- IsolatedStorageSettings.ApplicationSettings["password"] = null;
+ IsolatedStorageSettings.ApplicationSettings.TryGetValue<string>("password", out _password);
}
+ return _password;
+ }
+ set
+ {
+ _password = value;
+ IsolatedStorageSettings.ApplicationSettings["password"] = value;
}
}
- public void SignIn(Page page, string login, string pass, Uri nextUri)
+ public bool IsAuthenticated
{
- Credentials = new NetworkCredential(login, pass);
- page.NavigationService.Navigate(nextUri);
- page.Dispatcher.BeginInvoke(() => page.NavigationService.RemoveBackEntry());
+ get {
+ bool authenticated;
+ IsolatedStorageSettings.ApplicationSettings.TryGetValue<bool>("authenticated", out authenticated);
+ return authenticated;
+ }
+ set { IsolatedStorageSettings.ApplicationSettings["authenticated"] = value; }
}
-
+
public void SignOut(Page page)
{
- Credentials = null;
+ IsAuthenticated = false;
page.NavigationService.Navigate(new Uri("/LoginView.xaml", UriKind.Relative));
page.Dispatcher.BeginInvoke(() => page.NavigationService.RemoveBackEntry());
}
diff --git a/Juick/Controls/MessageList.xaml b/Juick/Controls/MessageList.xaml index 7a7d823..afa6c69 100644 --- a/Juick/Controls/MessageList.xaml +++ b/Juick/Controls/MessageList.xaml @@ -14,7 +14,7 @@ d:DesignHeight="480" d:DesignWidth="480"> <Grid x:Name="LayoutRoot"> - <toolkit:LongListSelector x:Name="longList" Margin="0, 0, -12, 0" + <toolkit:LongListSelector Margin="0, 0, -12, 0" ItemsSource="{Binding Items}" IsFlatList="true"> <i:Interaction.Triggers> <i:EventTrigger EventName="Link"> diff --git a/Juick/Juick.csproj b/Juick/Juick.csproj index ebe128c..33fbd5c 100644 --- a/Juick/Juick.csproj +++ b/Juick/Juick.csproj @@ -103,9 +103,11 @@ <DependentUpon>ThreadView.xaml</DependentUpon>
</Compile>
<Compile Include="ViewModels\AppViewModel.cs" />
+ <Compile Include="ViewModels\LoginViewModel.cs" />
<Compile Include="ViewModels\PageViewModel.cs" />
<Compile Include="ViewModels\PostItem.cs" />
<Compile Include="ViewModels\ThreadViewModel.cs" />
+ <Compile Include="ViewModels\Validation\DataViewModelBase.cs" />
<Compile Include="ViewModels\ViewModelBase.cs" />
</ItemGroup>
<ItemGroup>
diff --git a/Juick/LoginView.xaml b/Juick/LoginView.xaml index ad1d70c..004580a 100644 --- a/Juick/LoginView.xaml +++ b/Juick/LoginView.xaml @@ -3,15 +3,19 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
- xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
+ xmlns:phoneshell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:viewModels="clr-namespace:Juick.ViewModels"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d" d:DesignHeight="768" d:DesignWidth="480"
- shell:SystemTray.IsVisible="True" Loaded="PhoneApplicationPage_Loaded">
+ phoneshell:SystemTray.IsVisible="True">
+ <phone:PhoneApplicationPage.DataContext>
+ <viewModels:LoginViewModel />
+ </phone:PhoneApplicationPage.DataContext>
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
@@ -28,24 +32,26 @@ <!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
- <TextBlock Height="30" HorizontalAlignment="Left" Margin="37,119,0,0" Name="textBlock1" Text="Username" VerticalAlignment="Top" />
- <TextBox Height="72" HorizontalAlignment="Left" Margin="25,155,0,0" Name="textBox1" Text="{Binding UserName}" VerticalAlignment="Top" Width="409" />
- <TextBlock Height="30" HorizontalAlignment="Left" Margin="37,246,0,0" Name="textBlock2" Text="Password" VerticalAlignment="Top" />
- <Button Content="Sign in" Height="72" HorizontalAlignment="Left" Margin="274,360,0,0" Name="button1" VerticalAlignment="Top" Width="160" Click="button1_Click" />
- <PasswordBox Height="72" HorizontalAlignment="Left" Password="{Binding Password}" Margin="25,282,0,0" Name="textBox2" VerticalAlignment="Top" Width="409" />
+ <TextBlock Height="30" HorizontalAlignment="Left" Margin="37,119,0,0" Text="Username" VerticalAlignment="Top" />
+ <TextBox Height="72" HorizontalAlignment="Left" Margin="25,155,0,0" Text="{Binding Username, Mode=TwoWay}" VerticalAlignment="Top" Width="409" />
+ <TextBlock Height="30" HorizontalAlignment="Left" Margin="37,246,0,0" Text="Password" VerticalAlignment="Top" />
+ <Button Content="Sign in" Height="72" HorizontalAlignment="Left" Margin="274,360,0,0"
+ VerticalAlignment="Top" Width="160" Command="{Binding SignInCommand}" />
+ <PasswordBox Height="72" HorizontalAlignment="Left" Password="{Binding Password, Mode=TwoWay}"
+ Margin="25,282,0,0" VerticalAlignment="Top" Width="409" />
</Grid>
</Grid>
<!--Sample code showing usage of ApplicationBar-->
<!--<phone:PhoneApplicationPage.ApplicationBar>
- <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
- <shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Button 1"/>
- <shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" Text="Button 2"/>
- <shell:ApplicationBar.MenuItems>
- <shell:ApplicationBarMenuItem Text="MenuItem 1"/>
- <shell:ApplicationBarMenuItem Text="MenuItem 2"/>
- </shell:ApplicationBar.MenuItems>
- </shell:ApplicationBar>
+ <phoneshell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
+ <phoneshell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Button 1"/>
+ <phoneshell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" Text="Button 2"/>
+ <phoneshell:ApplicationBar.MenuItems>
+ <phoneshell:ApplicationBarMenuItem Text="MenuItem 1"/>
+ <phoneshell:ApplicationBarMenuItem Text="MenuItem 2"/>
+ </phoneshell:ApplicationBar.MenuItems>
+ </phoneshell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>-->
</phone:PhoneApplicationPage>
diff --git a/Juick/LoginView.xaml.cs b/Juick/LoginView.xaml.cs index 098275b..06ac58c 100644 --- a/Juick/LoginView.xaml.cs +++ b/Juick/LoginView.xaml.cs @@ -1,5 +1,6 @@ using System.Linq;
using System.Windows;
+using Juick.ViewModels;
using Microsoft.Phone.Controls;
using System;
@@ -10,27 +11,18 @@ namespace Juick public LoginView()
{
InitializeComponent();
- DataContext = App.AppContext.Account.Credentials;
+ Loaded += (sender, args) =>
+ {
+ while (NavigationService.BackStack.Any())
+ NavigationService.RemoveBackEntry();
+ };
}
- private Uri nextUri;
-
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
- nextUri = NavigationContext.QueryString.ContainsKey("FileId")
+ ((LoginViewModel)DataContext).NextUri = NavigationContext.QueryString.ContainsKey("FileId")
? new Uri(string.Format("/NewPostView.xaml?FileId={0}", NavigationContext.QueryString["FileId"]), UriKind.Relative)
: new Uri("/MainPage.xaml", UriKind.Relative);
}
-
- private void button1_Click(object sender, RoutedEventArgs e)
- {
- App.AppContext.Account.SignIn(this, textBox1.Text, textBox2.Password, nextUri);
- }
-
- private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
- {
- while (NavigationService.BackStack.Any())
- NavigationService.RemoveBackEntry();
- }
}
}
\ No newline at end of file diff --git a/Juick/MainPage.xaml b/Juick/MainPage.xaml index 2e40d0f..45e1ff9 100644 --- a/Juick/MainPage.xaml +++ b/Juick/MainPage.xaml @@ -22,14 +22,19 @@ <RowDefinition Height="*" />
</Grid.RowDefinitions>
- <controls:Pivot Title="juick" Grid.Row="1">
- <controls:PivotItem Header="My feed" Margin="0, -5, 0, 0">
- <usercontrols:MessageList x:Name="Home" />
- </controls:PivotItem>
- <controls:PivotItem Header="Last" Margin="0, -5, 0, 0">
- <usercontrols:MessageList x:Name="Last"/>
- </controls:PivotItem>
+ <controls:Pivot x:Name="Pages" Title="juick" Grid.Row="1" ItemsSource="{Binding Pages}">
+ <controls:Pivot.HeaderTemplate>
+ <DataTemplate>
+ <TextBlock Text="{Binding Caption}"></TextBlock>
+ </DataTemplate>
+ </controls:Pivot.HeaderTemplate>
+ <controls:Pivot.ItemTemplate>
+ <DataTemplate>
+ <usercontrols:MessageList />
+ </DataTemplate>
+ </controls:Pivot.ItemTemplate>
</controls:Pivot>
+
</Grid>
<phone:PhoneApplicationPage.ApplicationBar>
<phoneshell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
diff --git a/Juick/MainPage.xaml.cs b/Juick/MainPage.xaml.cs index a634e7f..1c76ee1 100644 --- a/Juick/MainPage.xaml.cs +++ b/Juick/MainPage.xaml.cs @@ -4,10 +4,12 @@ using System.Text; using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
+using Juick.Controls;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Notification;
using Microsoft.Phone.Shell;
using Juick.ViewModels;
+using RestSharp;
namespace Juick
{
@@ -23,7 +25,7 @@ namespace Juick //string channelName = "JuickChannel"; // unused variable
InitializeComponent();
-
+
/* // Try to find the push channel.
pushChannel = HttpNotificationChannel.Find(channelName);
@@ -61,33 +63,32 @@ namespace Juick }*/
- // Set the data context of the listbox control to the sample data
DataContext = App.AppContext;
- Home.DataContext = ((AppViewModel)DataContext).Pages[0];
- Last.DataContext = ((AppViewModel)DataContext).Pages[1];
+
Loaded += (o, args) =>
- {
- var progressIndicator = SystemTray.ProgressIndicator;
+ {
+ Pages.SelectedIndex = 0;
+ var progressIndicator = SystemTray.ProgressIndicator;
- if (progressIndicator != null)
- {
- return;
- }
+ if (progressIndicator != null)
+ {
+ return;
+ }
- progressIndicator = new ProgressIndicator();
+ progressIndicator = new ProgressIndicator();
- SystemTray.SetProgressIndicator(this, progressIndicator);
+ SystemTray.SetProgressIndicator(this, progressIndicator);
- Binding binding = new Binding("IsDataLoading") { Source = DataContext };
+ Binding binding = new Binding("IsDataLoading") { Source = DataContext };
- BindingOperations.SetBinding(
- progressIndicator, ProgressIndicator.IsVisibleProperty, binding);
+ BindingOperations.SetBinding(
+ progressIndicator, ProgressIndicator.IsVisibleProperty, binding);
- binding = new Binding("IsDataLoading") { Source = DataContext };
+ binding = new Binding("IsDataLoading") { Source = DataContext };
- BindingOperations.SetBinding(
- progressIndicator, ProgressIndicator.IsIndeterminateProperty, binding);
- };
+ BindingOperations.SetBinding(
+ progressIndicator, ProgressIndicator.IsIndeterminateProperty, binding);
+ };
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
@@ -102,7 +103,7 @@ namespace Juick // Ensure that there is at least one key in the query string, and check
// whether the "FileId" key is present.
- navigateUri = string.IsNullOrEmpty(App.AppContext.Account.Credentials.UserName) ? loginUriPart : newPostUriPart;
+ navigateUri = App.AppContext.Account.IsAuthenticated ? newPostUriPart : loginUriPart;
if (queryStrings.ContainsKey("FileId"))
{
FileId = queryStrings["FileId"];
@@ -112,6 +113,11 @@ namespace Juick {
NavigationService.Navigate(new Uri(navigateUri, UriKind.Relative));
}
+ App.AppContext.Client.Authenticator = new HttpBasicAuthenticator(App.AppContext.Account.UserName, App.AppContext.Account.Password);
+ if (App.AppContext.Account.IsAuthenticated)
+ foreach (var page in ((AppViewModel)DataContext).Pages)
+ page.RefreshData();
+
}
void PushChannel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
@@ -163,10 +169,11 @@ namespace Juick private void ApplicationBarIconButtonClick(object sender, EventArgs e)
{
- App.AppContext.Pages[0].Items.Clear();
- App.AppContext.Pages[1].Items.Clear();
- App.AppContext.Pages[0].RefreshData();
- App.AppContext.Pages[1].RefreshData();
+ foreach (var page in App.AppContext.Pages)
+ {
+ page.Items.Clear();
+ page.RefreshData();
+ }
}
private void ApplicationBarMenuItemClick(object sender, EventArgs e)
diff --git a/Juick/NewPostView.xaml.cs b/Juick/NewPostView.xaml.cs index 4f3cc06..dbc45c4 100644 --- a/Juick/NewPostView.xaml.cs +++ b/Juick/NewPostView.xaml.cs @@ -43,7 +43,7 @@ namespace Juick // whether the "FileId" key is present.
if (queryStrings.ContainsKey("FileId"))
{
- App.AppContext.Client.Authenticator = new HttpBasicAuthenticator(App.AppContext.Account.Credentials.UserName, App.AppContext.Account.Credentials.Password);
+ App.AppContext.Client.Authenticator = new HttpBasicAuthenticator(App.AppContext.Account.UserName, App.AppContext.Account.Password);
// Retrieve the picture from the media library using the FileID
// passed to the application.
MediaLibrary library = new MediaLibrary();
diff --git a/Juick/ViewModels/LoginViewModel.cs b/Juick/ViewModels/LoginViewModel.cs new file mode 100644 index 0000000..95528fe --- /dev/null +++ b/Juick/ViewModels/LoginViewModel.cs @@ -0,0 +1,72 @@ +using System; +using System.Net; +using System.Windows; +using Juick.Classes; +using Juick.ViewModels.Validation; +using RestSharp; + +namespace Juick.ViewModels +{ + public class LoginViewModel : DataViewModelBase + { + private string _username; + private string _password; + private DelegateCommand<RoutedEventArgs> _signInCommand; + + public string Username + { + get + { + return _username ?? (_username = App.AppContext.Account.UserName); + } + set + { + _username = value; + NotifyPropertyChanged("Username"); + } + } + + public string Password + { + get + { + return _password ?? (_password = App.AppContext.Account.Password); + } + set + { + _password = value; + NotifyPropertyChanged("Password"); + } + } + + public Uri NextUri { get; set; } + + public DelegateCommand<RoutedEventArgs> SignInCommand + { + get + { + return _signInCommand ?? (_signInCommand = new DelegateCommand<RoutedEventArgs>(CheckAuth, () => true)); + } + } + + public void CheckAuth(RoutedEventArgs e) + { + App.AppContext.Client.Authenticator = new HttpBasicAuthenticator(Username, Password); + App.AppContext.Client.ExecuteAsync(new RestRequest("/post", Method.POST), response => + { + if (response.StatusCode == + HttpStatusCode.BadRequest) + { + App.AppContext.Account.UserName = Username; + App.AppContext.Account.Password = Password; + App.AppContext.Account.IsAuthenticated = true; + ((App)Application.Current).NavigateTo(NextUri, true); + } + else + { + AddError("Username", "Invalid username or password", false); + } + }); + } + } +} diff --git a/Juick/ViewModels/PageViewModel.cs b/Juick/ViewModels/PageViewModel.cs index 02f86ff..e398caf 100644 --- a/Juick/ViewModels/PageViewModel.cs +++ b/Juick/ViewModels/PageViewModel.cs @@ -31,11 +31,10 @@ namespace Juick.ViewModels set { _restUri = value; - RefreshData(); } } - public virtual string Caption { get { return "juick"; } } + public virtual string Caption { get; set; } public ObservableCollection<PostItem> Items { get; private set; } @@ -70,7 +69,6 @@ namespace Juick.ViewModels } var request = new RestRequest(requestUri); - _context.Client.Authenticator = new HttpBasicAuthenticator(_context.Account.Credentials.UserName, _context.Account.Credentials.Password); _context.Client.ExecuteAsync<List<Message>>(request, response => { _context.IsDataLoading = false; diff --git a/Juick/ViewModels/Validation/DataViewModelBase.cs b/Juick/ViewModels/Validation/DataViewModelBase.cs new file mode 100644 index 0000000..dcbf34b --- /dev/null +++ b/Juick/ViewModels/Validation/DataViewModelBase.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; + +namespace Juick.ViewModels.Validation +{ + public class DataViewModelBase : ViewModelBase, IDataErrorInfo + { + + private readonly Dictionary<String, List<String>> _errors = + new Dictionary<string, List<string>>(); + + // Adds the specified error to the errors collection if it is not already + // present, inserting it in the first position if isWarning is false. + public void AddError(string propertyName, string error, bool isWarning) + { + if (!_errors.ContainsKey(propertyName)) + _errors[propertyName] = new List<string>(); + + if (_errors[propertyName].Contains(error)) return; + if (isWarning) _errors[propertyName].Add(error); + else _errors[propertyName].Insert(0, error); + } + + // Removes the specified error from the errors collection if it is present. + public void RemoveError(string propertyName, string error) + { + if (!_errors.ContainsKey(propertyName) || !_errors[propertyName].Contains(error)) return; + _errors[propertyName].Remove(error); + if (_errors[propertyName].Count == 0) _errors.Remove(propertyName); + } + + public string Error { get { throw new NotImplementedException(); } } + + public string this[string columnName] + { + get + { + return (!_errors.ContainsKey(columnName) ? null : + String.Join(Environment.NewLine, _errors[columnName])); + } + } + } +} |