using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using Acr.Collections;
using Plugin.Messaging;
using Serilog;
using ShareeBike.Model;
using ShareeBike.Model.Connector;
using ShareeBike.Model.Stations.StationNS;
using ShareeBike.MultilingualResources;
using ShareeBike.View;
using ShareeBike.ViewModel.Bikes;
using Xamarin.Essentials;
using Xamarin.Forms;
using ICommand = System.Windows.Input.ICommand;
namespace ShareeBike.ViewModel.Contact
{
/// View model for contact page.
public class ContactPageViewModel : INotifyPropertyChanged
{
public Xamarin.Forms.Command ShowSelectStationInfoText { get; private set; }
///
/// Mail address for app related support.
///
public const string APPSUPPORTMAILADDRESS = "hotline@sharee.bike";
/// Reference on view service to show modal notifications and to perform navigation.
private IViewService ViewService { get; }
/// Station selected by user.
private IBikesViewModel SelectedBike { get; set; }
/// Station selected by user.
private IStation SelectedStation { get; set; } = new NullStation();
/// Holds the name of the app (sharee.bike, ...)
string AppFlavorName { get; }
/// Reference on the shareeBike app instance.
private IShareeBikeApp ShareeBikeApp { get; }
/// Holds a reference to the external trigger service.
private Action OpenUrlInExternalBrowser { get; }
Func CreateAttachment { get; }
/// Provides a connector object.
protected Func ConnectorFactory { get; }
/// Delegate to retrieve connected state.
protected Func IsConnectedDelegate { get; }
/// Notifies view about changes.
public event PropertyChangedEventHandler PropertyChanged;
/// Constructs a contact page view model.
/// Action to open an external browser.
/// Mail address of active user.
/// True if report level is verbose, false if not.
/// Holds object to query location permissions.
/// Holds object to query bluetooth state.
/// Specifies on which platform code is run.
/// Returns if mobile is connected to web or not.
/// Connects system to copri.
/// Service to control lock retrieve info.
/// Stations to get station name from station id.
/// Holds whether to poll or not and the period length is polling is on.
/// Executes actions on GUI thread.
/// Provides info about the smart device (phone, tablet, ...).
/// Interface to actuate methods on GUI.
public ContactPageViewModel(
string appFlavorName,
IShareeBikeApp shareeBikeApp,
Func createAttachment,
Action openUrlInExternalBrowser,
IViewService viewService,
Func isConnectedDelegate,
Func connectorFactory)
{
AppFlavorName = !string.IsNullOrEmpty(appFlavorName)
? appFlavorName
: throw new ArgumentException("Can not instantiate contact page view model- object. No app name centered.");
ShareeBikeApp = shareeBikeApp
?? throw new ArgumentException("Can not instantiate settings page view model- object. No shareeBike app object available.");
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.");
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());
/// Opens Help page, FAQ.
public async Task OpenFAQAsync()
{
try
{
await ViewService.PushAsync(ViewTypes.HelpPage);
}
catch (Exception exception)
{
Log.ForContext().Error("Fehler beim Öffnen der Seite 'Hilfe' aufgetreten. {Exception}", exception);
await ViewService.DisplayAlert(
AppResources.ErrorPageNotLoadedTitle,
$"{AppResources.ErrorPageNotLoaded}\r\n{exception.Message}",
AppResources.MessageAnswerOk);
return;
}
}
/// Is invoked when page is shown.
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;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedStationId)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedStationName)));
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)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsDoPhoncallAvailable)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsSendMailAvailable)));
await Task.CompletedTask;
}
/// Command object to bind mail button to view model.
public ICommand OnMailToOperatorRequest
=> new Xamarin.Forms.Command(async () => await DoSendMailToOperator());
/// Command object to bind mail app related button to model.
public ICommand OnMailAppRelatedRequest
=> new Xamarin.Forms.Command(async () => await DoSendMailAppRelated());
/// True if sending mail is possible.
public bool IsSendMailAvailable
=> CrossMessaging.Current.EmailMessenger.CanSendEmail;
/// cTrue if doing a phone call is possible.
public bool IsDoPhoncallAvailable
=> CrossMessaging.Current.PhoneDialer.CanMakePhoneCall;
/// Holds the mail address to mail to.
public string MailAddressText
=> SelectedStation?.OperatorData?.MailAddressText ?? string.Empty;
/// Holds the mail address to send mail to.
public string PhoneNumberText
=> SelectedStation?.OperatorData?.PhoneNumberText ?? string.Empty;
/// Holds the mail address to send mail to.
public string OfficeHoursText
=> SelectedStation?.OperatorData?.Hours ?? string.Empty;
/// Gets whether any operator support info is available.
public bool IsOperatorInfoAvaliable
=> MailAddressText.Length > 0 || PhoneNumberText.Length > 0;
/// Returns true if either mail was sent or if no mailer available.
public async Task DoSendMailToOperator()
{
if (!IsSendMailAvailable)
{
await ViewService.DisplayAlert(
String.Empty,
AppResources.ErrorSupportmailMailingFailed,
AppResources.MessageAnswerOk);
return;
}
else
{
try
{
// Send operator related support mail to operator.
await Email.ComposeAsync(new EmailMessage
{
To = new List { MailAddressText },
Cc = APPSUPPORTMAILADDRESS.ToUpper() != MailAddressText.ToUpper() // do not sent copy if same mail address
? new List { APPSUPPORTMAILADDRESS } : new List(),
Subject = string.Format(AppResources.SupportmailSubjectOperatormail, AppFlavorName, SelectedStation?.Id),
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)}"
});
return;
}
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;
}
}
}
/// Request to send a app related mail.
public async Task DoSendMailAppRelated()
{
if (!IsSendMailAvailable)
{
await ViewService.DisplayAlert(
String.Empty,
AppResources.ErrorSupportmailMailingFailed,
AppResources.MessageAnswerOk);
return;
}
else
{
try
{
// Ask for permission to append diagnostics.
await ViewService.DisplayAlert(
AppResources.QuestionSupportmailAttachmentTitle,
AppResources.QuestionSupportmailAttachment,
AppResources.MessageAnswerOk);
var message = new EmailMessage
{
To = new List { APPSUPPORTMAILADDRESS },
Subject = SelectedStation?.Id != null ? string.Format(AppResources.SupportmailSubjectAppmailWithStation, AppFlavorName, SelectedStation?.Id) : string.Format(AppResources.SupportmailSubjectAppmail, AppFlavorName),
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)}"
};
// 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().Error("An error occurred creating attachment for app mail. {@Exception)", exception);
}
if (!string.IsNullOrEmpty(logFileName))
{
message.Attachments.Add(new Xamarin.Essentials.EmailAttachment(logFileName));
}
// Send a shareeBike app related mail
await Email.ComposeAsync(message);
}
catch (Exception exception)
{
Log.ForContext().Error("An unexpected error occurred sending mail. {@Exception}", exception);
await ViewService.DisplayAdvancedAlert(
AppResources.MessageWaring,
AppResources.ErrorSupportmailMailingFailed,
exception.Message,
AppResources.MessageAnswerOk);
return;
}
}
}
/// Command object to bind login button to view model.
public ICommand OnSelectStationRequest
=> new Xamarin.Forms.Command(async () => await OpenSelectStationPageAsync());
/// Opens login page.
public async Task OpenSelectStationPageAsync()
{
try
{
// Switch to map page
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;
}
}
/// Command object to bind phone call button.
public ICommand OnPhoneRequest
=> new Xamarin.Forms.Command(async () => await DoPhoneCall());
/// Request to do a phone call.
public async Task DoPhoneCall()
{
if (!IsDoPhoncallAvailable)
{
await ViewService.DisplayAlert(
String.Empty,
AppResources.ErrorSupportmailPhoningFailed,
AppResources.MessageAnswerOk);
return;
}
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;
}
}
}
/// Text providing the id of the selected station.
public string SelectedStationId
=> SelectedStation?.Id != null ? SelectedStation?.Id : string.Empty;
public string SelectedStationName
=> SelectedStation?.StationName;
/// Text providing the name of the operator of the selected station
public string ProviderNameText
=> SelectedStation?.OperatorData?.Name;
/// Text providing mail address and possible reasons to contact.
public FormattedString MailAddressAndMotivationsText
{
get
{
var hint = new FormattedString();
hint.Spans.Add(new Span { Text = AppResources.MessageContactMail });
return hint;
}
}
/// Invitation to rate app.
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;
}
}
}
}