using System; using System.Collections.Generic; using System.ComponentModel; using System.Threading.Tasks; using System.Windows.Input; using Plugin.Messaging; using Serilog; using TINK.Model.Station; using TINK.MultilingualResources; using TINK.View; using Xamarin.Essentials; using Xamarin.Forms; namespace TINK.ViewModel.Info { /// View model for contact page. public class ContactPageViewModel : INotifyPropertyChanged { /// /// 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 IStation SelectedStation { get; set; } = new NullStation(); /// Holds the name of the app (sharee.bike, Mein konrad, ...) string AppFlavorName { get; } /// Holds a reference to the external trigger service. private Action OpenUrlInExternalBrowser { get; } Func CreateAttachment { get; } /// Notifies view about changes. public event PropertyChangedEventHandler PropertyChanged; /// Constructs a contact page view model. /// Holds the name of the app. /// Action to open an external browser. /// View service to notify user. public ContactPageViewModel( string appFlavorName, Func createAttachment, Action openUrlInExternalBrowser, IViewService viewService) { AppFlavorName = !string.IsNullOrEmpty(appFlavorName) ? appFlavorName : throw new ArgumentException("Can not instantiate contact page view model- object. No app name availalbe."); 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."); } /// 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(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; } /// Command object to bind mail button to view model. public ICommand OnMailToOperatorRequest => new Command( async () => await DoSendMailToOperator(), () => IsSendMailAvailable); /// Command object to bind mail app releated button to model. public ICommand OnMailAppRelatedRequest => new Command( async () => await DoSendMailAppRelated(), () => IsSendMailAvailable); /// 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 avaliable. public bool IsOperatorInfoAvaliable => MailAddressText.Length > 0 || PhoneNumberText.Length > 0; /// Returns true if eithe mail was sent or if no mailer available. public async Task DoSendMailToOperator() { try { if (!IsSendMailAvailable) { // Nothing to do because email can not be sent. return; } // Ask whether mail is operator or app specific. var operatorRelatedMail = await ViewService.DisplayAlert( AppResources.QuestionTitle, string.Format(AppResources.QuestionSupportmailSubject, AppFlavorName), string.Format(AppResources.QuestionSupportmailAnswerOperator, AppFlavorName), string.Format(AppResources.QuestionSupportmailAnswerApp, AppFlavorName)); if (operatorRelatedMail) { // Send operator related support mail to operator. await Email.ComposeAsync(new EmailMessage { To = new List { MailAddressText }, Subject = string.Format(AppResources.SupportmailSubjectOperatormail, AppFlavorName) }); return; } // Send app-related 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 adress && MailAddressText != "konrad@sharee.bike" // do not sent copy if Meinkonrad ? new List { APPSUPPORTMAILADDRESS } : new List(), Subject = string.Format(AppResources.SupportmailSubjectAppmail, AppFlavorName) }); 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() { try { // Ask for permission to append diagnostics. var appendFile = false; appendFile = await ViewService.DisplayAlert( AppResources.QuestionSupportmailTitle, AppResources.QuestionSupportmailAttachment, AppResources.QuestionAnswerYes, AppResources.QuestionSupportmailAnswerNo); var message = new EmailMessage { To = new List { APPSUPPORTMAILADDRESS }, Subject = string.Format(AppResources.SupportmailSubjectAppmail, AppFlavorName) }; if (appendFile == false) { // Send without attachment await Email.ComposeAsync(message); return; } // 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 tink 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 #if USEFLYOUT => new Xamarin.Forms.Command(() => OpenSelectStationPage()); #else => new Xamarin.Forms.Command(async () => await OpenSelectStationPageAsync()); #endif /// Opens login page. #if USEFLYOUT public void OpenSelectStationPage() #else public async Task OpenSelectStationPageAsync() #endif { try { // Switch to map page #if USEFLYOUT ViewService.PushAsync(ViewTypes.SelectStationPage); #else await ViewService.PushAsync(ViewTypes.SelectStationPage); #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; } } /// Command object to bind phone call button. public ICommand OnPhoneRequest => new Command( async () => await DoPhoneCall(), () => IsDoPhoncallAvailable); /// Request to do a phone call. public async Task DoPhoneCall() { try { // Make Phone Call if (IsDoPhoncallAvailable) { 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 mail address and possilbe reasons to contact. public string ProviderNameText => string.Format("Betreiber: {0}", SelectedStation?.OperatorData?.Name) ; /// Text providing mail address and possilbe 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 LikeTinkApp { get { var hint = new FormattedString(); hint.Spans.Add(new Span { Text = string.Format(AppResources.MessageRateMail, AppFlavorName) }); return hint; } } /// User clicks rate button. public ICommand OnRateRequest => new Command(() => RegisterRequest()); /// Opens login page. 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; } } /// 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; } } } }