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.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
@ -26,7 +26,8 @@ namespace TINK.Model.Bikes
/// <param name="bikesAll"> Object holding bikes info from copri to update from. Holds station id but not station name.</param>
/// <param name="stations"> All stations to get station names from.</param>
/// <param name="p_oDateTimeProvider">Provices date time information.</param>
public void Update(IEnumerable<BikeInfo> bikesAll,
public void Update(
IEnumerable<BikeInfo> bikesAll,
IEnumerable<IStation> stations)
{
// Get list of current bikes by state(s) to update.
@ -54,7 +55,9 @@ namespace TINK.Model.Bikes
}
// Update bike.
GetById(bikeInfo.Id).State.Load(bikeInfo.State);
var updateTarget = GetById(bikeInfo.Id);
updateTarget.State.Load(bikeInfo.State);
updateTarget.DataSource = bikeInfo.DataSource;
if (bikesToBeRemoved.Contains<string>(bikeInfo.Id))
{
@ -109,11 +112,7 @@ namespace TINK.Model.Bikes
/// <param name="id"></param>
/// <returns></returns>
public BikeInfoMutable GetById(string id)
{
{
return this.FirstOrDefault(bike => bike.Id == id);
}
}
=> this.FirstOrDefault(bike => bike.Id == id);
/// <summary>
/// Deteermines whether a bike by given key exists.
@ -121,9 +120,7 @@ namespace TINK.Model.Bikes
/// <param name="p_strKey">Key to check.</param>
/// <returns>True if bike exists.</returns>
public bool ContainsKey(string id)
{
return GetById(id) != null;
}
=> GetById(id) != null;
/// <summary>
/// Removes a bike by its id.
@ -150,9 +147,9 @@ namespace TINK.Model.Bikes
BikeInfo bikeInfo,
string stationName)
{
if (bikeInfo is Bikes.BikeInfoNS.BluetoothLock.BikeInfo btBikeInfo)
if (bikeInfo is BikeInfoNS.BluetoothLock.BikeInfo btBikeInfo)
{
return new Bikes.BikeInfoNS.BluetoothLock.BikeInfoMutable(btBikeInfo, stationName);
return new BikeInfoNS.BluetoothLock.BikeInfoMutable(btBikeInfo, stationName);
}
else if (bikeInfo is BikeInfoNS.CopriLock.BikeInfo copriBikeInfo)
{

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using TINK.Model.Bikes.BikeInfoNS.BikeNS;
using TINK.Model.Bikes.BikeInfoNS.DriveNS;
@ -17,18 +17,23 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
/// <summary>
/// Holds the bike object.
/// </summary>
public BikeNS.Bike Bike { get; }
public Bike Bike { get; }
/// <summary>
/// Holds the drive object.
/// </summary>
public Drive Drive { get; }
/// <summary> Gets the information where the data origins from. </summary>
public DataSource DataSource { get; }
/// <summary> Constructs a bike object.</summary>
/// <param name="dataSource">Specified the source of the data.</param>
protected BikeInfo(
IStateInfo stateInfo,
BikeNS.Bike bike,
Bike bike,
Drive drive,
DataSource dataSource,
bool? isDemo = DEFAULTVALUEISDEMO,
IEnumerable<string> group = null,
string stationId = null,
@ -37,6 +42,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
{
Bike = bike ?? throw new ArgumentNullException(nameof(bike));
Drive = drive ?? throw new ArgumentNullException(nameof(drive));
DataSource = dataSource;
_StateInfo = stateInfo;
IsDemo = isDemo ?? DEFAULTVALUEISDEMO;
@ -50,6 +56,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
bikeInfo != null ? bikeInfo?.State : throw new ArgumentNullException(nameof(bikeInfo)),
bikeInfo.Bike,
bikeInfo.Drive,
bikeInfo.DataSource,
bikeInfo.IsDemo,
bikeInfo.Group,
bikeInfo.StationId,
@ -60,12 +67,14 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
/// <summary>
/// Constructs a bike info object for a available bike.
/// </summary>
/// <param name="dataSource">Specified the source of the data.</param>
/// <param name="stationId">Id of station where bike is located.</param>
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
/// <param name="tariffDescription">Hold tariff description of bike.</param>
public BikeInfo(
BikeNS.Bike bike,
Bike bike,
Drive drive,
DataSource dataSource,
string stationId,
Uri operatorUri = null,
RentalDescription tariffDescription = null,
@ -74,6 +83,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
new StateInfo(),
bike,
drive,
dataSource,
isDemo,
group,
stationId,
@ -85,6 +95,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
/// <summary>
/// Constructs a bike info object for a booked bike.
/// </summary>
/// <param name="dataSource">Specified the source of the data.</param>
/// <param name="dateTimeProvider">Provider for current date time to calculate remainig time on demand for state of type reserved.</param>
/// <param name="currentStationId">Name of station where bike is located, null if bike is on the road.</param>
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
@ -93,8 +104,9 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
/// <param name="mailAddress">Mail address of user which booked bike.</param>
/// <param name="code">Booking code.</param>
public BikeInfo(
BikeNS.Bike bike,
Bike bike,
Drive drive,
DataSource dataSource,
bool? isDemo,
IEnumerable<string> group,
string currentStationId,
@ -109,6 +121,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
code),
bike,
drive,
dataSource,
isDemo,
group,
currentStationId,
@ -131,7 +144,6 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
/// <summary> Holds description about the tarif. </summary>
public RentalDescription TariffDescription { get; }
/// Holds the rent state of the bike.
/// </summary>
public IStateInfo State

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.Serialization;
@ -12,7 +12,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
public class BikeInfoMutable : IBikeInfoMutable, INotifyPropertyChanged
{
/// <summary> Holds the bike. </summary>
private readonly BikeNS.Bike _Bike;
private readonly Bike _Bike;
/// <summary> Holds the drive of the bike. </summary>
private readonly Drive _Drive;
@ -30,8 +30,9 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
/// <param name="tariffDescription">Hold tariff description of bike.</param>
/// <param name="stateInfo">Bike state info.</param>
protected BikeInfoMutable(
BikeNS.Bike bike,
Bike bike,
Drive drive,
DataSource dataSource,
bool isDemo = BikeInfo.DEFAULTVALUEISDEMO,
IEnumerable<string> group = null,
string stationId = null,
@ -45,6 +46,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
Group = group;
_Bike = bike;
_Drive = drive;
DataSource = dataSource;
_StateInfo = new StateInfoMutable(dateTimeProvider, stateInfo);
_StateInfo.PropertyChanged += (sender, eventargs) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(eventargs.PropertyName));
StationId = stationId;
@ -59,6 +61,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
? bike.Bike
: throw new ArgumentNullException(nameof(bike)),
bike.Drive,
bike.DataSource,
bike.IsDemo,
bike.Group,
bike.StationId,
@ -121,6 +124,22 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
private DataSource _DataSource = DataSource.Copri;
/// <summary> Gets or sets the information where the data origins from. </summary>
public DataSource DataSource
{
get => _DataSource;
set
{
if (_DataSource == value)
return;
_DataSource = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(DataSource)));
}
}
/// <summary>
/// Converts the instance to text.
/// </summary>

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using TINK.Model.Bikes.BikeInfoNS.DriveNS;
using TINK.Model.State;
@ -20,6 +20,9 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
/// </summary>
Drive Drive { get; }
/// <summary> Gets or sets the information where the data origins from. </summary>
DataSource DataSource { get; }
/// <summary> True if bike is a demo bike. </summary>
bool IsDemo { get; }
@ -44,4 +47,19 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
/// </summary>
IStateInfo State { get; }
}
/// <summary>
/// Origin of the data.
/// </summary>
public enum DataSource
{
/// <summary>
/// Data source corpi.
/// </summary>
Copri,
/// <summary>
/// Data source cache.
/// </summary>
Cache
}
}

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using TINK.Model.Bikes.BikeInfoNS.BikeNS;
@ -56,6 +56,9 @@ namespace TINK.Model.Bikes.BikeInfoNS.BC
/// </summary>
Drive Drive { get; }
/// <summary> Gets or sets the information where the data origins from. </summary>
DataSource DataSource { get; set; }
event PropertyChangedEventHandler PropertyChanged;
}

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using TINK.Model.Bikes.BikeInfoNS.BC;
using TINK.Model.Bikes.BikeInfoNS.BikeNS;
using TINK.Model.Bikes.BikeInfoNS.DriveNS;
using TINK.Model.State;
@ -11,6 +12,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock
/// <summary>
/// Constructs a bike info object for a available bike.
/// </summary>
/// <param name="dataSource">Specified the source of the data.</param>
/// <param name="lockId">Id of the lock.</param>
/// <param name="lockGuid">GUID specifying the lock.</param>
/// <param name="currentStationId">Id of station where bike is located.</param>
@ -19,6 +21,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock
public BikeInfo(
Bike bike,
Drive drive,
DataSource dataSource,
int lockId,
Guid lockGuid,
string currentStationId,
@ -36,6 +39,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock
bike.Description)
: throw new ArgumentNullException(nameof(bike)),
drive,
dataSource,
isDemo,
group,
currentStationId,
@ -48,6 +52,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock
/// <summary>
/// Constructs a bike info object for a requested bike.
/// </summary>
/// <param name="dataSource">Specified the source of the data.</param>
/// <param name="dateTimeProvider">Provider for current date time to calculate remainig time on demand for state of type reserved.</param>
/// <param name="lockId">Id of the lock.</param>
/// <param name="lockGuid">GUID specifying the lock.</param>
@ -60,6 +65,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock
public BikeInfo(
Bike bike,
Drive drive,
DataSource dataSource,
int lockId,
Guid lockGuid,
byte[] userKey,
@ -87,6 +93,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock
bike.Description)
: throw new ArgumentNullException(nameof(bike)),
drive,
dataSource,
isDemo,
group,
currentStationId,
@ -99,7 +106,8 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock
/// <summary>
/// Constructs a bike info object for a booked bike.
/// </summary>
/// <param name="id">Unique id of bike.</param>
/// <param name="bike">Unique id of bike.</param>
/// <param name="dataSource">Specified the source of the data.</param>
/// <param name="lockId">Id of the lock.</param>
/// <param name="lockGuid">GUID specifying the lock.</param>
/// <param name="bookedAt">Date time when bike was booked</param>
@ -111,6 +119,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock
public BikeInfo(
Bike bike,
Drive drive,
DataSource dataSource,
int lockId,
Guid lockGuid,
byte[] userKey,
@ -136,6 +145,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock
bike.Description)
: throw new ArgumentNullException(),
drive,
dataSource,
isDemo,
group,
currentStationId,

View file

@ -1,8 +1,8 @@
using System;
using System;
namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock
{
public class BikeInfoMutable : Model.Bikes.BikeInfoNS.BC.BikeInfoMutable, IBikeInfoMutable
public class BikeInfoMutable : BC.BikeInfoMutable, IBikeInfoMutable
{
/// <summary> Constructs a bike object from source. </summary>
public BikeInfoMutable(BikeInfo bike, string stationName) : base(
@ -10,6 +10,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.BluetoothLock
? bike.Bike
: throw new ArgumentNullException(nameof(bike)),
bike.Drive,
bike.DataSource,
bike.IsDemo,
bike.Group,
bike.StationId,

View file

@ -1,5 +1,6 @@
using System;
using System;
using System.Collections.Generic;
using TINK.Model.Bikes.BikeInfoNS.BC;
using TINK.Model.Bikes.BikeInfoNS.BikeNS;
using TINK.Model.Bikes.BikeInfoNS.DriveNS;
using TINK.Model.MiniSurvey;
@ -13,14 +14,16 @@ namespace TINK.Model.Bikes.BikeInfoNS.CopriLock
/// Constructs a bike info object for a available bike or bike for which feed back is pending.
/// </summary>
/// <param name="bike">Bike object.</param>
/// <param name="dataSource">Specified the source of the data.</param>
/// <param name="currentStationId">Id of station where bike is located.</param>
/// <param name="lockInfo">Lock info.</param>
/// <param name="isFeedbackPending">If true user has not yet given feedback after returning bike.</param>
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
/// <param name="tariffDescription">Hold tariff description of bike.</param>
public BikeInfo(
BikeNS.Bike bike,
Bike bike,
Drive drive,
DataSource dataSource,
string currentStationId,
LockInfo lockInfo,
bool isFeedbackPending = false,
@ -40,6 +43,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.CopriLock
bike.Description)
: throw new ArgumentNullException(nameof(bike)),
drive,
dataSource,
isDemo,
group,
currentStationId,
@ -55,6 +59,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.CopriLock
/// Constructs a bike info object for a requested bike.
/// </summary>
/// <param name="bike">Bike object.</param>
/// <param name="dataSource">Specified the source of the data.</param>
/// <param name="requestedAt">Date time when bike was requested</param>
/// <param name="mailAddress">Mail address of user which requested bike.</param>
/// <param name="currentStationId">Name of station where bike is located, null if bike is on the road.</param>
@ -63,8 +68,9 @@ namespace TINK.Model.Bikes.BikeInfoNS.CopriLock
/// <param name="tariffDescription">Hold tariff description of bike.</param>
/// <param name="dateTimeProvider">Provider for current date time to calculate remainig time on demand for state of type reserved.</param>
public BikeInfo(
BikeNS.Bike bike,
Bike bike,
Drive drive,
DataSource dataSource,
DateTime requestedAt,
string mailAddress,
string currentStationId,
@ -80,7 +86,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.CopriLock
mailAddress,
""),
bike != null
? new BikeNS.Bike(
? new Bike(
bike.Id,
LockModel.Sigo /* Ensure consistend lock model value */,
bike.WheelType,
@ -88,6 +94,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.CopriLock
bike.Description)
: throw new ArgumentNullException(nameof(bike)),
drive,
dataSource,
isDemo,
group,
currentStationId,
@ -103,6 +110,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.CopriLock
/// Constructs a bike info object for a booked bike.
/// </summary>
/// <param name="bike">Bike object.</param>
/// <param name="dataSource">Specified the source of the data.</param>
/// <param name="bookedAt">Date time when bike was booked</param>
/// <param name="mailAddress">Mail address of user which booked bike.</param>
/// <param name="currentStationId">Name of station where bike is located, null if bike is on the road.</param>
@ -110,8 +118,9 @@ namespace TINK.Model.Bikes.BikeInfoNS.CopriLock
/// <param name="operatorUri">Holds the uri of the operator or null, in case of single operator setup.</param>
/// <param name="tariffDescription">Hold tariff description of bike.</param>
public BikeInfo(
BikeNS.Bike bike,
Bike bike,
Drive drive,
DataSource dataSource,
DateTime bookedAt,
string mailAddress,
string currentStationId,
@ -125,7 +134,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.CopriLock
mailAddress,
""),
bike != null
? new BikeNS.Bike(
? new Bike(
bike.Id,
LockModel.Sigo /* Ensure consistend lock model value */,
bike.WheelType,
@ -133,6 +142,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.CopriLock
bike.Description)
: throw new ArgumentNullException(nameof(bike)),
drive,
dataSource,
isDemo,
group,
currentStationId,
@ -143,7 +153,6 @@ namespace TINK.Model.Bikes.BikeInfoNS.CopriLock
MiniSurvey = new MiniSurveyModel();
Co2Saving = string.Empty;
}
public BikeInfo(BC.BikeInfo bikeInfo, LockInfo lockInfo) : base(
bikeInfo ?? throw new ArgumentException($"Can not copy-construct {typeof(BikeInfo).Name}-object. Source bike info must not be null."))
{

View file

@ -1,4 +1,4 @@
using System;
using System;
namespace TINK.Model.Bikes.BikeInfoNS.CopriLock
{
@ -10,6 +10,7 @@ namespace TINK.Model.Bikes.BikeInfoNS.CopriLock
? bike.Bike
: throw new ArgumentNullException(nameof(bike)),
bike.Drive,
bike.DataSource,
bike.IsDemo,
bike.Group,
bike.StationId,

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Serilog;
@ -11,9 +11,10 @@ using BikeInfo = TINK.Model.Bikes.BikeInfoNS.BC.BikeInfo;
namespace TINK.Model.Connector
{
/// <summary> Provides query functionality for use without log in. </summary>
public class CachedQuery : Base, IQuery
{
/// <summary> Cached copri server. </summary>
/// <summary> Cached copri server (connection to copri backed up by cache). </summary>
private readonly ICachedCopriServer server;
/// <summary>Constructs a copri query object.</summary>
@ -40,7 +41,7 @@ namespace TINK.Model.Connector
resultStations.Source,
new StationsAndBikesContainer(
resultStations.Response.GetStationsAllMutable(),
(await server.GetBikesAvailable(true)).Response.GetBikesAvailable()),
(await server.GetBikesAvailable(true)).Response.GetBikesAvailable(Bikes.BikeInfoNS.BC.DataSource.Cache)),
resultStations.GeneralData,
resultStations.Exception);
}
@ -53,7 +54,7 @@ namespace TINK.Model.Connector
resultBikes.Source,
new StationsAndBikesContainer(
(await server.GetStations(true)).Response.GetStationsAllMutable(),
resultBikes.Response.GetBikesAvailable()),
resultBikes.Response.GetBikesAvailable(Bikes.BikeInfoNS.BC.DataSource.Cache)),
resultBikes.GeneralData,
resultBikes.Exception);
}
@ -64,7 +65,7 @@ namespace TINK.Model.Connector
return new Result<StationsAndBikesContainer>(
resultStations.Source,
new StationsAndBikesContainer(resultStations.Response.GetStationsAllMutable(), resultBikes.Response.GetBikesAvailable()),
new StationsAndBikesContainer(resultStations.Response.GetStationsAllMutable(), resultBikes.Response.GetBikesAvailable(Bikes.BikeInfoNS.BC.DataSource.Copri)),
resultStations.GeneralData);
}
@ -73,6 +74,7 @@ namespace TINK.Model.Connector
public async Task<Result<BikeCollection>> GetBikesOccupiedAsync()
{
Log.ForContext<CachedQuery>().Error("Unexpected call to get be bikes occpied detected. No user is logged in.");
return new Result<BikeCollection>(
typeof(CopriCallsMonkeyStore),
await Task.FromResult(new BikeCollection(new Dictionary<string, BikeInfo>())),
@ -85,8 +87,20 @@ namespace TINK.Model.Connector
public async Task<Result<BikeCollection>> GetBikesAsync()
{
var result = await server.GetBikesAvailable();
server.AddToCache(result);
return new Result<BikeCollection>(result.Source, result.Response.GetBikesAvailable(), result.GeneralData, result.Exception);
if (result.Source != typeof(CopriCallsMonkeyStore))
{
server.AddToCache(result);
}
return new Result<BikeCollection>(
result.Source,
result.Response.GetBikesAvailable(result.Source == typeof(CopriCallsMonkeyStore)
? Bikes.BikeInfoNS.BC.DataSource.Cache
: Bikes.BikeInfoNS.BC.DataSource.Copri),
result.GeneralData,
result.Exception);
}
}
}

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Linq;
using System.Threading.Tasks;
using Serilog;
@ -12,7 +12,7 @@ namespace TINK.Model.Connector
/// <summary> Provides query functionality for a logged in user. </summary>
public class CachedQueryLoggedIn : BaseLoggedIn, IQuery
{
/// <summary> Cached copri server. </summary>
/// <summary> Cached copri server (connection to copri backed up by cache). </summary>
private ICachedCopriServer Server { get; }
/// <summary>Constructs a copri query object.</summary>
@ -42,11 +42,12 @@ namespace TINK.Model.Connector
stationsResponse.Source,
new StationsAndBikesContainer(
stationsResponse.Response.GetStationsAllMutable(),
UpdaterJSON.GetBikesAll(
BikeCollectionFactory.GetBikesAll(
(await Server.GetBikesAvailable(true)).Response?.bikes?.Values,
(await Server.GetBikesOccupied(true)).Response?.bikes_occupied?.Values,
Mail,
DateTimeProvider)),
DateTimeProvider,
Bikes.BikeInfoNS.BC.DataSource.Cache)),
stationsResponse.GeneralData,
stationsResponse.Exception);
}
@ -60,10 +61,11 @@ namespace TINK.Model.Connector
bikesAvailableResponse.Source,
new StationsAndBikesContainer(
(await Server.GetStations(true)).Response.GetStationsAllMutable(),
UpdaterJSON.GetBikesAll(bikesAvailableResponse.Response?.bikes?.Values,
BikeCollectionFactory.GetBikesAll(bikesAvailableResponse.Response?.bikes?.Values,
(await Server.GetBikesOccupied(true)).Response?.bikes_occupied?.Values,
Mail,
DateTimeProvider)),
DateTimeProvider,
Bikes.BikeInfoNS.BC.DataSource.Cache)),
bikesAvailableResponse.GeneralData,
bikesAvailableResponse.Exception);
}
@ -77,11 +79,12 @@ namespace TINK.Model.Connector
bikesOccupiedResponse.Source,
new StationsAndBikesContainer(
(await Server.GetStations(true)).Response.GetStationsAllMutable(),
UpdaterJSON.GetBikesAll(
BikeCollectionFactory.GetBikesAll(
(await Server.GetBikesAvailable(true)).Response?.bikes?.Values,
bikesOccupiedResponse.Response?.bikes_occupied?.Values,
Mail,
DateTimeProvider)),
DateTimeProvider,
Bikes.BikeInfoNS.BC.DataSource.Cache)),
bikesOccupiedResponse.GeneralData,
bikesOccupiedResponse.Exception);
}
@ -94,11 +97,12 @@ namespace TINK.Model.Connector
var exceptions = new[] { stationsResponse?.Exception, bikesAvailableResponse?.Exception, bikesOccupiedResponse?.Exception }.Where(x => x != null).ToArray();
var stationsMutable = stationsResponse.Response.GetStationsAllMutable();
var bikesMutable = UpdaterJSON.GetBikesAll(
var bikesMutable = BikeCollectionFactory.GetBikesAll(
bikesAvailableResponse.Response?.bikes?.Values,
bikesOccupiedResponse.Response?.bikes_occupied?.Values,
Mail,
DateTimeProvider);
DateTimeProvider,
Bikes.BikeInfoNS.BC.DataSource.Copri);
return new Result<StationsAndBikesContainer>(
stationsResponse.Source,
@ -119,11 +123,12 @@ namespace TINK.Model.Connector
Log.ForContext<CachedQueryLoggedIn>().Debug("Bikes available read from cache. Reading bikes occupied from cache as well.");
return new Result<BikeCollection>(
bikesAvailableResponse.Source,
UpdaterJSON.GetBikesAll(
BikeCollectionFactory.GetBikesAll(
bikesAvailableResponse.Response?.bikes?.Values?.Where(bike => bike.GetState() == State.InUseStateEnum.FeedbackPending),
(await Server.GetBikesOccupied(true))?.Response?.bikes_occupied?.Values,
Mail,
DateTimeProvider),
DateTimeProvider,
Bikes.BikeInfoNS.BC.DataSource.Cache),
bikesAvailableResponse.GeneralData,
bikesAvailableResponse.Exception);
}
@ -136,11 +141,12 @@ namespace TINK.Model.Connector
Log.ForContext<CachedQueryLoggedIn>().Debug("Bikes occupied read from cache. Reread bikes available from cache as well.");
return new Result<BikeCollection>(
bikesOccupiedResponse.Source,
UpdaterJSON.GetBikesAll(
BikeCollectionFactory.GetBikesAll(
(await Server.GetBikesAvailable(true)).Response?.bikes?.Values?.Where(bike => bike.GetState() == State.InUseStateEnum.FeedbackPending),
bikesOccupiedResponse.Response?.bikes_occupied?.Values,
Mail,
DateTimeProvider),
DateTimeProvider,
Bikes.BikeInfoNS.BC.DataSource.Cache),
bikesOccupiedResponse.GeneralData,
bikesOccupiedResponse.Exception);
}
@ -151,11 +157,12 @@ namespace TINK.Model.Connector
return new Result<BikeCollection>(
bikesOccupiedResponse.Source,
UpdaterJSON.GetBikesAll(
BikeCollectionFactory.GetBikesAll(
bikesAvailableResponse?.Response.bikes?.Values?.Select(bike => bike)?.Where(bike => bike.GetState() == State.InUseStateEnum.FeedbackPending),
bikesOccupiedResponse?.Response?.bikes_occupied?.Values,
Mail,
DateTimeProvider),
DateTimeProvider,
Bikes.BikeInfoNS.BC.DataSource.Copri),
bikesOccupiedResponse.GeneralData,
bikesOccupiedResponse.Exception);
}
@ -173,11 +180,12 @@ namespace TINK.Model.Connector
Log.ForContext<CachedQueryLoggedIn>().Debug("Bikes available read from cache. Reading bikes occupied from cache as well.");
return new Result<BikeCollection>(
bikesAvailableResponse.Source,
UpdaterJSON.GetBikesAll(
BikeCollectionFactory.GetBikesAll(
bikesAvailableResponse.Response?.bikes?.Values,
(await Server.GetBikesOccupied(true)).Response?.bikes_occupied?.Values,
Mail,
DateTimeProvider),
DateTimeProvider,
Bikes.BikeInfoNS.BC.DataSource.Cache),
bikesAvailableResponse.GeneralData,
bikesAvailableResponse.Exception);
}
@ -190,11 +198,12 @@ namespace TINK.Model.Connector
Log.ForContext<CachedQueryLoggedIn>().Debug("Bikes occupied read from cache. Reread bikes available from cache as well.");
return new Result<BikeCollection>(
bikesOccupiedResponse.Source,
UpdaterJSON.GetBikesAll(
BikeCollectionFactory.GetBikesAll(
(await Server.GetBikesAvailable(true)).Response?.bikes?.Values,
bikesOccupiedResponse.Response?.bikes_occupied?.Values,
Mail,
DateTimeProvider),
DateTimeProvider,
Bikes.BikeInfoNS.BC.DataSource.Cache),
bikesOccupiedResponse.GeneralData,
bikesOccupiedResponse.Exception);
}
@ -206,11 +215,12 @@ namespace TINK.Model.Connector
Log.ForContext<CachedQueryLoggedIn>().Debug("Bikes available and occupied read successfully from server.");
return new Result<BikeCollection>(
bikesAvailableResponse.Source,
UpdaterJSON.GetBikesAll(
BikeCollectionFactory.GetBikesAll(
bikesAvailableResponse.Response?.bikes?.Values,
bikesOccupiedResponse.Response?.bikes_occupied?.Values,
Mail,
DateTimeProvider),
DateTimeProvider,
Bikes.BikeInfoNS.BC.DataSource.Copri),
bikesAvailableResponse.GeneralData,
bikesAvailableResponse.Exception != null || bikesOccupiedResponse.Exception != null ? new AggregateException(new[] { bikesAvailableResponse.Exception, bikesOccupiedResponse.Exception }) : null);
}

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Serilog;
@ -11,20 +11,20 @@ using BikeInfo = TINK.Model.Bikes.BikeInfoNS.BC.BikeInfo;
namespace TINK.Model.Connector
{
/// <summary> Provides query functionality without login. </summary>
/// <summary> Provides query functionality from cache without login. </summary>
public class Query : Base, IQuery
{
/// <summary> Cached copri server. </summary>
private readonly ICopriServer server;
/// <summary>Constructs a copri query object.</summary>
/// <param name="p_oCopriServer">Server which implements communication.</param>
public Query(ICopriServerBase p_oCopriServer) : base(p_oCopriServer)
/// <param name="copriServer">Server which implements communication.</param>
public Query(ICopriServerBase copriServer) : base(copriServer)
{
server = p_oCopriServer as ICopriServer;
server = copriServer as ICopriServer;
if (server == null)
{
throw new ArgumentException($"Copri server is not of expected typ. Type detected is {p_oCopriServer.GetType()}.");
throw new ArgumentException($"Copri server is not of expected typ. Type detected is {copriServer.GetType()}.");
}
}
@ -36,7 +36,7 @@ namespace TINK.Model.Connector
return new Result<StationsAndBikesContainer>(
typeof(CopriCallsMonkeyStore),
new StationsAndBikesContainer(stationsAllResponse.GetStationsAllMutable(), bikesAvailableResponse.GetBikesAvailable()),
new StationsAndBikesContainer(stationsAllResponse.GetStationsAllMutable(), bikesAvailableResponse.GetBikesAvailable(Bikes.BikeInfoNS.BC.DataSource.Cache)),
stationsAllResponse.GetGeneralData());
}
@ -59,7 +59,7 @@ namespace TINK.Model.Connector
var bikesAvailableResponse = await server.GetBikesAvailableAsync();
return new Result<BikeCollection>(
typeof(CopriCallsMonkeyStore),
bikesAvailableResponse.GetBikesAvailable(),
bikesAvailableResponse.GetBikesAvailable(Bikes.BikeInfoNS.BC.DataSource.Cache),
bikesAvailableResponse.GetGeneralData());
}
}

View file

@ -1,4 +1,4 @@
using System;
using System;
using System.Linq;
using System.Threading.Tasks;
using TINK.Model.Bikes;
@ -8,7 +8,7 @@ using TINK.Repository;
namespace TINK.Model.Connector
{
/// <summary> Provides query functionality for a logged in user. </summary>
/// <summary> Provides query functionality from cache for a logged in user. </summary>
public class QueryLoggedIn : BaseLoggedIn, IQuery
{
/// <summary> Copri server. </summary>
@ -41,11 +41,12 @@ namespace TINK.Model.Connector
typeof(CopriCallsMonkeyStore),
new StationsAndBikesContainer(
stationResponse.GetStationsAllMutable(),
UpdaterJSON.GetBikesAll(
BikeCollectionFactory.GetBikesAll(
bikesAvailableResponse?.bikes?.Values,
bikesOccupiedResponse?.bikes_occupied?.Values,
Mail,
DateTimeProvider)),
DateTimeProvider,
Bikes.BikeInfoNS.BC.DataSource.Cache)),
stationResponse.GetGeneralData());
}
@ -58,11 +59,12 @@ namespace TINK.Model.Connector
return new Result<BikeCollection>(
typeof(CopriCallsMonkeyStore),
UpdaterJSON.GetBikesAll(
BikeCollectionFactory.GetBikesAll(
bikesFeedbackRequired.bikes?.Values?.Select(bike => bike)?.Where(bike => bike.GetState() == State.InUseStateEnum.FeedbackPending),
bikesOccupiedResponse?.bikes_occupied?.Values,
Mail,
DateTimeProvider),
DateTimeProvider,
Bikes.BikeInfoNS.BC.DataSource.Cache),
bikesOccupiedResponse.GetGeneralData());
}
@ -75,11 +77,12 @@ namespace TINK.Model.Connector
return new Result<BikeCollection>(
typeof(CopriCallsMonkeyStore),
UpdaterJSON.GetBikesAll(
BikeCollectionFactory.GetBikesAll(
bikesAvailableResponse?.bikes?.Values,
bikesOccupiedResponse?.bikes_occupied?.Values,
Mail,
DateTimeProvider),
DateTimeProvider,
Bikes.BikeInfoNS.BC.DataSource.Cache),
bikesAvailableResponse.GetGeneralData());
}
}

View file

@ -0,0 +1,129 @@
using System;
using System.Collections.Generic;
using System.Text;
using TINK.Model.Bikes.BikeInfoNS.BC;
using TINK.Model.Bikes;
using TINK.Repository.Response;
using Serilog;
namespace TINK.Model.Connector.Updater
{
public static class BikeCollectionFactory
{
/// <summary> Gets bikes available from copri server response.</summary>
/// <param name="bikesAvailableResponse">Response to create collection from.</param>
/// <param name="dataSource">Specified the data source</param>
/// <returns>New collection of available bikes.</returns>
public static BikeCollection GetBikesAvailable(
this BikesAvailableResponse bikesAvailableResponse,
DataSource dataSource)
=> GetBikesAll(
bikesAvailableResponse?.bikes?.Values,
new BikesReservedOccupiedResponse()?.bikes_occupied?.Values, // There are no occupied bikes.
string.Empty,
() => DateTime.Now,
dataSource);
/// <summary> Gets bikes occupied from copri server response. </summary>
/// <param name="bikesOccupiedResponse">Response to create bikes from.</param>
/// <param name="dataSource">Specified the data source</param>
/// <returns>New collection of occupied bikes.</returns>
public static BikeCollection GetBikesOccupied(
this BikesReservedOccupiedResponse bikesOccupiedResponse,
string mail,
Func<DateTime> dateTimeProvider,
DataSource dataSource)
=> GetBikesAll(
new BikesAvailableResponse()?.bikes?.Values,
bikesOccupiedResponse?.bikes_occupied?.Values,
mail,
dateTimeProvider,
dataSource);
/// <summary> Gets bikes occupied from copri server response. </summary>
/// <param name="bikesAvailable">Response to create bikes available from.</param>
/// <param name="bikesOccupied">Response to create bikes occupied from.</param>
/// <returns>New collection of occupied bikes.</returns>
public static BikeCollection GetBikesAll(
IEnumerable<BikeInfoAvailable> bikesAvailable,
IEnumerable<BikeInfoReservedOrBooked> bikesOccupied,
string mail,
Func<DateTime> dateTimeProvider,
DataSource dataSource)
{
var bikesDictionary = new Dictionary<string, BikeInfo>();
var duplicates = new Dictionary<string, BikeInfo>();
// Get bikes from Copri/ file/ memory, ....
if (bikesAvailable != null)
{
foreach (var bikeInfoResponse in bikesAvailable)
{
var bikeInfo = BikeInfoFactory.Create(bikeInfoResponse, dataSource);
if (bikeInfo == null)
{
// Response is not valid.
continue;
}
if (bikesDictionary.ContainsKey(bikeInfo.Id))
{
// Duplicates are not allowed.
Log.Error($"Duplicate bike with id {bikeInfo.Id} detected evaluating bikes available. Bike status is {bikeInfo.State.Value}.");
if (!duplicates.ContainsKey(bikeInfo.Id))
{
duplicates.Add(bikeInfo.Id, bikeInfo);
}
continue;
}
bikesDictionary.Add(bikeInfo.Id, bikeInfo);
}
}
// Get bikes from Copri/ file/ memory, ....
if (bikesOccupied != null)
{
foreach (var bikeInfoResponse in bikesOccupied)
{
BikeInfo bikeInfo = BikeInfoFactory.Create(
bikeInfoResponse,
mail,
dateTimeProvider,
dataSource);
if (bikeInfo == null)
{
continue;
}
if (bikesDictionary.ContainsKey(bikeInfo.Id))
{
// Duplicates are not allowed.
Log.Error($"Duplicate bike with id {bikeInfo.Id} detected evaluating bikes occupied. Bike status is {bikeInfo.State.Value}.");
if (!duplicates.ContainsKey(bikeInfo.Id))
{
duplicates.Add(bikeInfo.Id, bikeInfo);
}
continue;
}
bikesDictionary.Add(bikeInfo.Id, bikeInfo);
}
}
// Remove entries which are not unique.
foreach (var l_oDuplicate in duplicates)
{
bikesDictionary.Remove(l_oDuplicate.Key);
}
return new BikeCollection(bikesDictionary);
}
}
}

View file

@ -1,6 +1,7 @@
using System;
using System;
using System.Collections.Generic;
using Serilog;
using TINK.Model.Bikes.BikeInfoNS.BC;
using TINK.Model.Bikes.BikeInfoNS.BikeNS;
using TINK.Model.MiniSurvey;
using TINK.Model.State;
@ -20,7 +21,10 @@ namespace TINK.Model.Connector.Updater
/// <summary> Creates a bike info object from copri response. </summary>
/// <param name="bikeInfo">Copri response for a disposable bike. </param>
public static BikeInfo Create(BikeInfoAvailable bikeInfo)
/// <param name="dataSource">Specifies the data source.</param>
public static BikeInfo Create(
BikeInfoAvailable bikeInfo,
DataSource dataSource)
{
if (bikeInfo == null) throw new ArgumentNullException(nameof(bikeInfo));
@ -74,6 +78,7 @@ namespace TINK.Model.Connector.Updater
bikeInfo.GetTypeOfBike(),
bikeInfo.description),
DriveFactory.Create(bikeInfo?.bike_type),
dataSource,
bikeInfo.station,
new Bikes.BikeInfoNS.CopriLock.LockInfo.Builder { State = bikeInfo.GetCopriLockingState() }.Build(),
bikeInfo.GetState() == InUseStateEnum.FeedbackPending,
@ -103,6 +108,7 @@ namespace TINK.Model.Connector.Updater
bikeInfo.GetTypeOfBike(),
bikeInfo.description),
DriveFactory.Create(bikeInfo?.bike_type),
dataSource,
bikeInfo.GetBluetoothLockId(),
bikeInfo.GetBluetoothLockGuid(),
bikeInfo.station,
@ -133,10 +139,12 @@ namespace TINK.Model.Connector.Updater
/// <param name="bikeInfo">Copri response. </param>
/// <param name="mailAddress">Mail address of user.</param>
/// <param name="dateTimeProvider">Date and time provider function.</param>
/// <param name="dataSource">Specified the source of the data.</param>
public static BikeInfo Create(
BikeInfoReservedOrBooked bikeInfo,
string mailAddress,
Func<DateTime> dateTimeProvider)
Func<DateTime> dateTimeProvider,
DataSource dataSource)
{
if (bikeInfo == null) throw new ArgumentNullException(nameof(bikeInfo));
@ -178,6 +186,7 @@ namespace TINK.Model.Connector.Updater
bikeInfo.GetTypeOfBike(),
bikeInfo.description),
DriveFactory.Create(bikeInfo?.bike_type),
dataSource,
lockSerial,
lockGuid,
bikeInfo.GetUserKey(),
@ -207,6 +216,7 @@ namespace TINK.Model.Connector.Updater
bikeInfo.GetTypeOfBike(),
bikeInfo.description),
DriveFactory.Create(bikeInfo?.bike_type),
dataSource,
bikeInfo.GetFrom(),
mailAddress,
bikeInfo.station,
@ -247,6 +257,7 @@ namespace TINK.Model.Connector.Updater
bikeInfo.GetTypeOfBike(),
bikeInfo.description),
DriveFactory.Create(bikeInfo?.bike_type),
dataSource,
lockSerial,
bikeInfo.GetBluetoothLockGuid(),
bikeInfo.GetUserKey(),
@ -275,6 +286,7 @@ namespace TINK.Model.Connector.Updater
bikeInfo.GetTypeOfBike(),
bikeInfo.description),
DriveFactory.Create(bikeInfo?.bike_type),
dataSource,
bikeInfo.GetIsDemo(),
bikeInfo.GetGroup(),
bikeInfo.station,
@ -298,6 +310,7 @@ namespace TINK.Model.Connector.Updater
bikeInfo.GetTypeOfBike(),
bikeInfo.description),
DriveFactory.Create(bikeInfo?.bike_type),
DataSource.Copri,
bikeInfo.GetFrom(),
mailAddress,
bikeInfo.station,

View file

@ -1,7 +1,8 @@
using System;
using System;
using System.Collections.Generic;
using Serilog;
using TINK.Model.Bikes;
using TINK.Model.Bikes.BikeInfoNS.BC;
using TINK.Model.State;
using TINK.Model.Station;
using TINK.Model.Station.Operator;
@ -26,11 +27,8 @@ namespace TINK.Model.Connector.Updater
/// <param name="notifyLevel">Controls whether notify property changed events are fired or not.</param>
public static void Load(
this IBikeInfoMutable bike,
Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel notifyLevel)
{
bike.State.Load(InUseStateEnum.Disposable, notifyLevel: notifyLevel);
}
NotifyPropertyChangedLevel notifyLevel)
=> bike.State.Load(InUseStateEnum.Disposable, notifyLevel: notifyLevel);
/// <summary>
/// Gets all statsion for station provider and add them into station list.
@ -126,7 +124,7 @@ namespace TINK.Model.Connector.Updater
this IBikeInfoMutable bike,
BikeInfoReservedOrBooked bikeInfo,
string mailAddress,
Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel notifyLevel = Bikes.BikeInfoNS.BC.NotifyPropertyChangedLevel.All)
NotifyPropertyChangedLevel notifyLevel = NotifyPropertyChangedLevel.All)
{
if (bike is Bikes.BikeInfoNS.BluetoothLock.BikeInfoMutable btBikeInfo)
{
@ -170,113 +168,5 @@ namespace TINK.Model.Connector.Updater
}
}
/// <summary> Gets bikes available from copri server response.</summary>
/// <param name="bikesAvailableResponse">Response to create collection from.</param>
/// <returns>New collection of available bikes.</returns>
public static BikeCollection GetBikesAvailable(
this BikesAvailableResponse bikesAvailableResponse)
=> GetBikesAll(
bikesAvailableResponse?.bikes?.Values,
new BikesReservedOccupiedResponse()?.bikes_occupied?.Values, // There are no occupied bikes.
string.Empty,
() => DateTime.Now);
/// <summary> Gets bikes occupied from copri server response. </summary>
/// <param name="p_oBikesAvailable">Response to create bikes from.</param>
/// <returns>New collection of occupied bikes.</returns>
public static BikeCollection GetBikesOccupied(
this BikesReservedOccupiedResponse bikesOccupiedResponse,
string mail,
Func<DateTime> dateTimeProvider)
{
return GetBikesAll(
new BikesAvailableResponse()?.bikes?.Values,
bikesOccupiedResponse?.bikes_occupied?.Values,
mail,
dateTimeProvider);
}
/// <summary> Gets bikes occupied from copri server response. </summary>
/// <param name="bikesAvailable">Response to create bikes available from.</param>
/// <param name="bikesOccupied">Response to create bikes occupied from.</param>
/// <returns>New collection of occupied bikes.</returns>
public static BikeCollection GetBikesAll(
IEnumerable<BikeInfoAvailable> bikesAvailable,
IEnumerable<BikeInfoReservedOrBooked> bikesOccupied,
string mail,
Func<DateTime> dateTimeProvider)
{
var bikesDictionary = new Dictionary<string, BikeInfo>();
var duplicates = new Dictionary<string, BikeInfo>();
// Get bikes from Copri/ file/ memory, ....
if (bikesAvailable != null)
{
foreach (var bikeInfoResponse in bikesAvailable)
{
var bikeInfo = BikeInfoFactory.Create(bikeInfoResponse);
if (bikeInfo == null)
{
// Response is not valid.
continue;
}
if (bikesDictionary.ContainsKey(bikeInfo.Id))
{
// Duplicates are not allowed.
Log.Error($"Duplicate bike with id {bikeInfo.Id} detected evaluating bikes available. Bike status is {bikeInfo.State.Value}.");
if (!duplicates.ContainsKey(bikeInfo.Id))
{
duplicates.Add(bikeInfo.Id, bikeInfo);
}
continue;
}
bikesDictionary.Add(bikeInfo.Id, bikeInfo);
}
}
// Get bikes from Copri/ file/ memory, ....
if (bikesOccupied != null)
{
foreach (var bikeInfoResponse in bikesOccupied)
{
BikeInfo bikeInfo = BikeInfoFactory.Create(
bikeInfoResponse,
mail,
dateTimeProvider);
if (bikeInfo == null)
{
continue;
}
if (bikesDictionary.ContainsKey(bikeInfo.Id))
{
// Duplicates are not allowed.
Log.Error($"Duplicate bike with id {bikeInfo.Id} detected evaluating bikes occupied. Bike status is {bikeInfo.State.Value}.");
if (!duplicates.ContainsKey(bikeInfo.Id))
{
duplicates.Add(bikeInfo.Id, bikeInfo);
}
continue;
}
bikesDictionary.Add(bikeInfo.Id, bikeInfo);
}
}
// Remove entries which are not unique.
foreach (var l_oDuplicate in duplicates)
{
bikesDictionary.Remove(l_oDuplicate.Key);
}
return new BikeCollection(bikesDictionary);
}
}
}

View file

@ -671,6 +671,11 @@ namespace TINK.Model
{
new Version(3, 0, 356),
AppResources.ChangeLog3_0_231
},
{
new Version(3, 0, 357),
AppResources.ChangeLog_3_0_357_MK_SB,
new List<AppFlavor> { AppFlavor.MeinKonrad, AppFlavor.ShareeBike }
},
};

View file

@ -747,6 +747,15 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to You can only start or end a rent with network reception. Turn on mobile data or wifi and drag the page downward with the finger to refresh the page..
/// </summary>
public static string ChangeLog_3_0_357_MK_SB {
get {
return ResourceManager.GetString("ChangeLog_3_0_357_MK_SB", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to We have fixed some bugs. Enjoy the ride!.
/// </summary>
@ -765,6 +774,15 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Minor improvements..
/// </summary>
public static string ChangeLog_MinorImprovements {
get {
return ResourceManager.GetString("ChangeLog_MinorImprovements", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Software packages were updated..
/// </summary>
@ -1840,6 +1858,15 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to There are currently no bicycles available at this station..
/// </summary>
public static string MarkingBikesAtStationNoBikesAvailable {
get {
return ResourceManager.GetString("MarkingBikesAtStationNoBikesAvailable", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Station id: {0}.
/// </summary>
@ -1930,6 +1957,15 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to No network, data is outdated..
/// </summary>
public static string MarkingDataIsFromCache {
get {
return ResourceManager.GetString("MarkingDataIsFromCache", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Charging level: {0}/{1} bars.
/// </summary>
@ -2147,6 +2183,15 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to There are currently no bicycles reserved/booked on user {0}..
/// </summary>
public static string MarkingMyBikesNoBikesReservedRented {
get {
return ResourceManager.GetString("MarkingMyBikesNoBikesReservedRented", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Bike is ok.
/// </summary>
@ -2821,6 +2866,15 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Information.
/// </summary>
public static string MessageTitleInformation {
get {
return ResourceManager.GetString("MessageTitleInformation", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Warning.
/// </summary>

View file

@ -1114,4 +1114,22 @@ Probieren Sie es aus!</value>
<data name="ChangeLog_3_0_355_MK_SB_iOS" xml:space="preserve">
<value>Bluetooth-Kommunikation verbessert.</value>
</data>
<data name="MarkingDataIsFromCache" xml:space="preserve">
<value>Kein Netz, Daten sind veraltet.</value>
</data>
<data name="MarkingBikesAtStationNoBikesAvailable" xml:space="preserve">
<value>Momentan sind keine Fahrräder an dieser Station verfügbar.</value>
</data>
<data name="MarkingMyBikesNoBikesReservedRented" xml:space="preserve">
<value>Momentan sind keine Fahrräder auf Benutzer {0} reserviert/ gebucht.</value>
</data>
<data name="MessageTitleInformation" xml:space="preserve">
<value>Information</value>
</data>
<data name="ChangeLog_3_0_357_MK_SB" xml:space="preserve">
<value>Sie können eine Miete nur bei Netzempfang beginnen oder beenden. Schalten Sie mobile Daten oder WLAN ein und ziehen Sie die Seite mit dem Finger nach unten, um Ihre Ansicht zu aktualisieren.</value>
</data>
<data name="ChangeLog_MinorImprovements" xml:space="preserve">
<value>Kleine Verbesserungen.</value>
</data>
</root>

View file

@ -1204,4 +1204,22 @@ Try it out!</value>
<data name="ChangeLog_3_0_355_MK_SB_iOS" xml:space="preserve">
<value>Bluetooth communication improved.</value>
</data>
<data name="MarkingDataIsFromCache" xml:space="preserve">
<value>No network, data is outdated.</value>
</data>
<data name="MarkingBikesAtStationNoBikesAvailable" xml:space="preserve">
<value>There are currently no bicycles available at this station.</value>
</data>
<data name="MarkingMyBikesNoBikesReservedRented" xml:space="preserve">
<value>There are currently no bicycles reserved/booked on user {0}.</value>
</data>
<data name="MessageTitleInformation" xml:space="preserve">
<value>Information</value>
</data>
<data name="ChangeLog_3_0_357_MK_SB" xml:space="preserve">
<value>You can only start or end a rent with network reception. Turn on mobile data or wifi and drag the page downward with the finger to refresh the page.</value>
</data>
<data name="ChangeLog_MinorImprovements" xml:space="preserve">
<value>Minor improvements.</value>
</data>
</root>

View file

@ -1524,6 +1524,30 @@ Probieren Sie es aus!</target>
<source>Bluetooth communication improved.</source>
<target state="translated">Bluetooth-Kommunikation verbessert.</target>
</trans-unit>
<trans-unit id="MarkingDataIsFromCache" translate="yes" xml:space="preserve">
<source>No network, data is outdated.</source>
<target state="translated">Kein Netz, Daten sind veraltet.</target>
</trans-unit>
<trans-unit id="MarkingBikesAtStationNoBikesAvailable" translate="yes" xml:space="preserve">
<source>There are currently no bicycles available at this station.</source>
<target state="translated">Momentan sind keine Fahrräder an dieser Station verfügbar.</target>
</trans-unit>
<trans-unit id="MarkingMyBikesNoBikesReservedRented" translate="yes" xml:space="preserve">
<source>There are currently no bicycles reserved/booked on user {0}.</source>
<target state="translated">Momentan sind keine Fahrräder auf Benutzer {0} reserviert/ gebucht.</target>
</trans-unit>
<trans-unit id="MessageTitleInformation" translate="yes" xml:space="preserve">
<source>Information</source>
<target state="translated">Information</target>
</trans-unit>
<trans-unit id="ChangeLog_3_0_357_MK_SB" translate="yes" xml:space="preserve">
<source>You can only start or end a rent with network reception. Turn on mobile data or wifi and drag the page downward with the finger to refresh the page.</source>
<target state="translated">Sie können eine Miete nur bei Netzempfang beginnen oder beenden. Schalten Sie mobile Daten oder WLAN ein und ziehen Sie die Seite mit dem Finger nach unten, um Ihre Ansicht zu aktualisieren.</target>
</trans-unit>
<trans-unit id="ChangeLog_MinorImprovements" translate="yes" xml:space="preserve">
<source>Minor improvements.</source>
<target state="translated">Kleine Verbesserungen.</target>
</trans-unit>
</group>
</body>
</file>

View file

@ -12,7 +12,7 @@ namespace TINK.Repository.Request
public class RequestBuilder : IRequestBuilder
{
/// <summary> Constructs a object for building requests. </summary>
/// <param name="merchantId"></param>
/// <param name="merchantId">Holds the id denoting the merchant.</param>
/// <param name="uiIsoLangugageName">Two letter ISO language name.</param>
public RequestBuilder(
string merchantId,
@ -22,12 +22,12 @@ namespace TINK.Repository.Request
? merchantId
: throw new ArgumentException("Merchant id must not be null.", nameof(merchantId));
UiIsoLanguageNameParameter = RequestBuilderHelper.GetLanguageParameter(uiIsoLangugageName);
UiIsoLanguageNameParameter = RequestBuilderHelper.GetLanguageParameter(WebUtility.UrlEncode(uiIsoLangugageName));
AuthCookieParameter = $"&authcookie={MerchantId}";
AuthCookieParameter = $"&authcookie={WebUtility.UrlEncode(MerchantId)}";
}
/// <summary> Parameter specifying current ui language. </summary>
/// <summary>Holds the id denoting the merchant.</summary>
public string MerchantId { get; }
/// <summary> Holds the session cookie if a user is logged in. </summary>
@ -52,7 +52,7 @@ namespace TINK.Repository.Request
$"&merchant_id={MerchantId}" +
$"&user_id={WebUtility.UrlEncode(mailAddress)}" +
$"&user_pw={WebUtility.UrlEncode(password)}" +
$"&hw_id={deviceId}" +
$"&hw_id={WebUtility.UrlEncode(deviceId)}" +
UiIsoLanguageNameParameter;
/// <summary> Logs user out. </summary>

View file

@ -15,7 +15,7 @@ namespace TINK.Repository.Request
public class RequestBuilderLoggedIn : IRequestBuilder
{
/// <summary> Constructs a object for building requests. </summary>
/// <param name="merchantId"></param>
/// <param name="merchantId">Holds the id denoting the merchant.</param>
/// <param name="uiIsoLangugageName">Two letter ISO language name.</param>
public RequestBuilderLoggedIn(
string merchantId,
@ -30,12 +30,12 @@ namespace TINK.Repository.Request
? sessionCookie
: throw new ArgumentException("Session cookie must not be null.", nameof(sessionCookie));
UiIsoLanguageNameParameter = RequestBuilderHelper.GetLanguageParameter(uiIsoLangugageName);
UiIsoLanguageNameParameter = RequestBuilderHelper.GetLanguageParameter(WebUtility.UrlEncode(uiIsoLangugageName));
AuthCookieParameter = $"&authcookie={SessionCookie}{MerchantId}";
AuthCookieParameter = $"&authcookie={WebUtility.UrlEncode(SessionCookie)}{WebUtility.UrlEncode(MerchantId)}";
}
/// <summary> Holds the id denoting the merchant (TINK app). </summary>
/// <summary> Holds the id denoting the merchant. </summary>
public string MerchantId { get; }
/// <summary> Holds the session cookie if a user is logged in. </summary>
@ -268,25 +268,42 @@ namespace TINK.Repository.Request
UiIsoLanguageNameParameter;
}
/// <summary>
/// Gets bike id parameter.
/// </summary>
/// <param name="bikeId">Id of bike.</param>
/// <returns>bike id parameter in a format which is urlencode invariant.</returns>
private static string GetBikeIdParameter(string bikeId)
=> $"&bike={bikeId}";
=> $"&bike={WebUtility.UrlEncode(bikeId)}";
/// <summary>
/// Gets parameter holding lock state or null if state is unknown.
/// </summary>
/// <param name="lockState">Null or state of lock.</param>
/// <returns>Lock state in a format which is urlencode invariant.</returns>
private static string GetLockStateParameter(lock_state? lockState)
=> lockState.HasValue ? $"&lock_state={lockState}" : string.Empty;
/// <summary>
/// Gets the battery level percentage parameter if percentage is not NaN an empty string otherwise.
/// </summary>
/// <returns>Gets battery percentage parameters in a format which is urlencode invariant or an empty string if percentage percentge is NaN.</returns>
private static string GetBatteryPercentageParameters(double batteryPercentage) => !double.IsNaN(batteryPercentage)
? $"&voltage={batteryPercentage.ToString(CultureInfo.InvariantCulture)}"
: string.Empty;
/// <summary>
/// Gets the version info parameter or an empty string if version info is not available.
/// </summary>
/// <param name="versionInfo">Lock version info.</param>
/// <returns>Version info in a format which is urlencode invariant or empty. </returns>
private static string GetVersionInfoParameter(IVersionInfo versionInfo) => versionInfo?.FirmwareVersion > 0 || versionInfo?.HardwareVersion > 0 || versionInfo?.LockVersion > 0
? $"&firmware=HW%20{versionInfo.HardwareVersion}%3BFW%20{versionInfo.FirmwareVersion}%3BLock%20{versionInfo.LockVersion}"
: string.Empty;
/// <summary> Gets the geolocation parameter. </summary>
/// <param name="geolocation">Geolocation or null.</param>
/// <returns>Empty string if geoloction is null otherwise parmeter including latitude, longitude and age in a format which is urlencode invariant.</returns>
private static string GetLocationParameters(LocationDto geolocation)
{
if (geolocation == null)
@ -299,14 +316,14 @@ namespace TINK.Repository.Request
}
/// <summary> Gets the geolocation parameter. </summary>
/// <param name="geolocation">Geolocation or null.</param>
/// <returns>in a format which is urlencode invariant.</returns>
private static string GetSmartDeviceParameters(ISmartDevice smartDevice)
=> smartDevice != null
? $"{(!string.IsNullOrEmpty(smartDevice.Manufacturer) ? $"&user_device_manufaturer={smartDevice.Manufacturer})" : string.Empty)}" +
$"{(!string.IsNullOrEmpty(smartDevice.Model) ? $"&user_device_model={smartDevice.Model}" : string.Empty)}" +
$"{(!string.IsNullOrEmpty(smartDevice.Platform.ToString()) ? $"&user_device_platform={smartDevice.Platform.ToString()}" : string.Empty)}" +
$"{(!string.IsNullOrEmpty(smartDevice.VersionText) ? $"&user_device_version={smartDevice.VersionText}" : string.Empty)}" +
$"{(!string.IsNullOrEmpty(smartDevice.Identifier) ? $"&user_device_id={smartDevice.Identifier}" : string.Empty)}"
? $"{(!string.IsNullOrEmpty(smartDevice.Manufacturer) ? $"&user_device_manufaturer={WebUtility.UrlEncode(smartDevice.Manufacturer)})" : string.Empty)}" +
$"{(!string.IsNullOrEmpty(smartDevice.Model) ? $"&user_device_model={WebUtility.UrlEncode(smartDevice.Model)}" : string.Empty)}" +
$"{(!string.IsNullOrEmpty(smartDevice.Platform.ToString()) ? $"&user_device_platform={WebUtility.UrlEncode(smartDevice.Platform.ToString())}" : string.Empty)}" +
$"{(!string.IsNullOrEmpty(smartDevice.VersionText) ? $"&user_device_version={WebUtility.UrlEncode(smartDevice.VersionText)}" : string.Empty)}" +
$"{(!string.IsNullOrEmpty(smartDevice.Identifier) ? $"&user_device_id={WebUtility.UrlEncode(smartDevice.Identifier)}" : string.Empty)}"
: string.Empty;
/// <summary>

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");