using MonkeyCache.FileStore; using Serilog; using System; using System.Threading.Tasks; using TINK.Repository.Exception; using TINK.Model.Device; using TINK.Model.State; using TINK.Model.Station; using TINK.Model.User; using Xamarin.Forms; using TINK.Model.Bikes.Bike.BC; using TINK.Repository; using System.Net; using TINK.MultilingualResources; using System.Linq; namespace TINK.ViewModel { public static class ViewModelHelper { /// First part of text making up a station name. private const string USER_FIENDLY_STATIONNUMBER_PREFIX = "Station"; /// Holds the color which marks link to TINK- app pages, web sites, ... public static Color LINK_COLOR = Color.Blue; /// /// Gets station name from station object. /// /// Station to get id from /// public static string GetStationName(this IStation station) { if (station == null) { return string.Empty; } if (!string.IsNullOrEmpty(station.StationName)) { return $"{station.StationName}, Nr. {station.Id}."; } return GetStationName(station.Id); } /// /// Gets station name from station object. /// /// Station to get id from /// public static string GetStationName(string station) { return string.Format("{0} {1}", USER_FIENDLY_STATIONNUMBER_PREFIX, station); } /// /// Gets station id from station name. /// /// Station to get id from /// public static int GetStationId(string stationName) { return int.Parse(stationName.Replace(USER_FIENDLY_STATIONNUMBER_PREFIX, "").Trim()); } /// Get full display name of a bike which includes id. /// /// If name is empty return Id as name. /// /// bike to get name for. /// Display name of bike. public static string GetFullDisplayName(this IBikeInfoMutable bike) => $"{(!string.IsNullOrEmpty(bike.Description) ? $"{bike.Description}, " : string.Empty)}Nr. {bike.Id}"; /// Get the display name of a bike. /// bike to get name for. /// Display name of bike. public static string GetDisplayName(this IBikeInfoMutable bike) => $"{(!string.IsNullOrEmpty(bike.Description) ? $"{bike.Description}" : $"{bike.Id}")}"; /// Get the display id of a bike. /// /// If name is empty id is used as name. For this reason return nothing in this case to avoid duplicate output of id. /// /// bike to get name for. /// Display name of bike. public static string GetDisplayId(this IBikeInfoMutable bike) => $"{(!string.IsNullOrEmpty(bike.Description) ? $"{bike.Id}" : string.Empty)}"; /// /// Maps state to color. /// /// /// public static Color GetColor(this InUseStateEnum state) { switch (state) { case InUseStateEnum.Disposable: return Color.Default; case InUseStateEnum.Reserved: return Color.Orange; case InUseStateEnum.Booked: return Color.Green; default: return Color.Default; } } /// Gets message that logged in user has not booked any bikes. public static string GetShortErrorInfoText(this Exception exception) { if (exception == null) { return string.Empty; } // An error occurred getting bikes information. #if USCSHARP9 switch (exception) { case WebConnectFailureException: return AppResources.ActivityTextErrorWebConnectFailureException; case InvalidResponseException: return AppResources.ActivityTextErrorInvalidResponseException; case WebForbiddenException: return AppResources.ActivityTextErrorWebForbiddenException; case DeserializationException: return AppResources.ActivityTextErrorDeserializationException; case WebException webException: return webException.Status == WebExceptionStatus.ProtocolError && webException.Response is HttpWebResponse webResponse ? string.Format(AppResources.ActivityTextErrorWebExceptionProtocolError, webResponse.StatusDescription) : string.Format(AppResources.ActivityTextErrorWebExceptionGeneralError, webException.Status.ToString()); default: return AppResources.ActivityTextErrorException; } #else if (exception is WebConnectFailureException) return AppResources.ActivityTextErrorWebConnectFailureException; if (exception is InvalidResponseException) return AppResources.ActivityTextErrorInvalidResponseException; if (exception is WebForbiddenException) return AppResources.ActivityTextErrorWebForbiddenException; if (exception is DeserializationException) return AppResources.ActivityTextErrorDeserializationException; if (exception is WebException webException) { return webException.Status == WebExceptionStatus.ProtocolError && webException.Response is HttpWebResponse webResponse ? string.Format(AppResources.ActivityTextErrorWebExceptionProtocolError, webResponse.StatusDescription) : string.Format(AppResources.ActivityTextErrorWebExceptionGeneralError, webException.Status.ToString()); } return AppResources.ActivityTextErrorException; #endif } /// Gets error message and handles aggegate exceptions. public static string GetErrorMessage(this Exception exception) { if (exception == null) return string.Empty; if (!(exception is AggregateException aggregateException)) return exception.Message; if (aggregateException.InnerExceptions.Count == 1) return aggregateException.InnerExceptions[0].Message; return new AggregateException().Message + "\r\n"+ string.Join("\r\n", aggregateException.InnerExceptions.Select(x => x.Message)); } /// Gets message that logged in user has not booked any bikes. public static FormattedString GetErrorInfoText(this Exception exception) { if (exception == null) { return string.Empty; } FormattedString l_oError; // An error occurred getting bikes information. if (exception is WebConnectFailureException) { l_oError = new FormattedString(); l_oError.Spans.Add(new Span { Text = "Information!\r\n", FontAttributes = FontAttributes.Bold }); l_oError.Spans.Add(new Span { Text = $"{exception.Message}\r\n{WebConnectFailureException.GetHintToPossibleExceptionsReasons}" }); return l_oError; } else if (exception is InvalidResponseException) { l_oError = new FormattedString(); l_oError.Spans.Add(new Span { Text = "Fehler, ungültige Serverantwort!\r\n", FontAttributes = FontAttributes.Bold }); l_oError.Spans.Add(new Span { Text = $"{exception.Message}" }); return l_oError; } else if (exception is WebForbiddenException) { l_oError = new FormattedString(); l_oError.Spans.Add(new Span { Text = "Beschäftigt... Einen Moment bitte!" }); return l_oError; } l_oError = new FormattedString(); l_oError.Spans.Add(new Span { Text = "Allgemeiner Fehler!\r\n", FontAttributes = FontAttributes.Bold }); l_oError.Spans.Add(new Span { Text = $"{exception}" }); return l_oError; } /// User tabbed a URI. /// Sender of the event. /// Event arguments public static void OnNavigating(object sender, WebNavigatingEventArgs eventArgs) { if (!eventArgs.Url.ToUpper().StartsWith("HTTP")) { // An internal link was detected. // Stay inside WebView eventArgs.Cancel = false; return; } // Do not navigate outside the document. eventArgs.Cancel = true; DependencyService.Get().OpenUrl(eventArgs.Url); } /// Gets the user group if a user friendly name. /// /// public static string GetUserGroupDisplayName(this User user) { return string.Join(" & ", user.Group); } /// Called when page is shown. /// Url to load data from. /// Holds value wether site caching is on or off. /// Provides resource from embedded ressources. public static async Task GetSource( string resourceUrl, bool isSiteCachingOn, Func resourceProvider = null) { if (!Barrel.Current.IsExpired(resourceUrl) && isSiteCachingOn) { // Cached html is still valid and caching is on. return Barrel.Current.Get(resourceUrl); } string htmlContent = string.Empty; try { // Get info from web server. htmlContent = await CopriCallsHttps.Get(resourceUrl); } catch (Exception l_oException) { // Getting html failed. Log.Error($"Requesting {resourceUrl} failed. {l_oException.Message}"); } switch (string.IsNullOrEmpty(htmlContent)) { case true: // An error occurred getting resource from web htmlContent = Barrel.Current.Exists(resourceUrl) ? Barrel.Current.Get(key: resourceUrl) // Get from MonkeyCache : resourceProvider != null ? resourceProvider() : $"Error loading {resourceUrl}."; // Get build in ressource. break; default: // Add resource to cache. Barrel.Current.Add(key: resourceUrl, data: htmlContent, expireIn: isSiteCachingOn ? TimeSpan.FromDays(1) : TimeSpan.FromMilliseconds(1) ); break; } return htmlContent ?? string.Format("An error occurred loading html- ressource."); } } }