2022-12-13 10:53:08 +01:00
using System ;
2021-05-13 20:03:07 +02:00
using System.Collections.Generic ;
2022-04-10 17:38:34 +02:00
using System.ComponentModel ;
2024-04-09 12:53:23 +02:00
using System.Linq ;
2021-05-13 20:03:07 +02:00
using System.Threading.Tasks ;
2024-04-09 12:53:23 +02:00
using Acr.Collections ;
2022-08-30 15:42:25 +02:00
using Plugin.Messaging ;
using Serilog ;
2024-04-09 12:53:23 +02:00
using ShareeBike.Model ;
using ShareeBike.Model.Connector ;
using ShareeBike.Model.Stations.StationNS ;
using ShareeBike.MultilingualResources ;
using ShareeBike.View ;
using ShareeBike.ViewModel.Bikes ;
2021-05-13 20:03:07 +02:00
using Xamarin.Essentials ;
using Xamarin.Forms ;
2024-04-09 12:53:23 +02:00
using ICommand = System . Windows . Input . ICommand ;
2021-05-13 20:03:07 +02:00
2024-04-09 12:53:23 +02:00
namespace ShareeBike.ViewModel.Contact
2021-05-13 20:03:07 +02:00
{
2022-09-06 16:08:19 +02:00
/// <summary> View model for contact page.</summary>
public class ContactPageViewModel : INotifyPropertyChanged
{
2024-04-09 12:53:23 +02:00
public Xamarin . Forms . Command ShowSelectStationInfoText { get ; private set ; }
2022-09-06 16:08:19 +02:00
/// <summary>
/// Mail address for app related support.
/// </summary>
public const string APPSUPPORTMAILADDRESS = "hotline@sharee.bike" ;
/// <summary> Reference on view service to show modal notifications and to perform navigation. </summary>
private IViewService ViewService { get ; }
2024-04-09 12:53:23 +02:00
/// <summary> Station selected by user. </summary>
private IBikesViewModel SelectedBike { get ; set ; }
2022-09-06 16:08:19 +02:00
/// <summary> Station selected by user. </summary>
private IStation SelectedStation { get ; set ; } = new NullStation ( ) ;
2024-04-09 12:53:23 +02:00
/// <summary> Holds the name of the app (sharee.bike, ...)</summary>
2022-09-06 16:08:19 +02:00
string AppFlavorName { get ; }
2024-04-09 12:53:23 +02:00
/// <summary> Reference on the shareeBike app instance. </summary>
private IShareeBikeApp ShareeBikeApp { get ; }
2023-08-31 12:31:38 +02:00
2022-09-06 16:08:19 +02:00
/// <summary> Holds a reference to the external trigger service. </summary>
private Action OpenUrlInExternalBrowser { get ; }
Func < string > CreateAttachment { get ; }
2024-04-09 12:53:23 +02:00
/// <summary> Provides a connector object.</summary>
protected Func < bool , IConnector > ConnectorFactory { get ; }
/// <summary> Delegate to retrieve connected state. </summary>
protected Func < bool > IsConnectedDelegate { get ; }
2022-09-06 16:08:19 +02:00
/// <summary> Notifies view about changes. </summary>
public event PropertyChangedEventHandler PropertyChanged ;
/// <summary> Constructs a contact page view model. </summary>
/// <param name="openUrlInExternalBrowser">Action to open an external browser.</param>
2024-04-09 12:53:23 +02:00
/// <param name="user">Mail address of active user.</param>
/// <param name="isReportLevelVerbose">True if report level is verbose, false if not.</param>
/// <param name="permissions">Holds object to query location permissions.</param>
/// <param name="bluetoothLE">Holds object to query bluetooth state.</param>
/// <param name="runtimPlatform">Specifies on which platform code is run.</param>
/// <param name="isConnectedDelegate">Returns if mobile is connected to web or not.</param>
/// <param name="connectorFactory">Connects system to copri.</param>
/// <param name="lockService">Service to control lock retrieve info.</param>
/// <param name="stations">Stations to get station name from station id.</param>
/// <param name="polling"> Holds whether to poll or not and the period length is polling is on. </param>
/// <param name="postAction">Executes actions on GUI thread.</param>
/// <param name="smartDevice">Provides info about the smart device (phone, tablet, ...).</param>
/// <param name="viewService">Interface to actuate methods on GUI.</param>
2022-09-06 16:08:19 +02:00
public ContactPageViewModel (
string appFlavorName ,
2024-04-09 12:53:23 +02:00
IShareeBikeApp shareeBikeApp ,
2022-09-06 16:08:19 +02:00
Func < string > createAttachment ,
Action openUrlInExternalBrowser ,
2024-04-09 12:53:23 +02:00
IViewService viewService ,
Func < bool > isConnectedDelegate ,
Func < bool , IConnector > connectorFactory )
2022-09-06 16:08:19 +02:00
{
AppFlavorName = ! string . IsNullOrEmpty ( appFlavorName )
? appFlavorName
2023-04-19 12:14:14 +02:00
: throw new ArgumentException ( "Can not instantiate contact page view model- object. No app name centered." ) ;
2022-09-06 16:08:19 +02:00
2024-04-09 12:53:23 +02:00
ShareeBikeApp = shareeBikeApp
? ? throw new ArgumentException ( "Can not instantiate settings page view model- object. No shareeBike app object available." ) ;
2023-08-31 12:31:38 +02:00
2022-09-06 16:08:19 +02:00
CreateAttachment = createAttachment
? ? throw new ArgumentException ( "Can not instantiate contact page view model- object. No create attachment provider available." ) ;
ViewService = viewService
? ? throw new ArgumentException ( "Can not instantiate contact page view model- object. No user view service available." ) ;
OpenUrlInExternalBrowser = openUrlInExternalBrowser
? ? throw new ArgumentException ( "Can not instantiate contact page view model- object. No user external browse service available." ) ;
2024-04-09 12:53:23 +02:00
ConnectorFactory = connectorFactory
? ? throw new ArgumentException ( "Can not instantiate bikes page view model- object. No connector available." ) ;
IsConnectedDelegate = isConnectedDelegate
? ? throw new ArgumentException ( "Can not instantiate bikes page view model- object. No is connected delegate available." ) ;
ShowSelectStationInfoText = new Xamarin . Forms . Command ( async ( ) = > {
await ViewService . DisplayAlert (
AppResources . MarkingLastSelectedStation ,
AppResources . MarkingContactNoStationInfoAvailableNoButton ,
AppResources . MessageAnswerOk ) ;
} ) ;
}
public ICommand OnFAQClickedRequest
= > new Xamarin . Forms . Command ( async ( ) = > await OpenFAQAsync ( ) ) ;
/// <summary> Opens Help page, FAQ. </summary>
public async Task OpenFAQAsync ( )
{
try
{
await ViewService . PushAsync ( ViewTypes . HelpPage ) ;
}
catch ( Exception exception )
{
Log . ForContext < ContactPageViewModel > ( ) . Error ( "Fehler beim Öffnen der Seite 'Hilfe' aufgetreten. {Exception}" , exception ) ;
await ViewService . DisplayAlert (
AppResources . ErrorPageNotLoadedTitle ,
$"{AppResources.ErrorPageNotLoaded}\r\n{exception.Message}" ,
AppResources . MessageAnswerOk ) ;
return ;
}
2022-09-06 16:08:19 +02:00
}
/// <summary> Is invoked when page is shown. </summary>
public async Task OnAppearing (
IStation selectedStation )
{
if ( SelectedStation ? . Id = = selectedStation ? . Id )
{
// Nothing to do because either both are null or of same id.
return ;
}
SelectedStation = selectedStation ;
2023-07-19 10:10:36 +02:00
PropertyChanged ? . Invoke ( this , new PropertyChangedEventArgs ( nameof ( SelectedStationId ) ) ) ;
PropertyChanged ? . Invoke ( this , new PropertyChangedEventArgs ( nameof ( SelectedStationName ) ) ) ;
2022-09-06 16:08:19 +02:00
PropertyChanged ? . Invoke ( this , new PropertyChangedEventArgs ( nameof ( MailAddressText ) ) ) ;
PropertyChanged ? . Invoke ( this , new PropertyChangedEventArgs ( nameof ( OfficeHoursText ) ) ) ;
PropertyChanged ? . Invoke ( this , new PropertyChangedEventArgs ( nameof ( PhoneNumberText ) ) ) ;
PropertyChanged ? . Invoke ( this , new PropertyChangedEventArgs ( nameof ( ProviderNameText ) ) ) ;
PropertyChanged ? . Invoke ( this , new PropertyChangedEventArgs ( nameof ( IsOperatorInfoAvaliable ) ) ) ;
2023-09-22 11:38:42 +02:00
PropertyChanged ? . Invoke ( this , new PropertyChangedEventArgs ( nameof ( IsDoPhoncallAvailable ) ) ) ;
PropertyChanged ? . Invoke ( this , new PropertyChangedEventArgs ( nameof ( IsSendMailAvailable ) ) ) ;
2022-09-06 16:08:19 +02:00
await Task . CompletedTask ;
}
/// <summary> Command object to bind mail button to view model. </summary>
public ICommand OnMailToOperatorRequest
2024-04-09 12:53:23 +02:00
= > new Xamarin . Forms . Command ( async ( ) = > await DoSendMailToOperator ( ) ) ;
2022-09-06 16:08:19 +02:00
2023-04-19 12:14:14 +02:00
/// <summary> Command object to bind mail app related button to model. </summary>
2022-09-06 16:08:19 +02:00
public ICommand OnMailAppRelatedRequest
2024-04-09 12:53:23 +02:00
= > new Xamarin . Forms . Command ( async ( ) = > await DoSendMailAppRelated ( ) ) ;
2022-09-06 16:08:19 +02:00
/// <summary>True if sending mail is possible.</summary>
2023-09-22 11:38:42 +02:00
public bool IsSendMailAvailable
= > CrossMessaging . Current . EmailMessenger . CanSendEmail ;
2022-09-06 16:08:19 +02:00
/// <summary>cTrue if doing a phone call is possible.</summary>
public bool IsDoPhoncallAvailable
= > CrossMessaging . Current . PhoneDialer . CanMakePhoneCall ;
/// <summary>Holds the mail address to mail to.</summary>
public string MailAddressText
= > SelectedStation ? . OperatorData ? . MailAddressText ? ? string . Empty ;
/// <summary>Holds the mail address to send mail to.</summary>
public string PhoneNumberText
= > SelectedStation ? . OperatorData ? . PhoneNumberText ? ? string . Empty ;
/// <summary>Holds the mail address to send mail to.</summary>
public string OfficeHoursText
= > SelectedStation ? . OperatorData ? . Hours ? ? string . Empty ;
2023-04-19 12:14:14 +02:00
/// <summary> Gets whether any operator support info is available. </summary>
2022-09-06 16:08:19 +02:00
public bool IsOperatorInfoAvaliable
= > MailAddressText . Length > 0 | | PhoneNumberText . Length > 0 ;
2023-04-19 12:14:14 +02:00
/// <returns> Returns true if either mail was sent or if no mailer available.</returns>
2022-09-06 16:08:19 +02:00
public async Task DoSendMailToOperator ( )
{
2023-09-22 11:38:42 +02:00
if ( ! IsSendMailAvailable )
2022-09-06 16:08:19 +02:00
{
2023-09-22 11:38:42 +02:00
await ViewService . DisplayAlert (
String . Empty ,
AppResources . ErrorSupportmailMailingFailed ,
AppResources . MessageAnswerOk ) ;
return ;
}
else
{
try
2022-09-06 16:08:19 +02:00
{
2023-09-22 11:38:42 +02:00
// Send operator related support mail to operator.
await Email . ComposeAsync ( new EmailMessage
{
To = new List < string > { MailAddressText } ,
Cc = APPSUPPORTMAILADDRESS . ToUpper ( ) ! = MailAddressText . ToUpper ( ) // do not sent copy if same mail address
? new List < string > { APPSUPPORTMAILADDRESS } : new List < string > ( ) ,
Subject = string . Format ( AppResources . SupportmailSubjectOperatormail , AppFlavorName , SelectedStation ? . Id ) ,
2024-04-09 12:53:23 +02:00
Body = ShareeBikeApp . ActiveUser . Mail ! = null ? $"{AppResources.SupportmailBodyText}\r\n\r\n\r\n\r\n{string.Format(AppResources.MarkingLoggedInStateInfoLoggedIn, ShareeBikeApp.ActiveUser.Mail)}" : $"{AppResources.SupportmailBodyText}\r\n\r\n\r\n\r\n{string.Format(AppResources.SupportmailBodyNotLoggedIn)}"
2023-09-22 11:38:42 +02:00
} ) ;
2022-09-06 16:08:19 +02:00
return ;
}
2023-09-22 11:38:42 +02:00
catch ( Exception exception )
2022-09-06 16:08:19 +02:00
{
2023-09-22 11:38:42 +02:00
Log . Error ( "An unexpected error occurred sending mail to operator. {@Exception}" , exception ) ;
await ViewService . DisplayAdvancedAlert (
AppResources . MessageWaring ,
AppResources . ErrorSupportmailMailingFailed ,
exception . Message ,
AppResources . MessageAnswerOk ) ;
return ;
}
2022-09-06 16:08:19 +02:00
}
}
/// <summary> Request to send a app related mail. </summary>
public async Task DoSendMailAppRelated ( )
{
2023-09-22 11:38:42 +02:00
if ( ! IsSendMailAvailable )
2022-09-06 16:08:19 +02:00
{
2023-07-19 10:10:36 +02:00
await ViewService . DisplayAlert (
2023-09-22 11:38:42 +02:00
String . Empty ,
AppResources . ErrorSupportmailMailingFailed ,
2023-07-19 10:10:36 +02:00
AppResources . MessageAnswerOk ) ;
2023-09-22 11:38:42 +02:00
return ;
}
else
{
2022-09-06 16:08:19 +02:00
try
{
2023-09-22 11:38:42 +02:00
// Ask for permission to append diagnostics.
await ViewService . DisplayAlert (
AppResources . QuestionSupportmailAttachmentTitle ,
AppResources . QuestionSupportmailAttachment ,
AppResources . MessageAnswerOk ) ;
var message = new EmailMessage
{
To = new List < string > { APPSUPPORTMAILADDRESS } ,
Subject = SelectedStation ? . Id ! = null ? string . Format ( AppResources . SupportmailSubjectAppmailWithStation , AppFlavorName , SelectedStation ? . Id ) : string . Format ( AppResources . SupportmailSubjectAppmail , AppFlavorName ) ,
2024-04-09 12:53:23 +02:00
Body = ShareeBikeApp . ActiveUser . Mail ! = null ? $"{AppResources.SupportmailBodyText}\r\n\r\n\r\n\r\n{string.Format(AppResources.MarkingLoggedInStateInfoLoggedIn, ShareeBikeApp.ActiveUser.Mail)}" : $"{AppResources.SupportmailBodyText}\r\n\r\n\r\n\r\n{string.Format(AppResources.SupportmailBodyNotLoggedIn)}"
2023-09-22 11:38:42 +02:00
} ;
// Send with attachment.
var logFileName = string . Empty ;
try
{
logFileName = CreateAttachment ( ) ;
}
catch ( Exception exception )
{
await ViewService . DisplayAdvancedAlert (
AppResources . MessageWaring ,
AppResources . ErrorSupportmailCreateAttachment ,
exception . Message ,
AppResources . MessageAnswerOk ) ;
Log . ForContext < ContactPageViewModel > ( ) . Error ( "An error occurred creating attachment for app mail. {@Exception)" , exception ) ;
}
if ( ! string . IsNullOrEmpty ( logFileName ) )
{
message . Attachments . Add ( new Xamarin . Essentials . EmailAttachment ( logFileName ) ) ;
}
2024-04-09 12:53:23 +02:00
// Send a shareeBike app related mail
2023-09-22 11:38:42 +02:00
await Email . ComposeAsync ( message ) ;
2022-09-06 16:08:19 +02:00
}
catch ( Exception exception )
{
2023-09-22 11:38:42 +02:00
Log . ForContext < ContactPageViewModel > ( ) . Error ( "An unexpected error occurred sending mail. {@Exception}" , exception ) ;
2022-09-06 16:08:19 +02:00
await ViewService . DisplayAdvancedAlert (
AppResources . MessageWaring ,
2023-09-22 11:38:42 +02:00
AppResources . ErrorSupportmailMailingFailed ,
2022-09-06 16:08:19 +02:00
exception . Message ,
AppResources . MessageAnswerOk ) ;
2023-09-22 11:38:42 +02:00
return ;
2022-09-06 16:08:19 +02:00
}
}
}
/// <summary> Command object to bind login button to view model. </summary>
public ICommand OnSelectStationRequest
= > new Xamarin . Forms . Command ( async ( ) = > await OpenSelectStationPageAsync ( ) ) ;
2021-07-22 22:41:35 +02:00
2022-09-06 16:08:19 +02:00
/// <summary> Opens login page. </summary>
public async Task OpenSelectStationPageAsync ( )
{
try
{
// Switch to map page
2021-07-22 22:41:35 +02:00
2022-09-06 16:08:19 +02:00
await ViewService . PushAsync ( ViewTypes . SelectStationPage ) ;
}
catch ( Exception p_oException )
{
Log . Error ( "Ein unerwarteter Fehler ist in der Klasse ContactPageViewModel aufgetreten. Kontext: Klick auf Hinweistext auf Station N- seite ohne Anmeldung. {@Exception}" , p_oException ) ;
return ;
}
}
/// <summary> Command object to bind phone call button. </summary>
public ICommand OnPhoneRequest
2024-04-09 12:53:23 +02:00
= > new Xamarin . Forms . Command ( async ( ) = > await DoPhoneCall ( ) ) ;
2022-09-06 16:08:19 +02:00
/// <summary> Request to do a phone call. </summary>
public async Task DoPhoneCall ( )
{
2023-09-22 11:38:42 +02:00
if ( ! IsDoPhoncallAvailable )
2022-09-06 16:08:19 +02:00
{
2023-09-22 11:38:42 +02:00
await ViewService . DisplayAlert (
String . Empty ,
2022-09-06 16:08:19 +02:00
AppResources . ErrorSupportmailPhoningFailed ,
AppResources . MessageAnswerOk ) ;
return ;
}
2023-09-22 11:38:42 +02:00
else
{
try
{
// Make Phone Call
CrossMessaging . Current . PhoneDialer . MakePhoneCall ( PhoneNumberText ) ;
}
catch ( Exception exception )
{
Log . Error ( "An unexpected error occurred doing a phone call. {@Exception}" , exception ) ;
await ViewService . DisplayAdvancedAlert (
AppResources . MessageWaring ,
AppResources . ErrorSupportmailPhoningFailed ,
exception . Message ,
AppResources . MessageAnswerOk ) ;
return ;
}
}
2022-09-06 16:08:19 +02:00
}
2023-07-19 10:10:36 +02:00
/// <summary> Text providing the id of the selected station.</summary>
public string SelectedStationId
2023-08-31 12:20:06 +02:00
= > SelectedStation ? . Id ! = null ? SelectedStation ? . Id : string . Empty ;
2022-09-06 16:08:19 +02:00
2023-07-19 10:10:36 +02:00
public string SelectedStationName
= > SelectedStation ? . StationName ;
2022-09-06 16:08:19 +02:00
2023-07-19 10:10:36 +02:00
/// <summary> Text providing the name of the operator of the selected station </summary>
2022-09-06 16:08:19 +02:00
public string ProviderNameText
2023-07-19 10:10:36 +02:00
= > SelectedStation ? . OperatorData ? . Name ;
/// <summary> Text providing mail address and possible reasons to contact. </summary>
2022-09-06 16:08:19 +02:00
public FormattedString MailAddressAndMotivationsText
{
get
{
var hint = new FormattedString ( ) ;
hint . Spans . Add ( new Span { Text = AppResources . MessageContactMail } ) ;
return hint ;
}
}
/// <summary> Invitation to rate app.</summary>
public FormattedString PhoneContactText
{
get
{
var l_oHint = new FormattedString ( ) ;
l_oHint . Spans . Add ( new Span
{
Text = string . Format ( AppResources . MessagePhoneMail , AppFlavorName ) + $"{(OfficeHoursText.Length > 0 ? $" { OfficeHoursText } " : string.Empty)}"
} ) ;
return l_oHint ;
}
}
}
2021-05-13 20:03:07 +02:00
}