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; using TINK.Model.Stations.StationNS; 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; } /// Reference on the tink app instance. private ITinkApp TinkApp { 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, ITinkApp tinkApp, 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 centered."); TinkApp = tinkApp ?? throw new ArgumentException("Can not instantiate settings page view model- object. No tink 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."); } /// 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 Command(async () => await DoSendMailToOperator()); /// Command object to bind mail app related button to model. public ICommand OnMailAppRelatedRequest => new 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 && MailAddressText != "konrad@sharee.bike" // do not sent copy if Mein konrad ? new List { APPSUPPORTMAILADDRESS } : new List(), Subject = string.Format(AppResources.SupportmailSubjectOperatormail, AppFlavorName, SelectedStation?.Id), Body = TinkApp.ActiveUser.Mail != null ? $"{AppResources.SupportmailBodyText}\r\n\r\n\r\n\r\n{string.Format(AppResources.MarkingLoggedInStateInfoLoggedIn, TinkApp.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 = TinkApp.ActiveUser.Mail != null ? $"{AppResources.SupportmailBodyText}\r\n\r\n\r\n\r\n{string.Format(AppResources.MarkingLoggedInStateInfoLoggedIn, TinkApp.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 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()); /// 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; } } } }