Contact page shows operator specific info

This commit is contained in:
Oliver Hauff 2021-07-20 23:06:09 +02:00
parent e436e83c1d
commit a58c33f005
51 changed files with 948 additions and 221 deletions

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="preferExternal" package="com.hauffware.sharee" android:versionName="3.0.240" android:versionCode="240">
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="preferExternal" package="com.hauffware.sharee" android:versionName="3.0.241" android:versionCode="241">
<uses-sdk android:minSdkVersion="18" android:targetSdkVersion="30" />
<!-- Google Maps related permissions -->
<permission android:name="com.ecs.google.maps.v2.actionbarsherlock.permission.MAPS_RECEIVE" android:protectionLevel="signature" />

View file

@ -49,8 +49,8 @@
<key>CFBundleDisplayName</key>
<string>sharee.bike</string>
<key>CFBundleVersion</key>
<string>240</string>
<string>241</string>
<key>CFBundleShortVersionString</key>
<string>3.0.240</string>
<string>3.0.241</string>
</dict>
</plist>

View file

@ -29,6 +29,7 @@
<DependentUpon>ILockItBike.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)View\BoolInverterConverter.cs" />
<Compile Include="$(MSBuildThisFileDirectory)View\FeedbackPopup.xaml.cs">
<DependentUpon>FeedbackPopup.xaml</DependentUpon>
<SubType>Code</SubType>

View file

@ -1,9 +1,10 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TINK.View.BikesAtStation.BikesAtStationPage"
xmlns:local_bike="clr-namespace:TINK.View.Bike"
Title="{Binding Title}">
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TINK.View.BikesAtStation.BikesAtStationPage"
xmlns:local_bike="clr-namespace:TINK.View.Bike"
xmlns:resources="clr-namespace:TINK.MultilingualResources;assembly=TINKLib"
Title="{Binding Title}">
<ContentPage.Resources>
<ResourceDictionary>
<local_bike:BikeViewCellTemplateSelector x:Key="bikeTemplateSelector"/>
@ -11,7 +12,8 @@
</ContentPage.Resources>
<ContentPage.Content>
<Frame>
<StackLayout>
<StackLayout
Orientation="Vertical">
<ListView
x:Name="BikesAtStationListView"
SelectionMode="None"
@ -19,21 +21,29 @@
IsEnabled="{Binding IsIdle}"
IsVisible="{Binding IsBikesListVisible}"
HasUnevenRows="True"
ItemTemplate="{StaticResource bikeTemplateSelector}"/>
<StackLayout
VerticalOptions="EndAndExpand"
Orientation="Horizontal">
<Label
ItemTemplate="{StaticResource bikeTemplateSelector}"/>
<Label
IsVisible="{Binding IsNoBikesAtStationVisible}"
VerticalOptions="EndAndExpand"
Text="{Binding NoBikesAtStationText}"/>
<Label
<Label
TextType="Html"
Text="{Binding ContactSupportHintText}">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding ContactSupportClickedCommand}"/>
</Label.GestureRecognizers>
</Label>
<Label
IsVisible="{Binding IsLoginRequiredHintVisible}"
FormattedText="{Binding LoginRequiredHintText}">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding LoginRequiredHintClickedCommand}"/>
</Label.GestureRecognizers>
</Label>
TextType="Html"
Text="{Binding LoginRequiredHintText}">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding LoginRequiredHintClickedCommand}"/>
</Label.GestureRecognizers>
</Label>
<StackLayout
VerticalOptions="EndAndExpand"
Orientation="Horizontal">
<Label
HeightRequest="20"
Text="{Binding StatusInfoText}"

View file

@ -0,0 +1,19 @@
using System;
using System.Globalization;
using Xamarin.Forms;
namespace TINK.View
{
/// <summary> Inverts a bool.</summary>
public class BoolInverterConverter : IValueConverter
{
/// <summary> Inverts a bool.</summary>
/// <param name="value">Bool to invert.</param>
/// <returns>Inverted bool.</returns>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
=> value is bool flag && !flag;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> value is bool flag && !flag;
}
}

View file

@ -1,27 +1,49 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:conv="clr-namespace:TINK.View"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:resources="clr-namespace:TINK.MultilingualResources;assembly=TINKLib"
x:Class="TINK.View.Contact.ContactPage"
Title="Kontakt">
x:Class="TINK.View.Contact.ContactPage"
Title="Kontakt">
<ContentPage.Resources>
<conv:StringNotNullOrEmptyToVisibleConverter x:Key="StringNotNullOrEmpty_Converter"/>
<conv:BoolInverterConverter x:Key="BoolInvert_Converter"/>
</ContentPage.Resources>
<ContentPage.Content>
<ScrollView>
<Frame>
<StackLayout x:Name="ContactPageView">
<Frame>
<StackLayout>
<Label FormattedText="{Binding MaliAddressAndMotivationsText}"/>
<Button Text="{Binding MailAddressText}"
IsEnabled="{Binding IsSendMailAvailable}"
Command="{Binding OnMailRequest}"/>
</StackLayout>
<Frame
IsVisible="{Binding
Path=IsOperatorInfoAvaliable,
Converter={StaticResource BoolInvert_Converter}}">
<Label
TextType="Html"
Text="{x:Static resources:AppResources.MarkingContactNoStationInfoAvailableNoButton}"/>
</Frame>
<Frame>
<Frame
IsVisible="{Binding IsOperatorInfoAvaliable}">
<StackLayout>
<Label FormattedText="{Binding PhoneContactText}"/>
<Button Text="{Binding PhoneNumberText}"
IsEnabled="{Binding IsDoPhoncallAvailable}"
Command="{Binding OnPhoneRequest}"/>
<!--- Mail address -->
<Label
IsVisible="{Binding MailAddressText, Converter={StaticResource StringNotNullOrEmpty_Converter}}"
FormattedText="{Binding MaliAddressAndMotivationsText}"/>
<Button
x:Name="MailAddressButton"
IsVisible="{Binding MailAddressText, Converter={StaticResource StringNotNullOrEmpty_Converter}}"
Text="{Binding MailAddressText}"
IsEnabled="{Binding IsSendMailAvailable}"
Command="{Binding OnMailRequest}"/>
<!--- Mail address -->
<Label
IsVisible="{Binding PhoneNumberText, Converter={StaticResource StringNotNullOrEmpty_Converter}}"
FormattedText="{Binding PhoneContactText}"/>
<Button
x:Name="PhoneNumberButton"
IsVisible="{Binding PhoneNumberText, Converter={StaticResource StringNotNullOrEmpty_Converter}}"
Text="{Binding PhoneNumberText}"
IsEnabled="{Binding IsDoPhoncallAvailable}"
Command="{Binding OnPhoneRequest}"/>
</StackLayout>
</Frame>
<Frame>

View file

@ -16,6 +16,7 @@ namespace TINK.View.Contact
InitializeComponent ();
ContactPageView.BindingContext = new ContactPageViewModel(
App.ModelRoot.SelectedStation,
App.ModelRoot.Uris.ActiveUri,
() => App.CreateAttachment(),
() => DependencyService.Get<IExternalBrowserService>().OpenUrl(DependencyService.Get<IAppInfo>().StoreUrl),

View file

@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TINK.Model.Station;
#if USEMASTERDETAIL || USEFLYOUT
using TINK.View.MasterDetail;
#endif
@ -58,9 +55,14 @@ namespace TINK.View.Root
/// </summary>
private void OnListViewItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var item = e.SelectedItem as RootPageFlyoutMenuItem;
if (item == null)
if (!(e.SelectedItem is RootPageFlyoutMenuItem item))
{
// Unexpected argument detected.
return;
}
// Set selected station to new
App.ModelRoot.SelectedStation = new NullStation();
ShowPage(item.TargetType, item.Title);

View file

@ -1,6 +1,4 @@

using System;
using System.Collections.Generic;
using TINK.ViewModel.RootShell;
using Xamarin.Forms;

View file

@ -1,11 +1,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using Xamarin.Forms;
using TINK.Model.User.Account;
using System.Linq;
using System.Reflection;
namespace TINK.View.Settings
{

View file

@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using Xamarin.Forms;
namespace TINK.View
@ -14,7 +12,7 @@ namespace TINK.View
/// <returns>Boolean value indicating whether object is visible or not.</returns>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (value != null && value is string text && !string.IsNullOrEmpty(text));
return value != null && value is string text && !string.IsNullOrEmpty(text);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)

View file

@ -44,7 +44,7 @@ namespace TINK.View
case ViewTypes.TabbedPageInfo:
return typeof(TabbedPageInfo);
case ViewTypes.TabbedPageHelpContact:
case ViewTypes.FeesAndBikesPage:
return typeof(FeesAndBikesPage);
case ViewTypes.ManageAccountPage:
@ -59,6 +59,9 @@ namespace TINK.View
case ViewTypes.BikesAtStation:
return typeof(BikesAtStationPage);
case ViewTypes.ContactPage:
return typeof(ContactPage);
default:
return typeof(ContentPage);
}

View file

@ -28,7 +28,7 @@ namespace TINK.Model.Connector
/// </summary>
/// <param name="p_oStationInfo">Object to get information from.</param>
/// <returns>Position information.</returns>
public static Station.Position GetPosition(this StationsAllResponse.StationInfo p_oStationInfo)
public static Station.Position GetPosition(this StationsAvailableResponse.StationInfo p_oStationInfo)
{
return GetPosition(p_oStationInfo.gps);
}
@ -74,7 +74,7 @@ namespace TINK.Model.Connector
/// <summary> Gets the position from StationInfo object. </summary>
/// <param name="p_oStationInfo">Object to get information from.</param>
/// <returns>Position information.</returns>
public static IEnumerable<string> GetGroup(this StationsAllResponse.StationInfo p_oStationInfo)
public static IEnumerable<string> GetGroup(this StationsAvailableResponse.StationInfo p_oStationInfo)
{
try
{

View file

@ -11,6 +11,8 @@ using Serilog;
using BikeInfo = TINK.Model.Bike.BC.BikeInfo;
using IBikeInfoMutable = TINK.Model.Bikes.Bike.BC.IBikeInfoMutable;
using System.Globalization;
using TINK.Model.Station.Operator;
using Xamarin.Forms;
namespace TINK.Model.Connector
{
@ -34,37 +36,44 @@ namespace TINK.Model.Connector
/// Gets all statsion for station provider and add them into station list.
/// </summary>
/// <param name="p_oStationList">List of stations to update.</param>
public static StationDictionary GetStationsAllMutable(this StationsAllResponse p_oStationsAllResponse)
public static StationDictionary GetStationsAllMutable(this StationsAvailableResponse stationsAllResponse)
{
// Get stations from Copri/ file/ memory, ....
if (p_oStationsAllResponse == null
|| p_oStationsAllResponse.stations == null)
if (stationsAllResponse == null
|| stationsAllResponse.stations == null)
{
// Latest list of stations could not be retrieved from provider.
return new StationDictionary();
}
Version.TryParse(p_oStationsAllResponse.copri_version, out Version l_oCopriVersion);
Version.TryParse(stationsAllResponse.copri_version, out Version copriVersion);
var l_oStations = new StationDictionary(p_oVersion: l_oCopriVersion);
var stations = new StationDictionary(p_oVersion: copriVersion);
foreach (var l_oStation in p_oStationsAllResponse.stations)
foreach (var station in stationsAllResponse.stations)
{
if (l_oStations.GetById(l_oStation.Value.station) != null)
if (stations.GetById(station.Value.station) != null)
{
// Can not add station to list of station. Id is not unique.
throw new InvalidResponseException<StationsAllResponse>(
string.Format("Station id {0} is not unique.", l_oStation.Value.station), p_oStationsAllResponse);
throw new InvalidResponseException<StationsAvailableResponse>(
string.Format("Station id {0} is not unique.", station.Value.station), stationsAllResponse);
}
l_oStations.Add(new Station.Station(
l_oStation.Value.station,
l_oStation.Value.GetGroup(),
l_oStation.Value.GetPosition(),
l_oStation.Value.description));
stations.Add(new Station.Station(
station.Value.station,
station.Value.GetGroup(),
station.Value.GetPosition(),
station.Value.description,
new Data(station.Value.operator_data?.operator_name,
station.Value.operator_data?.operator_phone,
station.Value.operator_data?.operator_hours,
station.Value.operator_data?.operator_email,
!string.IsNullOrEmpty(station.Value.operator_data?.operator_color)
? Color.FromHex(station.Value.operator_data?.operator_color)
: (Color?)null)));
}
return l_oStations;
return stations;
}
/// <summary> Gets account object from login response.</summary>

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using TINK.Model.Station.Operator;
namespace TINK.Model.Station
{
@ -15,5 +16,8 @@ namespace TINK.Model.Station
/// <summary> Holds the gps- position of the station.</summary>
Position Position { get; }
/// <summary> Holds operator related data.</summary>
IData OperatorData { get; }
}
}

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using TINK.Model.Station.Operator;
namespace TINK.Model.Station
{
@ -16,5 +17,8 @@ namespace TINK.Model.Station
/// <summary> Holds the gps- position of the station.</summary>
public Position Position => new Position(double.NaN, double.NaN);
/// <summary> Holds operator related data.</summary>
public IData OperatorData => new Data();
}
}

View file

@ -0,0 +1,36 @@
using Xamarin.Forms;
namespace TINK.Model.Station.Operator
{
/// <summary> Holds operator related data.</summary>
public class Data : IData
{
public Data(
string name = null,
string phoneNumberText = null,
string hours = null,
string mailAddressText = null, Color? color = null)
{
Name = name ?? string.Empty;
PhoneNumberText = phoneNumberText ?? string.Empty;
Hours = hours ?? string.Empty;
MailAddressText = mailAddressText ?? string.Empty;
Color = color;
}
/// <summary> Name of the operator.</summary>
public string Name { get; private set; }
/// <summary> Support phone number of the operator.</summary>
public string PhoneNumberText { get; private set; }
/// <summary> Office times when support is available.</summary>
public string Hours { get; private set; }
/// <summary> Support mails address of the operator.</summary>
public string MailAddressText { get; private set; }
/// <summary> Color of the operator (operator specific skin)</summary>
public Color? Color { get; private set; }
}
}

View file

@ -0,0 +1,22 @@
using Xamarin.Forms;
namespace TINK.Model.Station.Operator
{
public interface IData
{
/// <summary> Name of the operator.</summary>
string Name { get; }
/// <summary> Support phone number of the operator.</summary>
string PhoneNumberText { get; }
/// <summary> Office times when support is available.</summary>
string Hours { get; }
/// <summary> Support mails address of the operator.</summary>
string MailAddressText { get; }
/// <summary> Color of the operator (operator specific skin)</summary>
Color? Color { get; }
}
}

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using TINK.Model.Station.Operator;
namespace TINK.Model.Station
{
@ -7,20 +8,22 @@ namespace TINK.Model.Station
public class Station : IStation
{
/// <summary> Constructs a station object.</summary>
/// <param name="p_iId">Id of the station.</param>
/// <param name="p_oGroup">Group (TINK, Konrad) to which station is related.</param>
/// <param name="p_oPosition">GPS- position of the station.</param>
/// <param name="p_strStationName">Name of the station.</param>
/// <param name="id">Id of the station.</param>
/// <param name="group">Group (TINK, Konrad) to which station is related.</param>
/// <param name="position">GPS- position of the station.</param>
/// <param name="stationName">Name of the station.</param>
public Station(
string p_iId,
IEnumerable<string> p_oGroup,
Position p_oPosition,
string p_strStationName = "")
string id,
IEnumerable<string> group,
Position position,
string stationName = "",
Data operatorData = null)
{
Id = p_iId;
Group = p_oGroup ?? throw new ArgumentException("Can not construct station object. Group of stations must not be null.");
Position = p_oPosition;
StationName = p_strStationName ?? string.Empty;
Id = id;
Group = group ?? throw new ArgumentException("Can not construct station object. Group of stations must not be null.");
Position = position;
StationName = stationName ?? string.Empty;
OperatorData = operatorData ?? new Data();
}
/// <summary> Holds the unique id of the station.c</summary>
@ -34,5 +37,8 @@ namespace TINK.Model.Station
/// <summary> Holds the gps- position of the station.</summary>
public Position Position { get; }
/// <summary> Holds operator related info.</summary>
public IData OperatorData { get; }
}
}

View file

@ -322,7 +322,7 @@ namespace TINK.Model
public ICipher Cipher { get; }
/// <summary> Name of the station which is selected. </summary>
public IStation SelectedStation { get; set; } = new Station.Station(null, new List<string>(), null);
public IStation SelectedStation { get; set; } = new NullStation();
/// <summary> Holds the stations availalbe. </summary>
public IEnumerable<IStation> Stations { get; set; } = new List<Station.Station>();

View file

@ -413,6 +413,10 @@ namespace TINK.Model
{
new Version(3, 0, 240),
AppResources.ChangeLog3_0_240
},
{
new Version(3, 0, 241),
AppResources.ChangeLog3_0_241
}
};

View file

@ -742,6 +742,15 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Bike sharing system operator specific support info displayed on contact page..
/// </summary>
public static string ChangeLog3_0_241 {
get {
return ResourceManager.GetString("ChangeLog3_0_241", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Lock of rented bike can not be found..
/// </summary>
@ -1047,6 +1056,33 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Please open a bike station page to to contact the bike sharing operator..
/// </summary>
public static string MarkingContactNoStationInfoAvailableNoButton {
get {
return ResourceManager.GetString("MarkingContactNoStationInfoAvailableNoButton", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Contact {0}.
/// </summary>
public static string MarkingContactPageTitle {
get {
return ResourceManager.GetString("MarkingContactPageTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to &lt;font color=&quot;blue&quot;&gt;&lt;u&gt;Contact&lt;/u&gt;&lt;/font&gt; {0}..
/// </summary>
public static string MarkingContactSupport {
get {
return ResourceManager.GetString("MarkingContactSupport", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Contact.
/// </summary>
@ -1174,6 +1210,15 @@ namespace TINK.MultilingualResources {
}
}
/// <summary>
/// Looks up a localized string similar to Please login to reserve bikes! Tap &lt;font color=&quot;blue&quot;&gt;&lt;u&gt;here&lt;/u&gt;&lt;/font&gt; to switch to login page..
/// </summary>
public static string MarkingLoginRequiredToRerserve {
get {
return ResourceManager.GetString("MarkingLoginRequiredToRerserve", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Bike Locations.
/// </summary>
@ -1517,7 +1562,7 @@ namespace TINK.MultilingualResources {
}
/// <summary>
/// Looks up a localized string similar to Urgent question related to {0}? (Monday-Friday: 10:00 18:00).
/// Looks up a localized string similar to Urgent question related to {0}?.
/// </summary>
public static string MessagePhoneMail {
get {

View file

@ -158,7 +158,7 @@ Eine Radrückgabe ist nur möglich, wenn das Rad in Reichweite ist und Standorti
<value>Fragen? Hinweise? Kritik?</value>
</data>
<data name="MessagePhoneMail" xml:space="preserve">
<value>Eilige Frage rund um {0}? (Montag-Freitag: 10:00 18:00)</value>
<value>Eilige Frage rund um {0}?</value>
</data>
<data name="MessageRateMail" xml:space="preserve">
<value>Gefällt die {0}-App?</value>
@ -613,4 +613,19 @@ Layout Anzeige Radnamen und nummern verbessert.</value>
<data name="ChangeLog3_0_240" xml:space="preserve">
<value>Wahl eines Rads über die Radnummer hinzugefügt.</value>
</data>
<data name="MarkingContactSupport" xml:space="preserve">
<value>{0} &lt;font color="blue"&gt;&lt;u&gt;kontaktieren&lt;/u&gt;&lt;/font&gt;.</value>
</data>
<data name="MarkingLoginRequiredToRerserve" xml:space="preserve">
<value>Bitte Anmelden um Fahrräder zu reservieren! &lt;font color="blue"&gt;&lt;u&gt;Hier&lt;/u&gt;&lt;/font&gt; tippen, um auf Anmeldeseite zu wechseln.</value>
</data>
<data name="MarkingContactNoStationInfoAvailableNoButton" xml:space="preserve">
<value>Bitte eine Fahrradstationsseite öffen, um den Betreiber zu kontaktieren.</value>
</data>
<data name="MarkingContactPageTitle" xml:space="preserve">
<value>Kontakt {0}</value>
</data>
<data name="ChangeLog3_0_241" xml:space="preserve">
<value>Auf der Kontaktseite werden Kontaktinformationen betreiberspezifisch angezeigt.</value>
</data>
</root>

View file

@ -248,7 +248,7 @@ Use of app is restricted to maximu 8 devices per account.
Please login to app once again. In case this fails please check on website if the account is still valid.</value>
</data>
<data name="MessagePhoneMail" xml:space="preserve">
<value>Urgent question related to {0}? (Monday-Friday: 10:00 18:00)</value>
<value>Urgent question related to {0}?</value>
</data>
<data name="MessageRateMail" xml:space="preserve">
<value>Are you enjoying the {0}-App?</value>
@ -709,4 +709,19 @@ Layout of bike names and id display improved.</value>
<data name="ChangeLog3_0_240" xml:space="preserve">
<value>Find bike by id functionality added.</value>
</data>
<data name="MarkingContactNoStationInfoAvailableNoButton" xml:space="preserve">
<value>Please open a bike station page to to contact the bike sharing operator.</value>
</data>
<data name="MarkingContactSupport" xml:space="preserve">
<value>&lt;font color="blue"&gt;&lt;u&gt;Contact&lt;/u&gt;&lt;/font&gt; {0}.</value>
</data>
<data name="MarkingLoginRequiredToRerserve" xml:space="preserve">
<value>Please login to reserve bikes! Tap &lt;font color="blue"&gt;&lt;u&gt;here&lt;/u&gt;&lt;/font&gt; to switch to login page.</value>
</data>
<data name="MarkingContactPageTitle" xml:space="preserve">
<value>Contact {0}</value>
</data>
<data name="ChangeLog3_0_241" xml:space="preserve">
<value>Bike sharing system operator specific support info displayed on contact page.</value>
</data>
</root>

View file

@ -203,8 +203,8 @@ Eine Radrückgabe ist nur möglich, wenn das Rad in Reichweite ist und Standorti
<target state="translated">Fragen? Hinweise? Kritik?</target>
</trans-unit>
<trans-unit id="MessagePhoneMail" translate="yes" xml:space="preserve">
<source>Urgent question related to {0}? (Monday-Friday: 10:00 18:00)</source>
<target state="translated">Eilige Frage rund um {0}? (Montag-Freitag: 10:00 18:00)</target>
<source>Urgent question related to {0}?</source>
<target state="translated">Eilige Frage rund um {0}?</target>
</trans-unit>
<trans-unit id="MessageRateMail" translate="yes" xml:space="preserve">
<source>Are you enjoying the {0}-App?</source>
@ -821,6 +821,26 @@ Layout Anzeige Radnamen und nummern verbessert.</target>
<source>Find bike by id functionality added.</source>
<target state="translated">Wahl eines Rads über die Radnummer hinzugefügt.</target>
</trans-unit>
<trans-unit id="MarkingContactSupport" translate="yes" xml:space="preserve">
<source><bpt id="1">&lt;font color="blue"&gt;</bpt><bpt id="2">&lt;u&gt;</bpt>Contact<ept id="2">&lt;/u&gt;</ept><ept id="1">&lt;/font&gt;</ept> {0}.</source>
<target state="translated">{0} <bpt id="1">&lt;font color="blue"&gt;</bpt><bpt id="2">&lt;u&gt;</bpt>kontaktieren<ept id="2">&lt;/u&gt;</ept><ept id="1">&lt;/font&gt;</ept>.</target>
</trans-unit>
<trans-unit id="MarkingLoginRequiredToRerserve" translate="yes" xml:space="preserve">
<source>Please login to reserve bikes! Tap <bpt id="1">&lt;font color="blue"&gt;</bpt><bpt id="2">&lt;u&gt;</bpt>here<ept id="2">&lt;/u&gt;</ept><ept id="1">&lt;/font&gt;</ept> to switch to login page.</source>
<target state="translated">Bitte Anmelden um Fahrräder zu reservieren! <bpt id="1">&lt;font color="blue"&gt;</bpt><bpt id="2">&lt;u&gt;</bpt>Hier<ept id="2">&lt;/u&gt;</ept><ept id="1">&lt;/font&gt;</ept> tippen, um auf Anmeldeseite zu wechseln.</target>
</trans-unit>
<trans-unit id="MarkingContactNoStationInfoAvailableNoButton" translate="yes" xml:space="preserve">
<source>Please open a bike station page to to contact the bike sharing operator.</source>
<target state="translated">Bitte eine Fahrradstationsseite öffen, um den Betreiber zu kontaktieren.</target>
</trans-unit>
<trans-unit id="MarkingContactPageTitle" translate="yes" xml:space="preserve">
<source>Contact {0}</source>
<target state="translated">Kontakt {0}</target>
</trans-unit>
<trans-unit id="ChangeLog3_0_241" translate="yes" xml:space="preserve">
<source>Bike sharing system operator specific support info displayed on contact page.</source>
<target state="translated">Auf der Kontaktseite werden Kontaktinformationen betreiberspezifisch angezeigt.</target>
</trans-unit>
</group>
</body>
</file>

View file

@ -106,7 +106,7 @@ namespace TINK.Repository
/// <summary> Get list of stations. </summary>
/// <returns>List of files.</returns>
public async Task<StationsAllResponse> GetStationsAsync()
public async Task<StationsAvailableResponse> GetStationsAsync()
{
var stations = await GetStationsAsync(m_oCopriHost.AbsoluteUri, requestBuilder.GetStations(), UserAgent);
return stations;
@ -316,7 +316,7 @@ namespace TINK.Repository
/// <param name="p_strCopriHost">URL of the copri host to connect to.</param>
/// <param name="p_oCommand">Command to get stations.</param>
/// <returns>List of files.</returns>
public static async Task<StationsAllResponse> GetStationsAsync(
public static async Task<StationsAvailableResponse> GetStationsAsync(
string p_strCopriHost,
string p_oCommand,
string userAgent = null)

View file

@ -120,6 +120,284 @@ namespace TINK.Repository
}
}";
/// <summary></summary>
public const string STATIONS_AVAILABLE_LOGGEDIN_20210720 = @"
{
""shareejson"": {
""lang"": ""DE"",
""impress_html"": ""site/impress.html"",
""tariff_info_html"": ""site/tariff_info_1.html"",
""debuglevel"": ""1"",
""user_tour"": [
null,
""""
],
""response"": ""stations_available"",
""user_id"": ""ohauff@posteo.de"",
""stations"": {
""LV_3"": {
""service_tour"": ""LV_1"",
""uri_operator"": ""https://shareeapp-lv.copri.eu"",
""authed"": ""1"",
""station"": ""LV_3"",
""gps"": {
""latitude"": ""47.9973"",
""longitude"": ""7.8585""
},
""gps_radius"": ""100"",
""description"": ""Katholische Akademie"",
""state"": ""available"",
""operator_data"": {
""operator_color"": ""#006269"",
""operator_hours"": """",
""operator_name"": ""LastenVelo Freiburg"",
""operator_email"": ""info@lastenvelofreiburg.de"",
""operator_phone"": """"
},
""station_group"": [
""LV_300005""
]
},
""LV_1"": {
""uri_operator"": ""https://shareeapp-lv.copri.eu"",
""station"": ""LV_1"",
""gps"": {
""latitude"": ""47.9848"",
""longitude"": ""7.848666""
},
""authed"": ""1"",
""service_tour"": ""LV_1"",
""operator_data"": {
""operator_hours"": """",
""operator_email"": ""info@lastenvelofreiburg.de"",
""operator_name"": ""LastenVelo Freiburg"",
""operator_color"": ""#006269"",
""operator_phone"": """"
},
""state"": ""available"",
""description"": ""Parkplatz Feuerwehr Wiehre"",
""station_group"": [
""LV_300005""
],
""gps_radius"": ""100""
},
""FR_105"": {
""gps_radius"": ""50"",
""station_group"": [
""FR_300029""
],
""state"": ""available"",
""description"": ""Contributor-Station Rainer"",
""operator_data"": {
""operator_phone"": ""+49 761 45370097"",
""operator_hours"": ""B<EFBFBD>rozeiten: Montag, Mittwoch, Freitag 9-12 Uhr"",
""operator_name"": ""sharee.bike | TeilRad GmbH"",
""operator_email"": ""hotline@sharee.bike"",
""operator_color"": ""#009699""
},
""service_tour"": ""FR_1"",
""authed"": ""1"",
""station"": ""FR_105"",
""gps"": {
""longitude"": "" 7.973855"",
""latitude"": ""47.927738""
},
""uri_operator"": ""https://shareeapp-fr01.copri.eu""
},
""FR_104"": {
""gps_radius"": ""50"",
""operator_data"": {
""operator_phone"": ""+49 761 45370097"",
""operator_color"": ""#009699"",
""operator_email"": ""hotline@sharee.bike"",
""operator_name"": ""sharee.bike | TeilRad GmbH"",
""operator_hours"": ""B<EFBFBD>rozeiten: Montag, Mittwoch, Freitag 9-12 Uhr""
},
""description"": ""Contributor-Station fahrradspezialitäten"",
""state"": ""available"",
""station_group"": [
""FR_300029""
],
""service_tour"": ""FR_1"",
""uri_operator"": ""https://shareeapp-fr01.copri.eu"",
""gps"": {
""latitude"": ""47.989807"",
""longitude"": "" 7.837621""
},
""station"": ""FR_104"",
""authed"": ""1""
},
""FR_103"": {
""uri_operator"": ""https://shareeapp-fr01.copri.eu"",
""authed"": ""1"",
""gps"": {
""longitude"": "" 7.785428"",
""latitude"": ""47.997930""
},
""station"": ""FR_103"",
""service_tour"": ""FR_1"",
""description"": ""Contributor-Station Oliver"",
""state"": ""available"",
""operator_data"": {
""operator_phone"": ""+49 761 45370097"",
""operator_hours"": ""B<EFBFBD>rozeiten: Montag, Mittwoch, Freitag 9-12 Uhr"",
""operator_name"": ""sharee.bike | TeilRad GmbH"",
""operator_email"": ""hotline@sharee.bike"",
""operator_color"": ""#009699""
},
""station_group"": [
""FR_300029""
],
""gps_radius"": ""50""
},
""LV_88"": {
""station_group"": [
""LV_300005""
],
""state"": ""available"",
""description"": ""TINK Test KN"",
""operator_data"": {
""operator_name"": ""LastenVelo Freiburg"",
""operator_email"": ""info@lastenvelofreiburg.de"",
""operator_hours"": """",
""operator_color"": ""#006269"",
""operator_phone"": """"
},
""gps_radius"": ""50"",
""authed"": ""1"",
""gps"": {
""latitude"": ""47.65934079179006"",
""longitude"": "" 9.166126178863573""
},
""station"": ""LV_88"",
""uri_operator"": ""https://shareeapp-lv.copri.eu"",
""service_tour"": ""LV_2""
},
""LV_4"": {
""uri_operator"": ""https://shareeapp-lv.copri.eu"",
""authed"": ""1"",
""station"": ""LV_4"",
""gps"": {
""latitude"": ""48.01095"",
""longitude"": ""7.8553""
},
""service_tour"": ""LV_1"",
""state"": ""available"",
""description"": ""Fabrik Habsburger Straße"",
""operator_data"": {
""operator_phone"": """",
""operator_hours"": """",
""operator_email"": ""info@lastenvelofreiburg.de"",
""operator_name"": ""LastenVelo Freiburg"",
""operator_color"": ""#006269""
},
""station_group"": [
""LV_300005""
],
""gps_radius"": ""100""
},
""LV_2"": {
""uri_operator"": ""https://shareeapp-lv.copri.eu"",
""authed"": ""1"",
""gps"": {
""longitude"": ""7.84795"",
""latitude"": ""47.99762""
},
""station"": ""LV_2"",
""service_tour"": ""LV_1"",
""description"": ""Predigertor ADAC"",
""state"": ""available"",
""operator_data"": {
""operator_phone"": """",
""operator_name"": ""LastenVelo Freiburg"",
""operator_email"": ""info@lastenvelofreiburg.de"",
""operator_hours"": """",
""operator_color"": ""#006269""
},
""station_group"": [
""LV_300005""
],
""gps_radius"": ""100""
},
""FR_102"": {
""description"": ""Contributor-Station Dieter"",
""state"": ""available"",
""operator_data"": {
""operator_phone"": ""+49 761 45370097"",
""operator_hours"": ""B<EFBFBD>rozeiten: Montag, Mittwoch, Freitag 9-12 Uhr"",
""operator_email"": ""hotline@sharee.bike"",
""operator_name"": ""sharee.bike | TeilRad GmbH"",
""operator_color"": ""#009699""
},
""station_group"": [
""FR_300029""
],
""gps_radius"": ""50"",
""uri_operator"": ""https://shareeapp-fr01.copri.eu"",
""authed"": ""1"",
""station"": ""FR_102"",
""gps"": {
""longitude"": "" 7.835669"",
""latitude"": ""47.994371""
},
""service_tour"": ""FR_1""
},
""FR_101"": {
""station_group"": [
""FR_300001""
],
""state"": ""available"",
""description"": ""Villaban sharee Station"",
""operator_data"": {
""operator_color"": ""#009699"",
""operator_email"": ""hotline@sharee.bike"",
""operator_name"": ""sharee.bike | TeilRad GmbH"",
""operator_hours"": ""B<EFBFBD>rozeiten: Montag, Mittwoch, Freitag 9-12 Uhr"",
""operator_phone"": ""+49 761 45370097""
},
""gps_radius"": ""50"",
""authed"": ""1"",
""gps"": {
""longitude"": "" 7.825490"",
""latitude"": ""47.976634""
},
""station"": ""FR_101"",
""uri_operator"": ""https://shareeapp-fr01.copri.eu"",
""service_tour"": ""FR_1""
}
},
""response_state"": ""OK, nothing todo"",
""agb_checked"": ""1"",
""agb_html"": ""site/agb.html"",
""authcookie"": ""5781_f172cf59108fe53e7524c841847fee69_oiF2kahH"",
""Ilockit_admin"": ""1"",
""copri_version"": ""4.1.0.0"",
""bike_info_html"": ""site/bike_info.html"",
""uri_operator_array"": [
""https://shareeapp-lv.copri.eu"",
""https://shareeapp-fr01.copri.eu""
],
""uri_primary"": ""https://shareeapp-primary.copri.eu"",
""apiserver"": ""https://shareeapp-primary.copri.eu"",
""last_used_operator"": {
""operator_phone"": ""+49 761 45370097"",
""operator_color"": ""#009699"",
""operator_email"": ""hotline@sharee.bike"",
""operator_name"": ""sharee.bike | TeilRad GmbH"",
""operator_hours"": ""Bürozeiten: Montag, Mittwoch, Freitag 9-12 Uhr""
},
""clearing_cache"": ""0"",
""user_group"": [
""LV_300005"",
""FR_300029"",
""FR_300001""
],
""privacy_html"": ""site/privacy.html"",
""new_authcoo"": ""0""
}
}
";
const string BOOKING_REQUEST_SET02_001_FILE = @"
{
""shareejson"" : {
@ -1065,7 +1343,7 @@ namespace TINK.Repository
/// </summary>
/// <param name="p_strCookie">Auto cookie of user if user is logged in.</param>
/// <returns>List of files.</returns>
public async Task<StationsAllResponse> GetStationsAsync()
public async Task<StationsAvailableResponse> GetStationsAsync()
{
return await Task.Run(() => GetStationsAll(null, SessionCookie, ActiveSampleSet, ActiveStageIndex));
}
@ -1256,7 +1534,7 @@ namespace TINK.Repository
/// <param name="p_eSampleSet"></param>
/// <param name="p_lStageIndex"></param>
/// <returns></returns>
public static StationsAllResponse GetStationsAll(
public static StationsAvailableResponse GetStationsAll(
string p_strMerchantId,
string p_strCookie = null,
SampleSets p_eSampleSet = DEFAULT_SAMPLE_SET,
@ -1268,7 +1546,7 @@ namespace TINK.Repository
switch (p_lStageIndex)
{
case 1:
return JsonConvertRethrow.DeserializeObject<ResponseContainer<StationsAllResponse>>(STATIONS_SET02_001_FILE).shareejson;
return JsonConvertRethrow.DeserializeObject<ResponseContainer<StationsAvailableResponse>>(STATIONS_SET02_001_FILE).shareejson;
default:
return null;

View file

@ -95,8 +95,8 @@ namespace TINK.Repository
/// <summary> Gets an empty response. </summary>
/// <param name="copriVersion">Version of empty response.</param>
/// <returns>Response.</returns>
public static StationsAllResponse GetEmptyStationsAllResponse(string copriVersion)
=> JsonConvertRethrow.DeserializeObject<StationsAllResponse>(STATIONSALL.Replace("4.1.0.0", copriVersion));
public static StationsAvailableResponse GetEmptyStationsAllResponse(string copriVersion)
=> JsonConvertRethrow.DeserializeObject<StationsAvailableResponse>(STATIONSALL.Replace("4.1.0.0", copriVersion));
/// <summary>
/// Holds the seconds after which station and bikes info is considered to be invalid.
@ -141,7 +141,7 @@ namespace TINK.Repository
if (!Barrel.Current.Exists(requestBuilder.GetStations()))
{
AddToCache(JsonConvertRethrow.DeserializeObject<StationsAllResponse>(STATIONSALL), new TimeSpan(0));
AddToCache(JsonConvertRethrow.DeserializeObject<StationsAvailableResponse>(STATIONSALL), new TimeSpan(0));
}
}
@ -224,12 +224,12 @@ namespace TINK.Repository
}
}
public async Task<StationsAllResponse> GetStationsAsync()
public async Task<StationsAvailableResponse> GetStationsAsync()
{
var l_oStationsAllTask = new TaskCompletionSource<StationsAllResponse>();
var l_oStationsAllTask = new TaskCompletionSource<StationsAvailableResponse>();
lock (monkeyLock)
{
l_oStationsAllTask.SetResult(Barrel.Current.Get<StationsAllResponse>(requestBuilder.GetStations()));
l_oStationsAllTask.SetResult(Barrel.Current.Get<StationsAvailableResponse>(requestBuilder.GetStations()));
}
return await l_oStationsAllTask.Task;
}
@ -248,7 +248,7 @@ namespace TINK.Repository
/// <summary> Adds a stations all response to cache.</summary>
/// <param name="stations">Stations to add.</param>
public void AddToCache(StationsAllResponse stations)
public void AddToCache(StationsAvailableResponse stations)
{
AddToCache(stations, ExpiresAfter);
}
@ -256,7 +256,7 @@ namespace TINK.Repository
/// <summary> Adds a stations all response to cache.</summary>
/// <param name="stations">Stations to add.</param>
/// <param name="expiresAfter">Time after which anser is considered to be expired.</param>
private void AddToCache(StationsAllResponse stations, TimeSpan expiresAfter)
private void AddToCache(StationsAvailableResponse stations, TimeSpan expiresAfter)
{
lock (monkeyLock)
{

View file

@ -112,7 +112,7 @@ namespace TINK.Repository
{
/// <summary> Get list of stations. </summary>
/// <returns>List of all stations.</returns>
Task<StationsAllResponse> GetStationsAsync();
Task<StationsAvailableResponse> GetStationsAsync();
/// <summary> Gets a list of bikes from Copri. </summary>
/// <returns>Response holding list of bikes.</returns>

View file

@ -7,14 +7,37 @@ namespace TINK.Repository.Response
/// Holds the information about all stations and is used for deserialization of copri answer.
/// </summary>
[DataContract]
public class StationsAllResponse : ResponseBase
public class StationsAvailableResponse : ResponseBase
{
/// <summary>
/// <summary>
/// Holds info about a single station.
/// </summary>
[DataContract]
public class StationInfo
{
/// <summary>
/// Holds info about opertor data.
/// </summary>
[DataContract]
public class OperatorData
{
[DataMember]
public string operator_name { get; private set; }
[DataMember]
public string operator_phone { get; private set; }
[DataMember]
public string operator_hours { get; private set; }
[DataMember]
public string operator_email { get; private set; }
[DataMember]
public string operator_color { get; private set; }
}
/// <summary>
/// Unique id of the station.
/// </summary>
@ -32,6 +55,9 @@ namespace TINK.Repository.Response
/// </summary>
[DataMember]
public GpsInfo gps { get; private set; }
[DataMember]
public OperatorData operator_data { get; private set; }
}
/// <summary>

View file

@ -109,7 +109,7 @@ namespace TINK.Model.Services.CopriApi
/// <summary> Get list of stations. </summary>
/// <returns>List of files.</returns>
public async Task<Result<StationsAllResponse>> GetStations(bool fromCache = false)
public async Task<Result<StationsAvailableResponse>> GetStations(bool fromCache = false)
{
Log.ForContext<CopriProviderHttps>().Debug($"Request to get stations{(fromCache ? " from cache" : "")}...");
if (!CacheServer.IsStationsExpired
@ -117,7 +117,7 @@ namespace TINK.Model.Services.CopriApi
{
// No need to query because previous answer is not yet outdated.
Log.ForContext<CopriProviderHttps>().Debug($"Returning stations from cache.");
return new Result<StationsAllResponse>(typeof(CopriCallsMonkeyStore), await CacheServer.GetStationsAsync());
return new Result<StationsAvailableResponse>(typeof(CopriCallsMonkeyStore), await CacheServer.GetStationsAsync());
}
try
@ -126,7 +126,7 @@ namespace TINK.Model.Services.CopriApi
var stations = await HttpsServer.GetStationsAsync();
return new Result<StationsAllResponse>(
return new Result<StationsAvailableResponse>(
typeof(CopriCallsHttps),
stations.GetIsResponseOk("Abfrage der Stationen fehlsgeschlagen."));
}
@ -134,14 +134,14 @@ namespace TINK.Model.Services.CopriApi
{
// Return response from cache.
Log.ForContext<CopriProviderHttps>().Debug("An error occurred querrying stations. {Exception}.", exception);
return new Result<StationsAllResponse>(typeof(CopriCallsMonkeyStore), await CacheServer.GetStationsAsync(), exception);
return new Result<StationsAvailableResponse>(typeof(CopriCallsMonkeyStore), await CacheServer.GetStationsAsync(), exception);
}
}
/// <summary>Adds https--response to cache if response is ok. </summary>
/// <param name="response">Response to add to cache.</param>
/// <returns></returns>
public void AddToCache(Result<StationsAllResponse> result)
public void AddToCache(Result<StationsAvailableResponse> result)
{
Log.ForContext<CopriProviderHttps>().Debug($"Request to add stations all response to cache...");
if (result.Source == typeof(CopriCallsMonkeyStore)

View file

@ -83,7 +83,7 @@ namespace TINK.Model.Services.CopriApi
return await monkeyStore.GetBikesOccupiedAsync();
}
public async Task<StationsAllResponse> GetStationsAsync()
public async Task<StationsAvailableResponse> GetStationsAsync()
{
return await monkeyStore.GetStationsAsync();
}

View file

@ -10,7 +10,7 @@ namespace TINK.Model.Services.CopriApi
{
/// <summary> Get list of stations. </summary>
/// <returns>List of all stations.</returns>
Task<Result<StationsAllResponse>> GetStations(bool fromCache = false);
Task<Result<StationsAvailableResponse>> GetStations(bool fromCache = false);
/// <summary> Gets a list of bikes from Copri. </summary>
/// <returns>Response holding list of bikes.</returns>
@ -22,7 +22,7 @@ namespace TINK.Model.Services.CopriApi
/// <summary>Adds https--response to cache if response is ok. </summary>
/// <param name="response">Response to add to cache.</param>
void AddToCache(Result<StationsAllResponse> result);
void AddToCache(Result<StationsAvailableResponse> result);
/// <summary>Adds https--response to cache if response is ok. </summary>
/// <param name="response">Response to add to cache.</param>

View file

@ -10,7 +10,7 @@ namespace TINK.Model.Services.CopriApi
/// <summary> Adds a stations all response to cache.</summary>
/// <param name="stations">Stations to add.</param>
void AddToCache(StationsAllResponse stations);
void AddToCache(StationsAvailableResponse stations);
/// <summary> Gets a value indicating whether stations are expired or not.</summary>
bool IsBikesAvailableExpired { get; }

View file

@ -113,23 +113,13 @@ namespace TINK.ViewModel.BikesAtStation
/// <summary>
/// Informs about need to log in before requesting an bike.
/// </summary>
public FormattedString LoginRequiredHintText
{
get
{
if (ActiveUser.IsLoggedIn)
{
return string.Empty;
}
public string LoginRequiredHintText
=> ActiveUser.IsLoggedIn
? string.Empty
: AppResources.MarkingLoginRequiredToRerserve;
var l_oHint = new FormattedString();
l_oHint.Spans.Add(new Span { Text = "Bitte Anmelden um Fahrräder zu reservieren! " });
l_oHint.Spans.Add(new Span { Text = "Hier", ForegroundColor = ViewModelHelper.LINK_COLOR });
l_oHint.Spans.Add(new Span { Text = " tippen um auf Anmeldeseite zu wechseln."});
return l_oHint;
}
}
public string ContactSupportHintText
=> string.Format(AppResources.MarkingContactSupport, m_oStation?.OperatorData?.Name ?? "Operator");
/// <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.
@ -154,21 +144,22 @@ namespace TINK.ViewModel.BikesAtStation
}
/// <summary> Command object to bind login page redirect link to view model.</summary>
public System.Windows.Input.ICommand LoginRequiredHintClickedCommand
{
get
{
public System.Windows.Input.ICommand ContactSupportClickedCommand
#if USEMASTERDETAIL || USEFLYOUT
return new Xamarin.Forms.Command(() => OpenLoginPageAsync());
=> new Xamarin.Forms.Command(() => OpenSupportPageAsync());
#else
return new Xamarin.Forms.Command(async () => await OpenLoginPageAsync());
=> new Xamarin.Forms.Command(async () => await OpenLoginPageAsync());
#endif
}
}
/// <summary>
/// Opens login page.
/// </summary>
/// <summary> Command object to bind login page redirect link to view model.</summary>
public System.Windows.Input.ICommand LoginRequiredHintClickedCommand
#if USEMASTERDETAIL || USEFLYOUT
=> new Xamarin.Forms.Command(() => OpenLoginPageAsync());
#else
=> new Xamarin.Forms.Command(async () => await OpenLoginPageAsync());
#endif
/// <summary> Opens login page. </summary>
#if USEMASTERDETAIL || USEFLYOUT
public void OpenLoginPageAsync()
#else
@ -192,6 +183,30 @@ namespace TINK.ViewModel.BikesAtStation
}
}
/// <summary> Opens login page. </summary>
#if USEMASTERDETAIL || USEFLYOUT
public void OpenSupportPageAsync()
#else
public async Task OpenLoginPageAsync()
#endif
{
try
{
// Switch to map page
#if USEMASTERDETAIL || USEFLYOUT
ViewService.ShowPage(ViewTypes.ContactPage, m_oStation?.OperatorData?.Name ?? AppResources.MarkingFeedbackAndContact);
#else
await ViewService.ShowPage("//LoginPage");
#endif
}
catch (Exception p_oException)
{
Log.Error("Ein unerwarteter Fehler ist auf der Seite Kontakt aufgetreten. Kontext: Klick auf Hinweistext auf Station N- seite ohne Anmeldung. {@Exception}", p_oException);
return;
}
}
/// <summary>
/// Invoked when page is shown.
/// Starts update process.

View file

@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Input;
using TINK.Model.Services.CopriApi.ServerUris;
using TINK.Model.Station;
using TINK.MultilingualResources;
using TINK.View;
using Xamarin.Essentials;
@ -18,6 +19,9 @@ namespace TINK.ViewModel.Info
/// <summary> Reference on view service to show modal notifications and to perform navigation. </summary>
private IViewService ViewService { get; }
/// <summary> Station selected by user. </summary>
private IStation SelectedStation;
Uri ActiveUri { get; }
/// <summary> Holds a reference to the external trigger service. </summary>
@ -29,6 +33,7 @@ namespace TINK.ViewModel.Info
/// <param name="openUrlInExternalBrowser">Action to open an external browser.</param>
/// <param name="viewService">View service to notify user.</param>
public ContactPageViewModel(
IStation selectedStation,
Uri activeUri,
Func<string> createAttachment,
Action openUrlInExternalBrowser,
@ -45,6 +50,9 @@ namespace TINK.ViewModel.Info
OpenUrlInExternalBrowser = openUrlInExternalBrowser
?? throw new ArgumentException("Can not instantiate contact page view model- object. No user external browse service available.");
// Station might be null-station, if no station info is avialable.
SelectedStation = selectedStation;
}
/// <summary> Command object to bind login button to view model. </summary>
@ -65,45 +73,19 @@ namespace TINK.ViewModel.Info
/// <summary>Holds the mail address to mail to.</summary>
public string MailAddressText
{
get
{
switch (ActiveUri.AbsoluteUri)
{
case CopriServerUriList.TINK_DEVEL:
case CopriServerUriList.TINK_LIVE:
return "tink@fahrradspezialitaeten.com";
case CopriServerUriList.SHAREE_DEVEL:
case CopriServerUriList.SHAREE_LIVE:
return "hotline@sharee.bike";
default:
return "post@sharee.bike";
}
}
}
=> SelectedStation?.OperatorData?.MailAddressText ?? string.Empty;
/// <summary>Holds the mail address to send mail to.</summary>
public string PhoneNumberText
{
get
{
switch (ActiveUri.AbsoluteUri)
{
case CopriServerUriList.TINK_DEVEL:
case CopriServerUriList.TINK_LIVE:
return "+49 7531 - 3694389";
=> SelectedStation?.OperatorData?.PhoneNumberText ?? string.Empty;
case CopriServerUriList.SHAREE_DEVEL:
case CopriServerUriList.SHAREE_LIVE:
return "+49 761 - 45370097";
/// <summary>Holds the mail address to send mail to.</summary>
public string OfficeHoursText
=> SelectedStation?.OperatorData?.Hours ?? string.Empty;
default:
return "+49 761 5158912";
}
}
}
/// <summary> Gets whether any operator support info is avaliable. </summary>
public bool IsOperatorInfoAvaliable
=> MailAddressText.Length > 0 || PhoneNumberText.Length > 0;
/// <summary> Request to do a phone call. </summary>
public async Task DoSendMail()
@ -256,7 +238,10 @@ namespace TINK.ViewModel.Info
get
{
var l_oHint = new FormattedString();
l_oHint.Spans.Add(new Span { Text = string.Format(AppResources.MessagePhoneMail, GetAppName(ActiveUri)) });
l_oHint.Spans.Add(new Span
{
Text = string.Format(AppResources.MessagePhoneMail, GetAppName(ActiveUri)) + $"{(OfficeHoursText.Length > 0 ? $" {OfficeHoursText}" : string.Empty)}"
});
return l_oHint;
}
}

View file

@ -11,10 +11,11 @@
MyBikesPage,
SettingsPage,
TabbedPageInfo,
TabbedPageHelpContact,
FeesAndBikesPage,
ManageAccountPage,
AgbPage,
WhatsNewPage,
BikesAtStation
BikesAtStation,
ContactPage
}
}

View file

@ -305,7 +305,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.Connector
var https = Substitute.For<ICopriServer>();
cache.IsStationsExpired.Returns(false);
cache.GetStationsAsync().Returns(Task.Run(() => JsonConvert.DeserializeObject<StationsAllResponse>(STATIONSALL)));
cache.GetStationsAsync().Returns(Task.Run(() => JsonConvert.DeserializeObject<StationsAvailableResponse>(STATIONSALL)));
var provider = new CopriProviderHttps(
new Uri("http://1.2.3.4"),
@ -329,7 +329,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.Connector
var https = Substitute.For<ICopriServer>();
cache.IsStationsExpired.Returns(true);
cache.GetStationsAsync().Returns(Task.Run(() => JsonConvert.DeserializeObject<StationsAllResponse>(STATIONSALL)));
cache.GetStationsAsync().Returns(Task.Run(() => JsonConvert.DeserializeObject<StationsAvailableResponse>(STATIONSALL)));
var provider = new CopriProviderHttps(
new Uri("http://1.2.3.4"),
@ -352,7 +352,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.Connector
var https = Substitute.For<ICopriServer>();
cache.IsStationsExpired.Returns(true);
https.GetStationsAsync().Returns(Task.Run(() => JsonConvert.DeserializeObject<StationsAllResponse>(STATIONSALL)));
https.GetStationsAsync().Returns(Task.Run(() => JsonConvert.DeserializeObject<StationsAvailableResponse>(STATIONSALL)));
var provider = new CopriProviderHttps(
new Uri("http://1.2.3.4"),
@ -375,8 +375,8 @@ namespace TestTINKLib.Fixtures.ObjectTests.Connector
var https = Substitute.For<ICopriServer>();
cache.IsStationsExpired.Returns(true);
cache.GetStationsAsync().Returns(Task.Run(() => JsonConvert.DeserializeObject<StationsAllResponse>(STATIONSALL)));
https.GetStationsAsync().Returns<StationsAllResponse>(x => { throw new WebConnectFailureException("Bang...", new Exception()); });
cache.GetStationsAsync().Returns(Task.Run(() => JsonConvert.DeserializeObject<StationsAvailableResponse>(STATIONSALL)));
https.GetStationsAsync().Returns<StationsAvailableResponse>(x => { throw new WebConnectFailureException("Bang...", new Exception()); });
var provider = new CopriProviderHttps(
new Uri("http://1.2.3.4"),
@ -411,17 +411,17 @@ namespace TestTINKLib.Fixtures.ObjectTests.Connector
try
{
// Do not add if an excption occurred
provider.AddToCache(new Result<StationsAllResponse>(typeof(CopriCallsHttps), JsonConvert.DeserializeObject<StationsAllResponse>(STATIONSALL), new System.Exception("Bang...")));
provider.AddToCache(new Result<StationsAvailableResponse>(typeof(CopriCallsHttps), JsonConvert.DeserializeObject<StationsAvailableResponse>(STATIONSALL), new System.Exception("Bang...")));
stations = await provider.GetStations(true);
Assert.AreEqual(0, stations.Response.stations.Count);
// Do not add if results from cache
provider.AddToCache(new Result<StationsAllResponse>(typeof(CopriCallsMonkeyStore), JsonConvert.DeserializeObject<StationsAllResponse>(STATIONSALL)));
provider.AddToCache(new Result<StationsAvailableResponse>(typeof(CopriCallsMonkeyStore), JsonConvert.DeserializeObject<StationsAvailableResponse>(STATIONSALL)));
stations = await provider.GetStations(true);
Assert.AreEqual(0, stations.Response.stations.Count);
// Add result
provider.AddToCache(new Result<StationsAllResponse>(typeof(CopriCallsHttps), JsonConvert.DeserializeObject<StationsAllResponse>(STATIONSALL)));
provider.AddToCache(new Result<StationsAvailableResponse>(typeof(CopriCallsHttps), JsonConvert.DeserializeObject<StationsAvailableResponse>(STATIONSALL)));
stations = await provider.GetStations(true);
Assert.AreEqual(3, stations.Response.stations.Count);
}

View file

@ -13,6 +13,7 @@ using static TINK.Repository.CopriCallsMemory;
using BikeInfo = TINK.Model.Bike.BluetoothLock.BikeInfo;
using TINK.Model.User.Account;
using Xamarin.Forms;
namespace TestTINKLib.Fixtures.Connector
{
@ -42,6 +43,125 @@ namespace TestTINKLib.Fixtures.Connector
Assert.AreEqual("Südstadt Station", l_oStationsTarget.GetById("31").StationName);
}
[Test]
public void TestGetAllStations_OperatorData()
{
var stations = UpdaterJSON.GetStationsAllMutable(JsonConvert.DeserializeObject<ResponseContainer<StationsAvailableResponse>>(STATIONS_AVAILABLE_LOGGEDIN_20210720).shareejson);
var station = stations.FirstOrDefault(x => x.Id == "LV_3");
Assert.That(
station,
Is.Not.Null,
"Station LV_3 must exist.");
Assert.That(
station.OperatorData.Color,
Is.EqualTo(Color.FromHex("#006269")));
Assert.That(
station.OperatorData.Hours,
Is.EqualTo(string.Empty));
Assert.That(
station.OperatorData.Name,
Is.EqualTo("LastenVelo Freiburg"));
Assert.That(
station.OperatorData.MailAddressText,
Is.EqualTo("info@lastenvelofreiburg.de"));
Assert.That(
station.OperatorData.PhoneNumberText,
Is.EqualTo(string.Empty));
station = stations.FirstOrDefault(x => x.Id == "FR_105");
Assert.That(
station,
Is.Not.Null,
"Station FR_105 must exist.");
Assert.That(
station.OperatorData.Color,
Is.EqualTo(Color.FromHex("#009699")));
Assert.That(
station.OperatorData.Hours,
Is.EqualTo("B<>rozeiten: Montag, Mittwoch, Freitag 9-12 Uhr"));
Assert.That(
station.OperatorData.Name,
Is.EqualTo("sharee.bike | TeilRad GmbH"));
Assert.That(
station.OperatorData.MailAddressText,
Is.EqualTo("hotline@sharee.bike"));
Assert.That(
station.OperatorData.PhoneNumberText,
Is.EqualTo("+49 761 45370097"));
}
[Test]
public void TestGetAllStations_OperatorData_NoOperatorData()
{
var stations = UpdaterJSON.GetStationsAllMutable(JsonConvert.DeserializeObject<ResponseContainer<StationsAvailableResponse>>(@"
{
""shareejson"": {
""lang"": ""DE"",
""impress_html"": ""site/impress.html"",
""tariff_info_html"": ""site/tariff_info_1.html"",
""debuglevel"": ""1"",
""user_tour"": [
null,
""""
],
""response"": ""stations_available"",
""user_id"": ""ohauff@posteo.de"",
""stations"": {
""LV_3"": {
""service_tour"": ""LV_1"",
""uri_operator"": ""https://shareeapp-lv.copri.eu"",
""authed"": ""1"",
""station"": ""LV_3"",
""gps"": {
""latitude"": ""47.9973"",
""longitude"": ""7.8585""
},
""gps_radius"": ""100"",
""description"": ""Katholische Akademie"",
""state"": ""available"",
""station_group"": [
""LV_300005""
]
},
},
}
}").shareejson);
var station = stations.FirstOrDefault(x => x.Id == "LV_3");
Assert.That(
station.OperatorData.Color,
Is.Null);
Assert.That(
station.OperatorData.Hours,
Is.EqualTo(string.Empty));
Assert.That(
station.OperatorData.Name,
Is.EqualTo(string.Empty));
Assert.That(
station.OperatorData.MailAddressText,
Is.EqualTo(string.Empty));
Assert.That(
station.OperatorData.PhoneNumberText,
Is.EqualTo(string.Empty));
}
[Test]
public void TestUpdateBikesAvailable_BikeNr5GetBooked()
{

View file

@ -0,0 +1,61 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Text;
namespace TestShareeLib.Model.Station
{
[TestFixture]
public class TestData
{
[Test]
public void TestCtor()
{
var data = new TINK.Model.Station.Operator.Data();
Assert.That(
data.Color,
Is.Null);
Assert.That(
data.Hours,
Is.EqualTo(string.Empty));
Assert.That(
data.Name,
Is.EqualTo(string.Empty));
Assert.That(
data.MailAddressText,
Is.EqualTo(string.Empty));
Assert.That(
data.PhoneNumberText,
Is.EqualTo(string.Empty));
}
[Test]
public void TestCtor_Null()
{
var data = new TINK.Model.Station.Operator.Data(null, null, null, null, null);
Assert.That(
data.Color,
Is.Null);
Assert.That(
data.Hours,
Is.EqualTo(string.Empty));
Assert.That(
data.Name,
Is.EqualTo(string.Empty));
Assert.That(
data.MailAddressText,
Is.EqualTo(string.Empty));
Assert.That(
data.PhoneNumberText,
Is.EqualTo(string.Empty));
}
}
}

View file

@ -19,7 +19,7 @@ namespace UITest.Fixtures.ObjectTests.Connector
var bikesOccupied = JsonConvert.DeserializeObject<BikesReservedOccupiedResponse>(CopriCallsMonkeyStore.BIKESOCCUPIED);
Assert.NotNull(bikesOccupied?.bikes_occupied);
var stations = JsonConvert.DeserializeObject<StationsAllResponse>(CopriCallsMonkeyStore.STATIONSALL);
var stations = JsonConvert.DeserializeObject<StationsAvailableResponse>(CopriCallsMonkeyStore.STATIONSALL);
Assert.NotNull(stations?.stations);
}

View file

@ -84,7 +84,7 @@ namespace UITest.Fixtures.Connector
/// <param name="p_strMerchantId">Id of the merchant.</param>
/// <param name="p_strCookie">Auto cookie of user if user is logged in.</param>
/// <returns>List of files.</returns>
public static StationsAllResponse GetStationsAllCall(
public static StationsAvailableResponse GetStationsAllCall(
string p_strCopriHost,
string p_strMerchantId,
string p_strCookie = null)
@ -100,7 +100,7 @@ namespace UITest.Fixtures.Connector
l_oStationsAllResponse = Post(l_oCommand, p_strCopriHost);
// Extract bikes from response.
return JsonConvert.DeserializeObject<ResponseContainer<StationsAllResponse>>(l_oStationsAllResponse)?.shareejson;
return JsonConvert.DeserializeObject<ResponseContainer<StationsAvailableResponse>>(l_oStationsAllResponse)?.shareejson;
#else
return null;
#endif

View file

@ -88,9 +88,9 @@ namespace TestTINKLib.Fixtures.ObjectTests.Connector.Query
{
var server = MockRepository.GenerateMock<ICachedCopriServer>();
server.Stub(x => x.GetStations(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<StationsAllResponse>(
server.Stub(x => x.GetStations(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<StationsAvailableResponse>(
typeof(CopriCallsMonkeyStore),
JsonConvert.DeserializeObject<StationsAllResponse>(STATIONSALL),
JsonConvert.DeserializeObject<StationsAvailableResponse>(STATIONSALL),
new System.Exception("Bang when getting stations..."))));
server.Stub(x => x.GetBikesAvailable(Arg<bool>.Matches(fromCache => fromCache == true))).Return(Task.Run(() => new Result<BikesAvailableResponse>(
@ -110,18 +110,18 @@ namespace TestTINKLib.Fixtures.ObjectTests.Connector.Query
{
var server = MockRepository.GenerateMock<ICachedCopriServer>();
server.Stub(x => x.GetStations(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<StationsAllResponse>(
server.Stub(x => x.GetStations(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<StationsAvailableResponse>(
typeof(CopriCallsHttps),
JsonConvert.DeserializeObject<StationsAllResponse>(STATIONSALLEMPTY))));
JsonConvert.DeserializeObject<StationsAvailableResponse>(STATIONSALLEMPTY))));
server.Stub(x => x.GetBikesAvailable(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<BikesAvailableResponse>(
typeof(CopriCallsMonkeyStore),
JsonConvert.DeserializeObject<BikesAvailableResponse>(BIKESAVAILABLE),
new System.Exception("Bang when getting bikes..."))));
server.Stub(x => x.GetStations(Arg<bool>.Matches(fromCache => fromCache == true))).Return(Task.Run(() => new Result<StationsAllResponse>(
server.Stub(x => x.GetStations(Arg<bool>.Matches(fromCache => fromCache == true))).Return(Task.Run(() => new Result<StationsAvailableResponse>(
typeof(CopriCallsMonkeyStore),
JsonConvert.DeserializeObject<StationsAllResponse>(STATIONSALL))));
JsonConvert.DeserializeObject<StationsAvailableResponse>(STATIONSALL))));
var result = await new CachedQuery(server).GetBikesAndStationsAsync();
@ -137,15 +137,15 @@ namespace TestTINKLib.Fixtures.ObjectTests.Connector.Query
{
var server = MockRepository.GenerateMock<ICachedCopriServer>();
server.Stub(x => x.GetStations(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<StationsAllResponse>(
server.Stub(x => x.GetStations(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<StationsAvailableResponse>(
typeof(CopriCallsHttps),
JsonConvert.DeserializeObject<StationsAllResponse>(STATIONSALL))));
JsonConvert.DeserializeObject<StationsAvailableResponse>(STATIONSALL))));
server.Stub(x => x.GetBikesAvailable(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<BikesAvailableResponse>(
typeof(CopriCallsHttps),
JsonConvert.DeserializeObject<BikesAvailableResponse>(BIKESAVAILABLE))));
server.Stub(x => x.AddToCache(Arg<Result<StationsAllResponse>>.Is.Anything));
server.Stub(x => x.AddToCache(Arg<Result<StationsAvailableResponse>>.Is.Anything));
server.Stub(x => x.AddToCache(Arg<Result<BikesAvailableResponse>>.Is.Anything));
var result = await new CachedQuery(server).GetBikesAndStationsAsync();

View file

@ -134,9 +134,9 @@ namespace TestTINKLib.Fixtures.ObjectTests.Connector.Query
{
var server = MockRepository.GenerateMock<ICachedCopriServer>();
server.Stub(x => x.GetStations(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<StationsAllResponse>(
server.Stub(x => x.GetStations(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<StationsAvailableResponse>(
typeof(CopriCallsMonkeyStore),
JsonConvert.DeserializeObject<StationsAllResponse>(STATIONSALL),
JsonConvert.DeserializeObject<StationsAvailableResponse>(STATIONSALL),
new System.Exception("Bang when getting stations..."))));
server.Stub(x => x.GetBikesAvailable(Arg<bool>.Matches(fromCache => fromCache == true))).Return(Task.Run(() => new Result<BikesAvailableResponse>(
@ -160,9 +160,9 @@ namespace TestTINKLib.Fixtures.ObjectTests.Connector.Query
{
var server = MockRepository.GenerateMock<ICachedCopriServer>();
server.Stub(x => x.GetStations(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<StationsAllResponse>(
server.Stub(x => x.GetStations(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<StationsAvailableResponse>(
typeof(CopriCallsHttps),
JsonConvert.DeserializeObject<StationsAllResponse>(STATIONSALLEMPTY))));
JsonConvert.DeserializeObject<StationsAvailableResponse>(STATIONSALLEMPTY))));
server.Stub(x => x.GetBikesAvailable(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<BikesAvailableResponse>(
typeof(CopriCallsMonkeyStore),
@ -173,9 +173,9 @@ namespace TestTINKLib.Fixtures.ObjectTests.Connector.Query
typeof(CopriCallsMonkeyStore),
JsonConvert.DeserializeObject<BikesReservedOccupiedResponse>(BIKESOCCUPIED))));
server.Stub(x => x.GetStations(Arg<bool>.Matches(fromCache => fromCache == true))).Return(Task.Run(() => new Result<StationsAllResponse>(
server.Stub(x => x.GetStations(Arg<bool>.Matches(fromCache => fromCache == true))).Return(Task.Run(() => new Result<StationsAvailableResponse>(
typeof(CopriCallsMonkeyStore),
JsonConvert.DeserializeObject<StationsAllResponse>(STATIONSALL))));
JsonConvert.DeserializeObject<StationsAvailableResponse>(STATIONSALL))));
var result = await new CachedQueryLoggedIn(server, "123", "a@b", () => DateTime.Now).GetBikesAndStationsAsync();
@ -190,9 +190,9 @@ namespace TestTINKLib.Fixtures.ObjectTests.Connector.Query
{
var server = MockRepository.GenerateMock<ICachedCopriServer>();
server.Stub(x => x.GetStations(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<StationsAllResponse>(
server.Stub(x => x.GetStations(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<StationsAvailableResponse>(
typeof(CopriCallsHttps),
JsonConvert.DeserializeObject<StationsAllResponse>(STATIONSALLEMPTY))));
JsonConvert.DeserializeObject<StationsAvailableResponse>(STATIONSALLEMPTY))));
server.Stub(x => x.GetBikesAvailable(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<BikesAvailableResponse>(
typeof(CopriCallsHttps),
@ -207,9 +207,9 @@ namespace TestTINKLib.Fixtures.ObjectTests.Connector.Query
typeof(CopriCallsMonkeyStore),
JsonConvert.DeserializeObject<BikesAvailableResponse>(BIKESAVAILABLE))));
server.Stub(x => x.GetStations(Arg<bool>.Matches(fromCache => fromCache == true))).Return(Task.Run(() => new Result<StationsAllResponse>(
server.Stub(x => x.GetStations(Arg<bool>.Matches(fromCache => fromCache == true))).Return(Task.Run(() => new Result<StationsAvailableResponse>(
typeof(CopriCallsMonkeyStore),
JsonConvert.DeserializeObject<StationsAllResponse>(STATIONSALL))));
JsonConvert.DeserializeObject<StationsAvailableResponse>(STATIONSALL))));
var result = await new CachedQueryLoggedIn(server, "123", "a@b", () => DateTime.Now).GetBikesAndStationsAsync();
@ -224,9 +224,9 @@ namespace TestTINKLib.Fixtures.ObjectTests.Connector.Query
{
var server = MockRepository.GenerateMock<ICachedCopriServer>();
server.Stub(x => x.GetStations(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<StationsAllResponse>(
server.Stub(x => x.GetStations(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<StationsAvailableResponse>(
typeof(CopriCallsHttps),
JsonConvert.DeserializeObject<StationsAllResponse>(STATIONSALL))));
JsonConvert.DeserializeObject<StationsAvailableResponse>(STATIONSALL))));
server.Stub(x => x.GetBikesAvailable(Arg<bool>.Matches(fromCache => fromCache == false))).Return(Task.Run(() => new Result<BikesAvailableResponse>(
typeof(CopriCallsHttps),
@ -236,7 +236,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.Connector.Query
typeof(CopriCallsHttps),
JsonConvert.DeserializeObject<BikesReservedOccupiedResponse>(BIKESOCCUPIED))));
server.Stub(x => x.AddToCache(Arg<Result<StationsAllResponse>>.Is.Anything));
server.Stub(x => x.AddToCache(Arg<Result<StationsAvailableResponse>>.Is.Anything));
server.Stub(x => x.AddToCache(Arg<Result<BikesAvailableResponse>>.Is.Anything));
server.Stub(x => x.AddToCache(Arg<Result<BikesReservedOccupiedResponse>>.Is.Anything));

View file

@ -73,7 +73,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.Query
{
var server = MockRepository.GenerateMock<ICopriServer>();
server.Stub(x => x.GetStationsAsync()).Return(Task.Run(() => JsonConvert.DeserializeObject<StationsAllResponse>(STATIONSALL)));
server.Stub(x => x.GetStationsAsync()).Return(Task.Run(() => JsonConvert.DeserializeObject<StationsAvailableResponse>(STATIONSALL)));
server.Stub(x => x.GetBikesAvailableAsync()).Return(Task.Run(() => JsonConvert.DeserializeObject<BikesAvailableResponse>(BIKESAVAILABLE)));
var result = await new TINK.Model.Connector.Query(server).GetBikesAndStationsAsync();

View file

@ -108,7 +108,7 @@ namespace TestTINKLib.Fixtures.ObjectTests.Connector
{
var server = MockRepository.GenerateMock<ICopriServer>();
server.Stub(x => x.GetStationsAsync()).Return(Task.Run(() => JsonConvert.DeserializeObject<StationsAllResponse>(STATIONSALL)));
server.Stub(x => x.GetStationsAsync()).Return(Task.Run(() => JsonConvert.DeserializeObject<StationsAvailableResponse>(STATIONSALL)));
server.Stub(x => x.GetBikesAvailableAsync()).Return(Task.Run(() => JsonConvert.DeserializeObject<BikesAvailableResponse>(BIKESAVAILABLE)));
server.Stub(x => x.GetBikesOccupiedAsync()).Return(Task.Run(() => JsonConvert.DeserializeObject<BikesReservedOccupiedResponse>(BIKESOCCUPIED)));

View file

@ -228,7 +228,7 @@ namespace TestTINKLib.Fixtures.Connector
[Test]
public void TestGetStationGroup_Invalid()
{
var l_oStation = JsonConvertRethrow.DeserializeObject<StationsAllResponse.StationInfo>(
var l_oStation = JsonConvertRethrow.DeserializeObject<StationsAvailableResponse.StationInfo>(
@"{
""station"" : ""4"",
""gps"" : { ""latitude"": ""47.6586936667"", ""longitude"": ""9.16863116667"" }
@ -241,7 +241,7 @@ namespace TestTINKLib.Fixtures.Connector
[Test]
public void TestGetStationGroup_TINK()
{
var l_oStation = JsonConvertRethrow.DeserializeObject<StationsAllResponse.StationInfo>(
var l_oStation = JsonConvertRethrow.DeserializeObject<StationsAvailableResponse.StationInfo>(
@"{
""station"" : ""4"",
""station_group"" : [ ""TINK"" ],
@ -254,7 +254,7 @@ namespace TestTINKLib.Fixtures.Connector
[Test]
public void TestGetStationGroup_TINKAndKonrad()
{
var l_oStation = JsonConvertRethrow.DeserializeObject<StationsAllResponse.StationInfo>(
var l_oStation = JsonConvertRethrow.DeserializeObject<StationsAvailableResponse.StationInfo>(
@"{
""station"" : ""4"",
""station_group"": [ ""TINK"", ""Konrad"" ],

View file

@ -10,17 +10,20 @@ namespace TestTINKLib.Fixtures.ObjectTests.Station
[Test]
public void TestConstruct()
{
var l_oNull = new NullStation();
var nullStation = new NullStation();
// Was -1 before swiching type of id from int to string when switching from COPRI version v4.0 to v4.1
Assert.That(
l_oNull.Id,
nullStation.Id,
Is.Null);
Assert.AreEqual(0, l_oNull.Group.ToList().Count);
Assert.AreEqual(string.Empty, l_oNull.StationName);
Assert.IsNaN(l_oNull.Position.Latitude);
Assert.IsNaN(l_oNull.Position.Longitude);
Assert.AreEqual(0, nullStation.Group.ToList().Count);
Assert.AreEqual(string.Empty, nullStation.StationName);
Assert.IsNaN(nullStation.Position.Latitude);
Assert.IsNaN(nullStation.Position.Longitude);
Assert.That(
nullStation.OperatorData,
Is.Not.Null);
}
}
}

View file

@ -38,5 +38,13 @@ namespace TestTINKLib.Fixtures.Station
{
Assert.Throws<ArgumentException>(() => new TINK.Model.Station.Station("7", null, new Position(1, 2), "Hallo"));
}
[Test]
public void TestConstruct_NoOperator()
{
Assert.That(
new TINK.Model.Station.Station("7", new List<string>(), new Position(1, 2), "Hallo").OperatorData,
Is.Not.Null);
}
}
}

View file

@ -34,7 +34,7 @@ namespace TestTINKLib.Mocks.Connector
public string MerchantId => server.MerchantId;
public void AddToCache(StationsAllResponse stations)
public void AddToCache(StationsAvailableResponse stations)
{
return;
}
@ -98,7 +98,7 @@ namespace TestTINKLib.Mocks.Connector
return server.GetBikesOccupiedAsync();
}
public Task<StationsAllResponse> GetStationsAsync()
public Task<StationsAvailableResponse> GetStationsAsync()
{
return server.GetStationsAsync();
}

View file

@ -74,7 +74,7 @@ namespace TestTINKLib.Mocks.Connector
throw ExceptionFactory($"Simulated error thrown at {nameof(GetBikesOccupiedAsync)}.");
}
public Task<StationsAllResponse> GetStationsAsync()
public Task<StationsAvailableResponse> GetStationsAsync()
{
throw ExceptionFactory($"Simulated error thrown at {nameof(GetStationsAsync)}.");
}