diff --git a/PokemonGo-UWP/App.xaml b/PokemonGo-UWP/App.xaml index 0980d4181..f047c8d65 100644 --- a/PokemonGo-UWP/App.xaml +++ b/PokemonGo-UWP/App.xaml @@ -18,6 +18,8 @@ + + diff --git a/PokemonGo-UWP/Controls/CircularProgressBar.xaml.cs b/PokemonGo-UWP/Controls/CircularProgressBar.xaml.cs index 90805cec1..8dcf9de82 100644 --- a/PokemonGo-UWP/Controls/CircularProgressBar.xaml.cs +++ b/PokemonGo-UWP/Controls/CircularProgressBar.xaml.cs @@ -121,7 +121,18 @@ public Brush InnerSegmentColor public Uri ImageSourcePath { - get { return (Uri)GetValue(ImageSourcePathProperty); } + get + { + try + { + Uri u = (Uri)GetValue(ImageSourcePathProperty); + return u; + } + catch (Exception ex) + { + return null; + } + } set { SetValue(ImageSourcePathProperty, value); } } #endregion diff --git a/PokemonGo-UWP/Package.appxmanifest b/PokemonGo-UWP/Package.appxmanifest index 8f8bed978..1d3f6fc02 100644 --- a/PokemonGo-UWP/Package.appxmanifest +++ b/PokemonGo-UWP/Package.appxmanifest @@ -1,6 +1,6 @@  - + PoGo diff --git a/PokemonGo-UWP/Strings/en-US/CodeResources.resw b/PokemonGo-UWP/Strings/en-US/CodeResources.resw index 32eb5ab43..659562822 100644 --- a/PokemonGo-UWP/Strings/en-US/CodeResources.resw +++ b/PokemonGo-UWP/Strings/en-US/CodeResources.resw @@ -365,4 +365,13 @@ You also got {1} Stardust, {2} Candy and {3} XP. Streak Bonus + + Cannot transfer your buddy Pokémon! + + + After you transfer, you can't undo the transfer. + + + Do you want to transfer {0} Pokémon to the Professor? + \ No newline at end of file diff --git a/PokemonGo-UWP/Utils/Game/Converters.cs b/PokemonGo-UWP/Utils/Game/Converters.cs index 6b8442f75..9316e761d 100644 --- a/PokemonGo-UWP/Utils/Game/Converters.cs +++ b/PokemonGo-UWP/Utils/Game/Converters.cs @@ -28,6 +28,7 @@ using Windows.Foundation; using Windows.Services.Maps; using System.Threading.Tasks; +using Windows.UI.Xaml.Input; namespace PokemonGo_UWP.Utils { @@ -88,6 +89,59 @@ public object ConvertBack(object value, Type targetType, object parameter, strin #endregion } + public class TappedRoutedEventArgsToClickedItemConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + TappedRoutedEventArgs args = (TappedRoutedEventArgs)value; + FrameworkElement clickedItem = args.OriginalSource as FrameworkElement; + return clickedItem.DataContext; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + return value; + } + } + + public class DoubleTappedRoutedEventArgsToClickedItemConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + DoubleTappedRoutedEventArgs args = (DoubleTappedRoutedEventArgs)value; + FrameworkElement clickedItem = args.OriginalSource as FrameworkElement; + return clickedItem.DataContext; + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + return value; + } + } + + public class HoldingRoutedEventArgsToClickedItemConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, string language) + { + HoldingRoutedEventArgs args = (HoldingRoutedEventArgs)value; + if (args.HoldingState == Windows.UI.Input.HoldingState.Completed) + { + FrameworkElement clickedItem = args.OriginalSource as FrameworkElement; + return clickedItem.DataContext; + } + else + { + args.Handled = true; + return null; + } + } + + public object ConvertBack(object value, Type targetType, object parameter, string language) + { + return value; + } + } + public class PokemonIdToPokedexDescription : IValueConverter { #region Implementation of IValueConverter diff --git a/PokemonGo-UWP/Utils/Game/DataTemplateSelectors.cs b/PokemonGo-UWP/Utils/Game/DataTemplateSelectors.cs index d5bf5c8e4..4f737ffa8 100644 --- a/PokemonGo-UWP/Utils/Game/DataTemplateSelectors.cs +++ b/PokemonGo-UWP/Utils/Game/DataTemplateSelectors.cs @@ -57,4 +57,22 @@ protected override DataTemplate SelectTemplateCore(object item, DependencyObject return template; } } + + public class SelectedItemsTemplateSelector : DataTemplateSelector + { + public DataTemplate DefaultStateTemplate { get; set; } + public DataTemplate SelectedStateTemplate { get; set; } + protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) + { + GridViewItem gvi = container as GridViewItem; + if (gvi != null) + { + if (gvi.IsSelected) + return SelectedStateTemplate; + else + return DefaultStateTemplate; + } + return base.SelectTemplateCore(item, container); + } + } } diff --git a/PokemonGo-UWP/Utils/GameClient.cs b/PokemonGo-UWP/Utils/GameClient.cs index d8940f0fb..1a64758fa 100644 --- a/PokemonGo-UWP/Utils/GameClient.cs +++ b/PokemonGo-UWP/Utils/GameClient.cs @@ -1178,6 +1178,16 @@ public static async Task TransferPokemon(ulong pokemonId return await _client.Inventory.TransferPokemon(pokemonId); } + /// + /// Transfers multiple Pokemons at once + /// + /// + /// + public static async Task TransferPokemons(ulong[] pokemonIds) + { + return await _client.Inventory.TransferPokemons(pokemonIds); + } + /// /// Favourites/Unfavourites the Pokemon /// diff --git a/PokemonGo-UWP/ViewModels/PokemonDetailPageViewModel.cs b/PokemonGo-UWP/ViewModels/PokemonDetailPageViewModel.cs index 8457438a7..8216a3189 100644 --- a/PokemonGo-UWP/ViewModels/PokemonDetailPageViewModel.cs +++ b/PokemonGo-UWP/ViewModels/PokemonDetailPageViewModel.cs @@ -266,6 +266,18 @@ public bool PlayerTeamIsSet cannotTransferDialog.Show(); return; } + // This is also for the buddy pokemon + if (Convert.ToBoolean(SelectedPokemon.IsBuddy)) + { + var cannotTransferDialog = new PoGoMessageDialog(Resources.CodeResources.GetString("CannotTransferBuddy"), "") + { + CoverBackground = true, + AnimationType = PoGoMessageDialogAnimation.Bottom + }; + cannotTransferDialog.Show(); + return; + } + // Ask for confirmation before moving the Pokemon var name = Resources.Pokemon.GetString(SelectedPokemon.PokemonId.ToString()); var dialog = diff --git a/PokemonGo-UWP/ViewModels/PokemonInventoryPageViewModel.cs b/PokemonGo-UWP/ViewModels/PokemonInventoryPageViewModel.cs index ed623f584..7a57619f1 100644 --- a/PokemonGo-UWP/ViewModels/PokemonInventoryPageViewModel.cs +++ b/PokemonGo-UWP/ViewModels/PokemonInventoryPageViewModel.cs @@ -14,6 +14,8 @@ using Template10.Services.NavigationService; using PokemonGo_UWP.Utils.Extensions; using Windows.UI.Xaml.Media.Animation; +using PokemonGo_UWP.Controls; +using POGOProtos.Networking.Responses; namespace PokemonGo_UWP.ViewModels { @@ -75,12 +77,15 @@ public override async Task OnNavigatedToAsync(object parameter, NavigationMode m ulong pokemonId = (ulong)NavigationHelper.NavigationState["LastViewedPokemonDetailID"]; NavigationHelper.NavigationState.Remove("LastViewedPokemonDetailID"); var pokemon = PokemonInventory.Where(p => p.Id == pokemonId).FirstOrDefault(); - if(pokemon != null) + if (pokemon != null) { ScrollPokemonToVisibleRequired?.Invoke(pokemon); } } + // set the selectioMode to GoToDetail + currentSelectionMode = SelectionMode.GoToDetail; + await Task.CompletedTask; } @@ -113,6 +118,7 @@ public override async Task OnNavigatingFromAsync(NavigatingEventArgs args) public delegate void ScrollPokemonToVisibleHandler(PokemonDataWrapper p); public event ScrollPokemonToVisibleHandler ScrollPokemonToVisibleRequired; + public event EventHandler MultiplePokemonSelected; /// /// Player's profile, we use it just for the maximum ammount of pokemon @@ -157,6 +163,38 @@ public PokemonSortingModes CurrentPokemonSortingMode /// public ObservableCollection IncubatorsInventory => GameClient.IncubatorsInventory; + /// + /// Flag for an ongoing server request. Used to disable the controls + /// + private bool _serverRequestRunning; + public bool ServerRequestRunning + { + get { return _serverRequestRunning; } + set + { + Set(ref _serverRequestRunning, value); + ReturnToGameScreen.RaiseCanExecuteChanged(); + GotoPokemonDetailCommand.RaiseCanExecuteChanged(); + GotoEggDetailCommand.RaiseCanExecuteChanged(); + CloseMultipleSelectPokemonCommand.RaiseCanExecuteChanged(); + TransferMultiplePokemonCommand.RaiseCanExecuteChanged(); + } + } + + public enum SelectionMode + { + GoToDetail, + MultipleSelect + }; + + public SelectionMode currentSelectionMode; + + /// + /// Collection of selected pokemons to transfer + /// + public ObservableCollection SelectedPokemons { get; private set; } = + new ObservableCollection(); + /// /// Total amount of Pokemon in players inventory /// @@ -175,11 +213,12 @@ public int TotalPokemonCount { /// /// Going back to map page /// - public DelegateCommand ReturnToGameScreen - => - _returnToGameScreen ?? - (_returnToGameScreen = - new DelegateCommand(() => { NavigationService.GoBack(); }, () => true)); + public DelegateCommand ReturnToGameScreen => _returnToGameScreen ?? ( + _returnToGameScreen = new DelegateCommand(() => + { + if (ServerRequestRunning) return; + NavigationService.GoBack(); + }, () => true)); #endregion @@ -204,27 +243,194 @@ private void UpdateSorting() /// Navigate to the detail page for the selected pokemon /// private DelegateCommand _gotoPokemonDetailCommand; - public DelegateCommand GotoPokemonDetailCommand => _gotoPokemonDetailCommand ?? (_gotoPokemonDetailCommand = new DelegateCommand((selectedPokemon) => + public DelegateCommand GotoPokemonDetailCommand => _gotoPokemonDetailCommand ?? (_gotoPokemonDetailCommand = new DelegateCommand((selectedPokemon) => { - NavigationService.Navigate(typeof(PokemonDetailPage), new SelectedPokemonNavModel() + // If the multiple select mode is on, select additional pokemons in stead of going to the details + if (currentSelectionMode == SelectionMode.MultipleSelect) { - SelectedPokemonId = selectedPokemon.Id.ToString(), - SortingMode = CurrentPokemonSortingMode, - ViewMode = PokemonDetailPageViewMode.Normal - }, new SuppressNavigationTransitionInfo()); + SelectPokemon(selectedPokemon); + } + else + { + NavigationService.Navigate(typeof(PokemonDetailPage), new SelectedPokemonNavModel() + { + SelectedPokemonId = selectedPokemon.Id.ToString(), + SortingMode = CurrentPokemonSortingMode, + ViewMode = PokemonDetailPageViewMode.Normal + }, new SuppressNavigationTransitionInfo()); + } })); /// /// Navigate to detail page for the selected egg /// private DelegateCommand _gotoEggDetailCommand; - public DelegateCommand GotoEggDetailCommand => _gotoEggDetailCommand ?? (_gotoEggDetailCommand = new DelegateCommand((selectedEgg) => + public DelegateCommand GotoEggDetailCommand => _gotoEggDetailCommand ?? (_gotoEggDetailCommand = new DelegateCommand((selectedEgg) => + { + NavigationService.Navigate(typeof(EggDetailPage), selectedEgg.Id.ToString(), new SuppressNavigationTransitionInfo()); + })); + + #endregion + + #region Multiple Select and transfer + private DelegateCommand _selectMultiplePokemonCommand; + public DelegateCommand SelectMultiplePokemonCommand => _selectMultiplePokemonCommand ?? (_selectMultiplePokemonCommand = new DelegateCommand((selectedPokemon) => { - NavigationService.Navigate(typeof(EggDetailPage), selectedEgg.Id.ToString(), new SuppressNavigationTransitionInfo()); + // switch the selectionMode, so that additional pokemons can be selected by a simple tap + currentSelectionMode = SelectionMode.MultipleSelect; + + SelectPokemon(selectedPokemon); })); - #endregion + private void SelectPokemon(PokemonDataWrapper selectedPokemon) + { + if (SelectedPokemons.Contains(selectedPokemon)) + { + SelectedPokemons.Remove(selectedPokemon); - #endregion + // If no pokemons are left selected, close the selection mode + if (SelectedPokemons.Count == 0) + { + currentSelectionMode = SelectionMode.GoToDetail; + MultiplePokemonSelected?.Invoke(null, selectedPokemon); + } + } + else + { + if (selectedPokemon != null) + { + SelectedPokemons.Add(selectedPokemon); + + // When the first pokemon is selected, open selection mode + if (SelectedPokemons.Count == 1) + { + MultiplePokemonSelected?.Invoke(null, selectedPokemon); + } + } + } + RaisePropertyChanged(() => SelectedPokemons); + RaisePropertyChanged(() => SelectedPokemonCount); + } + + public string SelectedPokemonCount + { + get { return "(" + SelectedPokemons.Count.ToString() + ")"; } + } + + private DelegateCommand _closeMultipleSelectPokemonCommand; + public DelegateCommand CloseMultipleSelectPokemonCommand => _closeMultipleSelectPokemonCommand ?? (_closeMultipleSelectPokemonCommand = new DelegateCommand(() => + { + // Switch the selectionMode back to details + currentSelectionMode = SelectionMode.GoToDetail; + + // Clear the selection of any pokemons + SelectedPokemons.Clear(); + RaisePropertyChanged(() => SelectedPokemons); + RaisePropertyChanged(() => SelectedPokemonCount); + + // Close the multiple selection view + MultiplePokemonSelected?.Invoke(null, null); + })); + + private DelegateCommand _transferMultiplePokemonCommand; + public DelegateCommand TransferMultiplePokemonCommand => _transferMultiplePokemonCommand ?? (_transferMultiplePokemonCommand = new DelegateCommand(() => + { + // build a list of PokemonIds and transfer them + ulong[] pokemonIds = new ulong[SelectedPokemons.Count]; + + int i = 0; + foreach (PokemonDataWrapper pokemon in SelectedPokemons) + { + // Catch if the Pokémon is a Favorite or Buddy, transferring is not permitted in these cases + // TODO: This isn't a MessageDialog in the original apps, implement error style (Shell needed) + if (Convert.ToBoolean(pokemon.Favorite)) + { + var cannotTransferDialog = new PoGoMessageDialog(Resources.CodeResources.GetString("CannotTransferFavorite"), "") + { + CoverBackground = true, + AnimationType = PoGoMessageDialogAnimation.Bottom + }; + cannotTransferDialog.Show(); + return; + } + if (Convert.ToBoolean(pokemon.IsBuddy)) + { + var cannotTransferDialog = new PoGoMessageDialog(Resources.CodeResources.GetString("CannotTransferBuddy"), "") + { + CoverBackground = true, + AnimationType = PoGoMessageDialogAnimation.Bottom + }; + cannotTransferDialog.Show(); + return; + } + + pokemonIds[i] = pokemon.Id; + i++; + } + + // Ask for confirmation before moving the Pokemons + var dialog = + new PoGoMessageDialog( + string.Format(Resources.CodeResources.GetString("TransferMultiplePokemonWarningTitle"), SelectedPokemons.Count), + Resources.CodeResources.GetString("TransferMultiplePokemonWarningText")) + { + AcceptText = Resources.CodeResources.GetString("YesText"), + CancelText = Resources.CodeResources.GetString("NoText"), + CoverBackground = true, + AnimationType = PoGoMessageDialogAnimation.Bottom + }; + + dialog.AcceptInvoked += async (sender, e) => + { + // User confirmed transfer + try + { + ServerRequestRunning = true; + var pokemonTransferResponse = await GameClient.TransferPokemons(pokemonIds); + + switch (pokemonTransferResponse.Result) + { + case ReleasePokemonResponse.Types.Result.Unset: + break; + case ReleasePokemonResponse.Types.Result.Success: + // Remove the transferred pokemons from the inventory on screen + foreach (PokemonDataWrapper transferredPokemon in SelectedPokemons) + { + PokemonInventory.Remove(transferredPokemon); + } + RaisePropertyChanged(() => PokemonInventory); + + // TODO: Implement message informing about success of transfer (Shell needed) + await GameClient.UpdateInventory(); + await GameClient.UpdatePlayerStats(); + + // Reset to default screen + CloseMultipleSelectPokemonCommand.Execute(); + break; + + case ReleasePokemonResponse.Types.Result.PokemonDeployed: + break; + case ReleasePokemonResponse.Types.Result.Failed: + break; + case ReleasePokemonResponse.Types.Result.ErrorPokemonIsEgg: + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + finally + { + ServerRequestRunning = false; + } + + }; + + dialog.Show(); + + }, () => !ServerRequestRunning)); } -} + + #endregion + + #endregion +} \ No newline at end of file diff --git a/PokemonGo-UWP/Views/PokehashKeyPage.xaml b/PokemonGo-UWP/Views/PokehashKeyPage.xaml index c753b0fe3..c6a3dca3f 100644 --- a/PokemonGo-UWP/Views/PokehashKeyPage.xaml +++ b/PokemonGo-UWP/Views/PokehashKeyPage.xaml @@ -58,7 +58,7 @@ diff --git a/PokemonGo-UWP/Views/Pokemon/PokemonInventoryControl.xaml b/PokemonGo-UWP/Views/Pokemon/PokemonInventoryControl.xaml index fa065f540..76425111e 100644 --- a/PokemonGo-UWP/Views/Pokemon/PokemonInventoryControl.xaml +++ b/PokemonGo-UWP/Views/Pokemon/PokemonInventoryControl.xaml @@ -10,8 +10,190 @@ xmlns:interactivity="using:Microsoft.Xaml.Interactivity" xmlns:media="using:Microsoft.Xaml.Interactions.Media" xmlns:entities="using:PokemonGo_UWP.Entities" + xmlns:utils="using:PokemonGo_UWP.Utils" xmlns:controls="using:Template10.Controls"> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -82,12 +264,16 @@ ItemsSource="{Binding ElementName=Root, Path=PokemonInventory, Mode=OneWay}" ScrollViewer.VerticalScrollMode="Auto" ScrollViewer.VerticalScrollBarVisibility="Hidden" - SelectionMode="None" - IsItemClickEnabled="True"> + SelectionMode="Multiple" + IsMultiSelectCheckBoxEnabled="False"> - + + InputConverter="{StaticResource TappedRoutedEventArgsToClickedItemConverter}" /> + + + @@ -111,100 +297,11 @@ HorizontalAlignment="Center" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + SELECT POKÉMON + + + + diff --git a/PokemonGo-UWP/Views/Pokemon/PokemonInventoryPage.xaml.cs b/PokemonGo-UWP/Views/Pokemon/PokemonInventoryPage.xaml.cs index 34703b8ff..f482b6e60 100644 --- a/PokemonGo-UWP/Views/Pokemon/PokemonInventoryPage.xaml.cs +++ b/PokemonGo-UWP/Views/Pokemon/PokemonInventoryPage.xaml.cs @@ -19,12 +19,17 @@ protected override void OnNavigatedTo(NavigationEventArgs e) { base.OnNavigatedTo(e); ViewModel.ScrollPokemonToVisibleRequired += ScrollPokemonToVisible; + ViewModel.MultiplePokemonSelected += ViewModel_MultiplePokemonSelected; + + // Hide the multiple select panel + HideMultipleSelectStoryboard.Begin(); } protected override void OnNavigatingFrom(NavigatingCancelEventArgs e) { base.OnNavigatingFrom(e); ViewModel.ScrollPokemonToVisibleRequired -= ScrollPokemonToVisible; + ViewModel.MultiplePokemonSelected -= ViewModel_MultiplePokemonSelected; } #endregion @@ -34,5 +39,22 @@ private void ScrollPokemonToVisible(PokemonDataWrapper p) PokemonInventory.PokemonInventoryGridView.ScrollIntoView(p); } + private void ViewModel_MultiplePokemonSelected(object sender, PokemonDataWrapper e) + { + // Show or hide the 'Transfer multiple' button at the bottom of the screen + if (ViewModel.SelectedPokemons.Count > 0) + { + PokemonInventory.SelectPokemon(e); + ShowMultipleSelectStoryboard.Begin(); + PokemonInventory.ShowHideSortingButton(false); + } + else + { + PokemonInventory.ClearSelectedPokemons(); + HideMultipleSelectStoryboard.Begin(); + PokemonInventory.ShowHideSortingButton(true); + } + } + } } \ No newline at end of file diff --git a/PokemonGoAPI/Rpc/Inventory.cs b/PokemonGoAPI/Rpc/Inventory.cs index 2f27431ed..ed6fb755f 100644 --- a/PokemonGoAPI/Rpc/Inventory.cs +++ b/PokemonGoAPI/Rpc/Inventory.cs @@ -22,6 +22,16 @@ public async Task TransferPokemon(ulong pokemonId) return await PostProtoPayload(RequestType.ReleasePokemon, message); } + public async Task TransferPokemons(ulong[] pokemonIds) + { + var message = new ReleasePokemonMessage + { + PokemonIds = { pokemonIds } + }; + + return await PostProtoPayload(RequestType.ReleasePokemon, message); + } + public async Task EvolvePokemon(ulong pokemonId) { var message = new EvolvePokemonMessage diff --git a/version.json b/version.json index c62c06e29..94d50a8dc 100644 --- a/version.json +++ b/version.json @@ -4,8 +4,8 @@ "seed1": 1189692920, "version_number": 4500, "latest_release": { - "version": "1.2.3", - "setup_file": "https://github.com/mtaheij/PoGo-UWP/releases/download/V1.2.3-beta/PokemonGo-UWP_1.2.3.0_{arch}.appxbundle", + "version": "1.2.4", + "setup_file": "https://github.com/mtaheij/PoGo-UWP/releases/download/v1.2.4-beta/PokemonGo-UWP_1.2.4.0_{arch}.appxbundle", "dependencies": [ ] }