Version 3.0.357

This commit is contained in:
Anja 2023-01-18 14:22:51 +01:00
parent 5980410182
commit 5c0b2e70c9
84 changed files with 1012 additions and 449 deletions

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.ComponentModel;
using TINK.Model.Connector;
using TINK.Model.Device;
@ -65,6 +65,8 @@ namespace TINK.ViewModel.Bikes.Bike.BC
viewService,
bikesViewModel,
ActiveUser);
selectedBike.PropertyChanged += (sender, ev) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsButtonVisible)));
}
/// <summary>
@ -94,7 +96,9 @@ namespace TINK.ViewModel.Bikes.Bike.BC
}
/// <summary> Gets visiblity of the copri command button. </summary>
public bool IsButtonVisible => RequestHandler.IsButtonVisible;
public bool IsButtonVisible
=> RequestHandler.IsButtonVisible
&& Bike.DataSource == Model.Bikes.BikeInfoNS.BC.DataSource.Copri /* do not show button if data is from cache */ ;
/// <summary> Gets the text of the copri command button. </summary>
public string ButtonText => RequestHandler.ButtonText;

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
@ -39,6 +39,12 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
string lastStateText = null,
Xamarin.Forms.Color? lastStateColor = null)
{
if (IsDataFromCache != IsDataFromCache)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsDataFromCache)));
}
if (lastHandler.ButtonText != ButtonText)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ButtonText)));
@ -121,6 +127,15 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
LockService = lockService
?? throw new ArgumentException($"Can not instantiate {this.GetType().Name}-object. Parameter {nameof(lockService)} can not be null.");
selectedBike.PropertyChanged += (sender, ev) =>
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsButtonVisible)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsLockitButtonVisible)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsDataFromCache)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(OnButtonClicked)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(OnLockitButtonClicked)));
};
}
/// <summary>
@ -147,22 +162,28 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock
}
/// <summary> Gets visiblity of the copri command button. </summary>
public bool IsButtonVisible => RequestHandler.IsButtonVisible;
public bool IsButtonVisible
=> RequestHandler.IsButtonVisible;
/// <summary> Gets the text of the copri command button. </summary>
public string ButtonText => RequestHandler.ButtonText;
/// <summary> Gets visiblity of the ILockIt command button. </summary>
public bool IsLockitButtonVisible => RequestHandler.IsLockitButtonVisible;
public bool IsLockitButtonVisible
=> RequestHandler.IsLockitButtonVisible;
/// <summary> Gets the text of the ILockIt command button. </summary>
public string LockitButtonText => RequestHandler.LockitButtonText;
/// <summary> True if Data is from Cache. </summary>
public bool IsDataFromCache
=> Bike.DataSource == Model.Bikes.BikeInfoNS.BC.DataSource.Cache;
/// <summary> Processes request to perform a copri action (reserve bike and cancel reservation). </summary>
public System.Windows.Input.ICommand OnButtonClicked => new Xamarin.Forms.Command(async () => await ClickButton(RequestHandler.HandleRequestOption1()));
public System.Windows.Input.ICommand OnButtonClicked => new Xamarin.Forms.Command(async () => await ClickButton(RequestHandler.HandleRequestOption1()), () => !IsDataFromCache);
/// <summary> Processes request to perform a ILockIt action (unlock bike and lock bike). </summary>
public System.Windows.Input.ICommand OnLockitButtonClicked => new Xamarin.Forms.Command(async () => await ClickButton(RequestHandler.HandleRequestOption2()));
public System.Windows.Input.ICommand OnLockitButtonClicked => new Xamarin.Forms.Command(async () => await ClickButton(RequestHandler.HandleRequestOption2()), () => !IsDataFromCache);
/// <summary> Processes request to perform a copri action (reserve bike and cancel reservation). </summary>
private async Task ClickButton(Task<IRequestHandler> handleRequest)

View file

@ -584,6 +584,7 @@ namespace TINK.ViewModel.Bikes.Bike.BluetoothLock.RequestHandler
AppResources.MessageAnswerOk);
}
// Update current state from exception
SelectedBike.LockInfo.State = exception is StateAwareException stateAwareException
? stateAwareException.State
: LockingState.UnknownDisconnected;

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
@ -107,6 +107,12 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock
selectedBike.State.Value,
viewService,
bikesViewModel);
selectedBike.PropertyChanged += (sender, ev) =>
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsButtonVisible)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsLockitButtonVisible)));
};
}
/// <summary>
@ -131,13 +137,17 @@ namespace TINK.ViewModel.Bikes.Bike.CopriLock
}
/// <summary> Gets visiblity of the copri command button. </summary>
public bool IsButtonVisible => RequestHandler.IsButtonVisible;
public bool IsButtonVisible
=> RequestHandler.IsButtonVisible
&& Bike.DataSource == Model.Bikes.BikeInfoNS.BC.DataSource.Copri /* do not show button if data is from cache */ ;
/// <summary> Gets the text of the copri command button. </summary>
public string ButtonText => RequestHandler.ButtonText;
/// <summary> Gets visiblity of the ILockIt command button. </summary>
public bool IsLockitButtonVisible => RequestHandler.IsLockitButtonVisible;
public bool IsLockitButtonVisible
=> RequestHandler.IsLockitButtonVisible
&& Bike.DataSource == Model.Bikes.BikeInfoNS.BC.DataSource.Copri /* do not show button if data is from cache */ ;
/// <summary> Gets the text of the ILockIt command button. </summary>
public string LockitButtonText => RequestHandler.LockitButtonText;

View file

@ -327,11 +327,11 @@ namespace TINK.ViewModel.Bikes
Log.ForContext<BikesViewModel>().Debug($"Switch value of {nameof(IsIdle)} to {value}.");
isIdle = value;
base.OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsIdle)));
base.OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsRunning)));
base.OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsProcessWithRunningProcessView)));
}
}
public bool IsRunning => !isIdle;
public bool IsProcessWithRunningProcessView => !isIdle;
/// <summary> Holds info about current action. </summary>
private string actionText;
@ -479,5 +479,6 @@ namespace TINK.ViewModel.Bikes
/// </summary>
public virtual async Task OnDisappearing()
=> await m_oViewUpdateManager.StopUpdatePeridically();
}
}

View file

@ -23,6 +23,7 @@ using TINK.Settings;
using TINK.View;
using TINK.ViewModel.Bikes;
using Xamarin.Forms;
using Command = Xamarin.Forms.Command;
namespace TINK.ViewModel.BikesAtStation
{
@ -36,6 +37,22 @@ namespace TINK.ViewModel.BikesAtStation
/// </summary>
private IStation Station { get; }
/// <summary>
/// True if ListView of Bikes is refreshing after user pulled;
/// </summary>
private bool _isRefreshing = false;
public bool IsRefreshing
{
get { return _isRefreshing; }
set
{
_isRefreshing = value;
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsRefreshing)));
}
}
public Command RefreshCommand { get; }
/// <summary>
/// Constructs bike collection view model.
/// </summary>
@ -75,11 +92,23 @@ namespace TINK.ViewModel.BikesAtStation
? string.Format(AppResources.MarkingBikesAtStationStationId, Station.Id)
: string.Empty;
/// <summary>
/// Holds what should be executed on pull to refresh
/// </summary>
RefreshCommand = new Command(async () => {
IsRefreshing = true;
await OnAppearing();
IsRefreshing = false;
});
CollectionChanged += (sender, eventargs) =>
{
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsNoBikesAtStationVisible)));
OnPropertyChanged(new PropertyChangedEventArgs(nameof(NoBikesAtStationText)));
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsLoginRequiredHintVisible)));
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsRefreshing)));
};
}
@ -131,7 +160,7 @@ namespace TINK.ViewModel.BikesAtStation
get
{
return IsNoBikesAtStationVisible
? $"Momentan sind keine Fahrräder an dieser Station verfügbar."
? AppResources.MarkingBikesAtStationNoBikesAvailable
: string.Empty;
}
}
@ -215,6 +244,8 @@ namespace TINK.ViewModel.BikesAtStation
/// </summary>
public async Task OnAppearing()
{
IsIdle = false;
Log.ForContext<BikesAtStationPageViewModel>().Information($"Bikes at station {Station.StationName} is appearing, either due to tap on a station or to app being shown again.");
ActionText = AppResources.ActivityTextOneMomentPlease;
@ -334,7 +365,7 @@ namespace TINK.ViewModel.BikesAtStation
}
/// <summary> Create task which updates my bike view model.</summary>
private void UpdateTask()
public void UpdateTask()
{
PostAction(
unused =>

View file

@ -271,7 +271,7 @@ namespace TINK.ViewModel.Contact
{
try
{
IsRunning = true;
IsProcessWithRunningProcessView = true;
// Process map page.
Log.ForContext<SelectStationPageViewModel>().Information(
@ -300,7 +300,7 @@ namespace TINK.ViewModel.Contact
// User decided to give access to locations permissions.
PermissionsService.OpenAppSettings();
ActionText = "";
IsRunning = false;
IsProcessWithRunningProcessView = false;
IsMapPageEnabled = true;
return;
}
@ -406,14 +406,14 @@ namespace TINK.ViewModel.Contact
Exception = resultStationsAndBikes.Exception;
ActionText = "";
IsRunning = false;
IsProcessWithRunningProcessView = false;
IsMapPageEnabled = true;
}
catch (Exception l_oException)
{
Log.ForContext<SelectStationPageViewModel>().Error($"An error occurred opening select station page.\r\n{l_oException.Message}");
IsRunning = false;
IsProcessWithRunningProcessView = false;
await ViewService.DisplayAlert(
"Fehler",
@ -582,22 +582,22 @@ namespace TINK.ViewModel.Contact
}
/// <summary> Used to block more than on copri requests at a given time.</summary>
private bool isRunning = false;
private bool isProcessWithRunningProcessView = false;
/// <summary>
/// True if any action can be performed (request and cancel request)
/// </summary>
public bool IsRunning
public bool IsProcessWithRunningProcessView
{
get => isRunning;
get => isProcessWithRunningProcessView;
set
{
if (value == isRunning)
if (value == isProcessWithRunningProcessView)
return;
Log.ForContext<SelectStationPageViewModel>().Debug($"Switch value of {nameof(isRunning)} to {value}.");
isRunning = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsRunning)));
Log.ForContext<SelectStationPageViewModel>().Debug($"Switch value of {nameof(isProcessWithRunningProcessView)} to {value}.");
isProcessWithRunningProcessView = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsProcessWithRunningProcessView)));
}
}

View file

@ -26,6 +26,7 @@ using TINK.Settings;
using TINK.View;
using TINK.ViewModel.Bikes;
using Xamarin.Forms;
using Command = Xamarin.Forms.Command;
namespace TINK.ViewModel.FindBike
{
@ -79,6 +80,22 @@ namespace TINK.ViewModel.FindBike
/// <summary> Holds the stations to get station names form station ids. </summary>
private IEnumerable<IStation> Stations { get; }
/// <summary>
/// True if ListView of Bikes is refreshing after user pulled;
/// </summary>
private bool _isRefreshing = false;
public bool IsRefreshing
{
get { return _isRefreshing; }
set
{
_isRefreshing = value;
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsRefreshing)));
}
}
public Command RefreshCommand { get; }
/// <summary>
/// Constructs bike collection view model in case information about occupied bikes is available.
/// </summary>
@ -118,6 +135,17 @@ namespace TINK.ViewModel.FindBike
};
Stations = stations ?? throw new ArgumentException(nameof(stations));
/// <summary>
/// Holds what should be executed on pull to refresh
/// </summary>
RefreshCommand = new Command(async () => {
IsRefreshing = true;
await OnAppearing();
IsRefreshing = false;
});
}
/// <summary>

View file

@ -313,7 +313,7 @@ namespace TINK.ViewModel.Map
var status = await PermissionsService.RequestAsync();
}
IsRunning = true;
IsProcessWithRunningProcessView = true;
IsNavBarVisible = false;
// Process map page.
Polling = TinkApp.Polling;
@ -332,11 +332,13 @@ namespace TINK.ViewModel.Map
TinkApp.Stations = resultStationsAndBikes.Response.StationsAll;
TinkApp.ResourceUrls = resultStationsAndBikes.GeneralData.ResourceUrls;
// Check if there is a message from COPRI ("merchant_message") to be shown to user.
if (!string.IsNullOrEmpty(resultStationsAndBikes?.GeneralData?.MerchantMessage)
&& !WasMerchantMessageAlreadyShown)
{
// Show COPRI message once.
await ViewService.DisplayAlert(
"Information",
AppResources.MessageTitleInformation,
resultStationsAndBikes.GeneralData.MerchantMessage,
AppResources.MessageAnswerOk);
WasMerchantMessageAlreadyShown = true;
@ -404,7 +406,7 @@ namespace TINK.ViewModel.Map
Exception = resultStationsAndBikes.Exception;
ActionText = "";
IsRunning = false;
IsProcessWithRunningProcessView = false;
IsNavBarVisible = true;
IsMapPageEnabled = true;
}
@ -412,7 +414,7 @@ namespace TINK.ViewModel.Map
{
Log.ForContext<MapPageViewModel>().Error($"An error occurred showing bike stations page.\r\n{l_oException.Message}");
IsRunning = false;
IsProcessWithRunningProcessView = false;
IsNavBarVisible = true;
await ViewService.DisplayAlert(
@ -541,7 +543,7 @@ namespace TINK.ViewModel.Map
// User decided to give access to locations permissions.
PermissionsService.OpenAppSettings();
ActionText = "";
IsRunning = false;
IsProcessWithRunningProcessView = false;
IsNavBarVisible = true;
IsMapPageEnabled = true;
}
@ -782,22 +784,22 @@ namespace TINK.ViewModel.Map
}
/// <summary> Used to block more than on copri requests at a given time.</summary>
private bool isRunning = false;
private bool isProcessWithRunningProcessView = false;
/// <summary>
/// True if any action can be performed (request and cancel request)
/// </summary>
public bool IsRunning
public bool IsProcessWithRunningProcessView
{
get => isRunning;
get => isProcessWithRunningProcessView;
set
{
if (value == isRunning)
if (value == isProcessWithRunningProcessView)
return;
Log.ForContext<MapPageViewModel>().Debug($"Switch value of {nameof(isRunning)} to {value}.");
isRunning = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsRunning)));
Log.ForContext<MapPageViewModel>().Debug($"Switch value of {nameof(isProcessWithRunningProcessView)} to {value}.");
isProcessWithRunningProcessView = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsProcessWithRunningProcessView)));
}
}
@ -900,7 +902,7 @@ namespace TINK.ViewModel.Map
try
{
IsMapPageEnabled = false;
IsRunning = true;
IsProcessWithRunningProcessView = true;
IsNavBarVisible = false;
Log.ForContext<MapPageViewModel>().Information($"Request to toggle to \"{selectedFilter}\".");
@ -954,7 +956,7 @@ namespace TINK.ViewModel.Map
}
ActionText = "";
IsRunning = false;
IsProcessWithRunningProcessView = false;
IsNavBarVisible = true;
IsMapPageEnabled = true;
Log.ForContext<MapPageViewModel>().Information($"Toggle to \"{selectedFilter}\" done.");
@ -963,7 +965,7 @@ namespace TINK.ViewModel.Map
{
Log.ForContext<MapPageViewModel>().Error("An error occurred switching view Cargobike/ Citybike.{}");
ActionText = "";
IsRunning = false;
IsProcessWithRunningProcessView = false;
IsNavBarVisible = true;
await ViewService.DisplayAlert(

View file

@ -23,6 +23,7 @@ using TINK.Settings;
using TINK.View;
using TINK.ViewModel.Bikes;
using Xamarin.Forms;
using Command = Xamarin.Forms.Command;
namespace TINK.ViewModel.MyBikes
{
@ -31,6 +32,20 @@ namespace TINK.ViewModel.MyBikes
/// <summary> Holds the stations to get station names form station ids. </summary>
private IEnumerable<IStation> Stations { get; }
/// <summary>
/// True if ListView of Bikes is refreshing after user pulled;
/// </summary>
private bool _isRefreshing = false;
public bool IsRefreshing
{
get { return _isRefreshing; }
set
{
_isRefreshing = value;
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsRefreshing)));
}
}
/// <summary>
/// Constructs bike collection view model in case information about occupied bikes is available.
/// </summary>
@ -71,8 +86,21 @@ namespace TINK.ViewModel.MyBikes
};
Stations = stations ?? throw new ArgumentException(nameof(stations));
/// <summary>
/// Holds what should be executed on pull to refresh
/// </summary>
RefreshCommand = new Command(async () => {
IsRefreshing = true;
await OnAppearing();
IsRefreshing = false;
});
}
public Command RefreshCommand { get; }
/// <summary> Returns if info about the fact that user did not request or book any bikes is visible or not.<summary>
/// Gets message that logged in user has not booked any bikes.
/// </summary>
@ -90,7 +118,7 @@ namespace TINK.ViewModel.MyBikes
get
{
return IsNoBikesOccupiedVisible
? $"Momentan sind keine Fahrräder auf Benutzer {ActiveUser?.Mail} reserviert/ gebucht."
? string.Format(AppResources.MarkingMyBikesNoBikesReservedRented, ActiveUser?.Mail)
: string.Empty;
}
}
@ -101,6 +129,8 @@ namespace TINK.ViewModel.MyBikes
/// </summary>
public async Task OnAppearing()
{
IsIdle = false;
// Get my bikes from COPRI
Log.ForContext<MyBikesPageViewModel>().Information("User request to show page MyBikes/ page re-appearing");