using System; using System.ComponentModel; using System.Threading.Tasks; using Serilog; using TINK.Model.Bikes; using TINK.Model; using TINK.Model.Device; using TINK.Model.Services.CopriApi; using Xamarin.Essentials; using Xamarin.Forms; using TINK.Model.Connector; namespace TINK.ViewModel.Info { /// Manges the tabbed info page. public class InfoPageViewModel : INotifyPropertyChanged { /// Fired whenever a property changed. public event PropertyChangedEventHandler PropertyChanged; /// Holds the name of the host. private string HostName { get; } /// Holds value wether site caching is on or off. bool IsSiteCachingOn { get; } /// /// Relative path to agb resources of empty if value was not yet querried from backend. /// private string AgbResourcePath { get; set; } /// /// Relative path to privacy resources of empty if value was not yet querried from backend. /// private string PrivacyResourcePath { get; set; } /// /// Relative path to impress resources of empty if value was not yet querried from backend. /// private string ImpressResourcePath { get; set; } private bool _IsIdle = false; /// /// Is true if no action is pending, false otherwise. /// public bool IsIdle { get => _IsIdle; private set { if (_IsIdle == value) { // Nothing to do. return; } _IsIdle = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsIdle))); return; } } /// /// Object to querry resources urls object from backend if required. /// This object is used to update resources path values , and from. /// private Func QueryProvider { get; } /// /// Action to update shared resources urls object exposed by main model. /// private Action UpdateUrlsAction { get; } /// Constructs Info view model /// Name of the host to get html resources from. /// Holds value wether site caching is on or off. /// Agb resouce path received from backend. /// Privacy resouce path received from backend. /// Impress resouce path received from backend. /// Delegate to get an an embedded html ressource. Used as fallback if download from web page does not work and cache is empty. /// Object to querry resources urls object from backend if required. /// Action to update shared resources urls object public InfoPageViewModel( string hostName, string agbResourcePath, string privacyResourcePath, string impressResourcePath, bool isSiteCachingOn, Func resourceProvider, Func queryProvider, Action updateUrlsAction) { HostName = hostName; AgbResourcePath = agbResourcePath; PrivacyResourcePath = privacyResourcePath; ImpressResourcePath = impressResourcePath; IsSiteCachingOn = isSiteCachingOn; QueryProvider = queryProvider; UpdateUrlsAction = updateUrlsAction; InfoAgb = new HtmlWebViewSource { Html = "Loading..." }; ResourceProvider = resourceProvider ?? throw new ArgumentException($"Can not instantiate {typeof(InfoPageViewModel)}-object. No ressource provider availalbe."); } /// Called when page is shown. public async void OnAppearing() { // Get resource urls object from backend. async Task GetUrls() { Result bikes; try { bikes = await QueryProvider().GetBikesAsync(); } catch (Exception ex) { Log.ForContext().Error($"Getting resource urls from COPRI failed. {ex.Message}"); return new ResourceUrls(); } IResourceUrls resourceUrls = bikes?.GeneralData?.ResourceUrls ?? new ResourceUrls(); return resourceUrls; } // Set state to busy. IsIdle = false; // Check if urls to show info from are already known. if (string.IsNullOrEmpty(PrivacyResourcePath)) { var urls = await GetUrls(); PrivacyResourcePath = urls.PrivacyResourcePath; ImpressResourcePath = urls.ImpressResourcePath; AgbResourcePath = urls.AgbResourcePath; UpdateUrlsAction(urls); // Update main model to prevent duplicate queries. } // Gets privacy info from server. async Task GetInfoPrivacy() { string GetUriText() => $"https://{HostName}/{PrivacyResourcePath}"; if (string.IsNullOrEmpty(PrivacyResourcePath)) { // Information to access ressource is missing return new HtmlWebViewSource { Html = await Task.FromResult(ViewModelHelper.FromBody("No privacy resource available. Resource path is null or empty.")) }; } Log.Debug($"Request to open url {GetUriText()} to get privacy info."); return new HtmlWebViewSource { Html = await ViewModelHelper.GetSource( GetUriText(), // "site/privacy.html" IsSiteCachingOn) }; } // Gets impress info from server. async Task GetImpressum() { string GetUriText() => $"https://{HostName}/{ImpressResourcePath}"; if (string.IsNullOrEmpty(ImpressResourcePath)) { // Information to access ressource is missing return new HtmlWebViewSource { Html = await Task.FromResult(ViewModelHelper.FromBody("No impress resource available. Resource path is null or empty.")) }; } Log.Debug($"Request to open url {GetUriText()} to get impress info."); return new HtmlWebViewSource { Html = await ViewModelHelper.GetSource( GetUriText(), // "site/privacy.html" IsSiteCachingOn) }; } InfoAgb = await GetAgb(HostName, AgbResourcePath, IsSiteCachingOn); InfoPrivacy = await GetInfoPrivacy(); InfoImpressum = await GetImpressum(); // Set state to idle. IsIdle = true; } /// Gets the AGBs /// /// AGBs public static async Task GetAgb( string hostName, string agbResourcePath, bool isSiteCachingOn) { string GetUriText() => $"https://{hostName}/{agbResourcePath}"; if (string.IsNullOrEmpty(agbResourcePath)) { return new HtmlWebViewSource { Html = await Task.FromResult(ViewModelHelper.FromBody("No terms resource available. Resource path is null or empty.")) }; } Log.Debug($"Request to open url {GetUriText()} to get AGB infos."); return new HtmlWebViewSource { Html = await ViewModelHelper.GetSource( GetUriText(), // "agb.html" isSiteCachingOn) }; } /// Gets the platfrom specific prefix. private Func ResourceProvider { get; set; } /// Gets the app related information (app version and licenses). public HtmlWebViewSource InfoLicenses => new HtmlWebViewSource { Html = ResourceProvider("HtmlResouces.V02.InfoLicenses.html") .Replace("CURRENT_VERSION_TINKAPP", DependencyService.Get().Version.ToString()) .Replace("ACTIVE_APPNAME", AppInfo.Name) .Replace("APPSUPPORTMAILADDRESS", ContactPageViewModel.APPSUPPORTMAILADDRESS) }; /// Privacy text. private HtmlWebViewSource infoImpress; /// Gets the privacy related information. public HtmlWebViewSource InfoImpressum { get => infoImpress; set { infoImpress = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(InfoImpressum))); } } /// Agb information text. private HtmlWebViewSource infoAgb; /// Privacy text. private HtmlWebViewSource infoPrivacy; /// Agb information text. public HtmlWebViewSource InfoAgb { get => infoAgb; set { infoAgb = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(InfoAgb))); } } /// Agb information text. public HtmlWebViewSource InfoPrivacy { get => infoPrivacy; set { infoPrivacy = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(InfoPrivacy))); } } } }