2022-08-30 15:42:25 +02: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 ;
2021-05-13 20:03:07 +02:00
using System.Threading.Tasks ;
using System.Windows.Input ;
2022-08-30 15:42:25 +02:00
using Plugin.Messaging ;
using Serilog ;
2021-05-13 20:03:07 +02:00
using TINK.Model.Services.CopriApi.ServerUris ;
2021-07-20 23:06:09 +02:00
using TINK.Model.Station ;
2021-05-13 20:03:07 +02:00
using TINK.MultilingualResources ;
using TINK.View ;
using Xamarin.Essentials ;
using Xamarin.Forms ;
namespace TINK.ViewModel.Info
{
/// <summary> View model for contact page.</summary>
2022-04-10 17:38:34 +02:00
public class ContactPageViewModel : INotifyPropertyChanged
2021-05-13 20:03:07 +02:00
{
2022-08-30 15:42:25 +02:00
/// <summary>
/// Mail address for app related support.
/// </summary>
public const string APPSUPPORTMAILADDRESS = "hotline@sharee.bike" ;
2021-05-13 20:03:07 +02:00
/// <summary> Reference on view service to show modal notifications and to perform navigation. </summary>
private IViewService ViewService { get ; }
2021-07-20 23:06:09 +02:00
/// <summary> Station selected by user. </summary>
2022-04-10 17:38:34 +02:00
private IStation SelectedStation { get ; set ; } = new NullStation ( ) ;
2021-07-20 23:06:09 +02:00
2021-05-13 20:03:07 +02:00
Uri ActiveUri { get ; }
2022-04-10 17:38:34 +02:00
/// <summary> Holds the name of the app (sharee.bike, Mein konrad, ...)</summary>
string AppName { get ; }
2021-05-13 20:03:07 +02:00
/// <summary> Holds a reference to the external trigger service. </summary>
private Action OpenUrlInExternalBrowser { get ; }
Func < string > CreateAttachment { get ; }
2022-04-10 17:38:34 +02:00
/// <summary> Notifies view about changes. </summary>
public event PropertyChangedEventHandler PropertyChanged ;
2021-05-13 20:03:07 +02:00
/// <summary> Constructs a contact page view model. </summary>
/// <param name="openUrlInExternalBrowser">Action to open an external browser.</param>
/// <param name="viewService">View service to notify user.</param>
public ContactPageViewModel (
Uri activeUri ,
2022-04-10 17:38:34 +02:00
string appName ,
2021-05-13 20:03:07 +02:00
Func < string > createAttachment ,
Action openUrlInExternalBrowser ,
IViewService viewService )
{
ActiveUri = activeUri
? ? throw new ArgumentException ( "Can not instantiate contact page view model- object. No active uri available." ) ;
2022-04-10 17:38:34 +02:00
AppName = ! string . IsNullOrEmpty ( appName )
? appName
: throw new ArgumentException ( "Can not instantiate contact page view model- object. No app name availalbe." ) ;
2021-05-13 20:03:07 +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." ) ;
2022-04-10 17:38:34 +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 ;
}
2021-07-20 23:06:09 +02:00
SelectedStation = selectedStation ;
2022-04-10 17:38:34 +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 ) ) ) ;
await Task . CompletedTask ;
2021-05-13 20:03:07 +02:00
}
2022-08-30 15:42:25 +02:00
/// <summary> Command object to bind mail button to view model. </summary>
2021-05-13 20:03:07 +02:00
public ICommand OnMailRequest
= > new Command (
2022-08-30 15:42:25 +02:00
async ( ) = >
{
if ( await DoSendMailToOperator ( ) )
{
// Mail sent to operator or no mailer availble.
return ;
}
await DoSendMailAppRelated (
new List < string > { MailAddressText } ,
MailAddressText . ToUpper ( ) ! = APPSUPPORTMAILADDRESS . ToUpper ( )
? new List < string > { APPSUPPORTMAILADDRESS }
: new List < string > ( ) ) ;
} ,
2021-05-13 20:03:07 +02:00
( ) = > IsSendMailAvailable ) ;
2022-08-30 15:42:25 +02:00
/// <summary> Command object to bind mail app releated button to model. </summary>
public ICommand OnSendAppFeedbackMailRequest
= > new Command (
async ( ) = > await DoSendMailToOperator ( ) ,
( ) = > IsSendMailAvailable ) ;
/// <summary> Command object to bind mail app releated button to model. </summary>
public ICommand OnMailAppRelatedRequest
= > new Command (
async ( ) = > await DoSendMailAppRelated ( new List < string > { APPSUPPORTMAILADDRESS } , new List < string > ( ) ) ,
( ) = > IsSendMailAvailable ) ;
2021-05-13 20:03:07 +02:00
/// <summary>True if sending mail is possible.</summary>
public bool IsSendMailAvailable = >
CrossMessaging . Current . EmailMessenger . CanSendEmail ;
2022-08-30 15:42:25 +02:00
2021-05-13 20:03:07 +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
2021-07-20 23:06:09 +02:00
= > SelectedStation ? . OperatorData ? . MailAddressText ? ? string . Empty ;
2021-05-13 20:03:07 +02:00
/// <summary>Holds the mail address to send mail to.</summary>
public string PhoneNumberText
2021-07-20 23:06:09 +02:00
= > SelectedStation ? . OperatorData ? . PhoneNumberText ? ? string . Empty ;
2021-05-13 20:03:07 +02:00
2021-07-20 23:06:09 +02:00
/// <summary>Holds the mail address to send mail to.</summary>
public string OfficeHoursText
= > SelectedStation ? . OperatorData ? . Hours ? ? string . Empty ;
2021-05-13 20:03:07 +02:00
2021-07-20 23:06:09 +02:00
/// <summary> Gets whether any operator support info is avaliable. </summary>
public bool IsOperatorInfoAvaliable
= > MailAddressText . Length > 0 | | PhoneNumberText . Length > 0 ;
2021-05-13 20:03:07 +02:00
2022-08-30 15:42:25 +02:00
/// <returns> Returns true if eithe mail was sent or if no mailer available.</returns>
public async Task < bool > DoSendMailToOperator ( )
2021-05-13 20:03:07 +02:00
{
try
{
if ( ! IsSendMailAvailable )
{
// Nothing to do because email can not be sent.
2022-08-30 15:42:25 +02:00
return true ;
2021-05-13 20:03:07 +02:00
}
2022-08-30 15:42:25 +02:00
// Ask whether mail is operator or app specific.
var operatorRelatedMail = await ViewService . DisplayAlert (
2021-05-13 20:03:07 +02:00
AppResources . QuestionTitle ,
string . Format ( AppResources . QuestionSupportmailSubject , GetAppName ( ActiveUri ) ) ,
string . Format ( AppResources . QuestionSupportmailAnswerOperator , GetAppName ( ActiveUri ) ) ,
string . Format ( AppResources . QuestionSupportmailAnswerApp , GetAppName ( ActiveUri ) ) ) ;
2022-08-30 15:42:25 +02:00
if ( operatorRelatedMail )
2021-05-13 20:03:07 +02:00
{
2022-08-30 15:42:25 +02:00
// Send operator related support mail.
2021-05-13 20:03:07 +02:00
await Email . ComposeAsync ( new EmailMessage { To = new List < string > { MailAddressText } , Subject = $"{GetAppName(ActiveUri)} Anfrage" } ) ;
2022-08-30 15:42:25 +02:00
return true ;
2021-05-13 20:03:07 +02:00
}
2022-08-30 15:42:25 +02:00
return false ;
}
catch ( Exception exception )
{
Log . Error ( "An unexpected error occurred sending mail to operator. {@Exception}" , exception ) ;
await ViewService . DisplayAdvancedAlert (
AppResources . MessageWaring ,
AppResources . ErrorSupportmailMailingFailed ,
exception . Message ,
AppResources . MessageAnswerOk ) ;
return true ;
}
}
/// <summary> Request to send a app related mail. </summary>
public async Task DoSendMailAppRelated ( List < string > to , List < string > cc )
{
try
{
// Send app related mail
2021-05-13 20:03:07 +02:00
var appendFile = false ;
appendFile = await ViewService . DisplayAlert (
AppResources . QuestionTitle ,
AppResources . QuestionSupportmailAttachment ,
AppResources . QuestionAnswerYes ,
AppResources . QuestionAnswerNo ) ;
2022-08-30 15:42:25 +02:00
var message = new EmailMessage
{
To = to ,
Cc = cc ,
Subject = $"{GetAppName(ActiveUri)}-App Anfrage"
} ;
2021-05-13 20:03:07 +02:00
if ( appendFile = = false )
{
2022-08-30 15:42:25 +02:00
// Send without attachment
2021-05-13 20:03:07 +02:00
await Email . ComposeAsync ( message ) ;
return ;
}
2022-08-30 15:42:25 +02:00
// Send with attachment.
2021-05-13 20:03:07 +02:00
var logFileName = string . Empty ;
try
{
logFileName = CreateAttachment ( ) ;
}
2022-08-30 15:42:25 +02:00
catch ( Exception exception )
2021-05-13 20:03:07 +02:00
{
await ViewService . DisplayAdvancedAlert (
AppResources . MessageWaring ,
AppResources . ErrorSupportmailCreateAttachment ,
2022-08-30 15:42:25 +02:00
exception . Message ,
2021-05-13 20:03:07 +02:00
AppResources . MessageAnswerOk ) ;
2022-08-30 15:42:25 +02:00
Log . Error ( "An error occurred creating attachment. {@Exception)" , exception ) ;
2021-05-13 20:03:07 +02:00
}
if ( ! string . IsNullOrEmpty ( logFileName ) )
{
message . Attachments . Add ( new Xamarin . Essentials . EmailAttachment ( logFileName ) ) ;
}
// Send a tink app related mail
await Email . ComposeAsync ( message ) ;
}
2022-08-30 15:42:25 +02:00
catch ( Exception exception )
2021-05-13 20:03:07 +02:00
{
2022-08-30 15:42:25 +02:00
Log . Error ( "An unexpected error occurred sending mail. {@Exception}" , exception ) ;
2021-05-13 20:03:07 +02:00
await ViewService . DisplayAdvancedAlert (
AppResources . MessageWaring ,
2022-08-30 15:42:25 +02:00
AppResources . ErrorSupportmailMailingFailed ,
exception . Message ,
2021-05-13 20:03:07 +02:00
AppResources . MessageAnswerOk ) ;
return ;
}
}
/// <summary> Command object to bind login button to view model. </summary>
2021-07-22 22:41:35 +02:00
public ICommand OnSelectStationRequest
2021-08-28 10:04:10 +02:00
#if USEFLYOUT
2021-07-22 22:41:35 +02:00
= > new Xamarin . Forms . Command ( ( ) = > OpenSelectStationPage ( ) ) ;
#else
= > new Xamarin . Forms . Command ( async ( ) = > await OpenSelectStationPageAsync ( ) ) ;
#endif
/// <summary> Opens login page. </summary>
2021-08-28 10:04:10 +02:00
#if USEFLYOUT
2021-07-22 22:41:35 +02:00
public void OpenSelectStationPage ( )
#else
public async Task OpenSelectStationPageAsync ( )
#endif
{
try
{
// Switch to map page
2021-08-28 10:04:10 +02:00
#if USEFLYOUT
2021-07-22 22:41:35 +02:00
ViewService . PushAsync ( ViewTypes . SelectStationPage ) ;
#else
2022-04-10 17:38:34 +02:00
await ViewService . PushAsync ( ViewTypes . SelectStationPage ) ;
2021-07-22 22:41:35 +02:00
#endif
}
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>
2021-05-13 20:03:07 +02:00
public ICommand OnPhoneRequest
= > new Command (
async ( ) = > await DoPhoneCall ( ) ,
( ) = > IsDoPhoncallAvailable ) ;
/// <summary> Request to do a phone call. </summary>
public async Task DoPhoneCall ( )
{
try
{
// Make Phone Call
if ( IsDoPhoncallAvailable )
{
CrossMessaging . Current . PhoneDialer . MakePhoneCall ( PhoneNumberText ) ;
}
}
2022-08-30 15:42:25 +02:00
catch ( Exception exception )
2021-05-13 20:03:07 +02:00
{
2022-08-30 15:42:25 +02:00
Log . Error ( "An unexpected error occurred doing a phone call. {@Exception}" , exception ) ;
2021-05-13 20:03:07 +02:00
await ViewService . DisplayAdvancedAlert (
AppResources . MessageWaring ,
2022-08-30 15:42:25 +02:00
AppResources . ErrorSupportmailPhoningFailed ,
exception . Message ,
2021-05-13 20:03:07 +02:00
AppResources . MessageAnswerOk ) ;
return ;
}
}
2021-08-01 17:24:15 +02:00
/// <summary> Text providing mail address and possilbe reasons to contact. </summary>
public string ProviderNameText
= > string . Format ( "Betreiber: {0}" , SelectedStation ? . OperatorData ? . Name )
;
2021-05-13 20:03:07 +02:00
/// <summary> Text providing mail address and possilbe reasons to contact. </summary>
2021-08-01 17:24:15 +02:00
public FormattedString MailAddressAndMotivationsText
2021-05-13 20:03:07 +02:00
{
get
{
var hint = new FormattedString ( ) ;
hint . Spans . Add ( new Span { Text = AppResources . MessageContactMail } ) ;
return hint ;
}
}
/// <summary> Invitation to rate app.</summary>
public FormattedString LikeTinkApp
{
get
{
var l_oHint = new FormattedString ( ) ;
2022-04-10 17:38:34 +02:00
l_oHint . Spans . Add ( new Span { Text = string . Format ( AppResources . MessageRateMail , AppName ) } ) ;
2021-05-13 20:03:07 +02:00
return l_oHint ;
}
}
/// <summary> User clicks rate button.</summary>
public ICommand OnRateRequest
= > new Command ( ( ) = > RegisterRequest ( ) ) ;
/// <summary> Opens login page.</summary>
public void RegisterRequest ( )
{
try
{
OpenUrlInExternalBrowser ( ) ;
}
catch ( Exception p_oException )
{
Log . Error ( "Ein unerwarteter Fehler ist auf der Login Seite beim Öffnen eines Browsers, Seite {url}, aufgetreten. {@Exception}" , p_oException ) ;
return ;
}
}
/// <summary> Invitation to rate app.</summary>
public FormattedString PhoneContactText
{
get
{
var l_oHint = new FormattedString ( ) ;
2021-07-20 23:06:09 +02:00
l_oHint . Spans . Add ( new Span
{
Text = string . Format ( AppResources . MessagePhoneMail , GetAppName ( ActiveUri ) ) + $"{(OfficeHoursText.Length > 0 ? $" { OfficeHoursText } " : string.Empty)}"
} ) ;
2021-05-13 20:03:07 +02:00
return l_oHint ;
}
}
/// <summary>Gets the application name.</summary>
public static string GetAppName ( Uri activeUri )
{
switch ( activeUri . AbsoluteUri )
{
case CopriServerUriList . TINK_DEVEL :
case CopriServerUriList . TINK_LIVE :
return "TINK" ;
case CopriServerUriList . SHAREE_DEVEL :
case CopriServerUriList . SHAREE_LIVE :
return "sharee.bike" ;
default :
return "Teilrad" ;
}
}
}
}