using System; using System.Linq; using System.Net; using System.Threading.Tasks; using MonkeyCache.FileStore; using Serilog; using ShareeBike.Model.Bikes.BikeInfoNS.BC; using ShareeBike.Model.Device; using ShareeBike.Model.State; using ShareeBike.Model.Stations.StationNS; using ShareeBike.Model.User; using ShareeBike.MultilingualResources; using ShareeBike.Repository; using ShareeBike.Repository.Exception; using Xamarin.Forms; namespace ShareeBike.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 ShareeBike- 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}")}"; public static string GetDisplayTypeOfBike(this IBikeInfoMutable bike) => $"{(!string.IsNullOrEmpty(bike.Description) ? $"{bike.TypeOfBike}" : $"{bike.Id}")}"; public static string GetDisplayWheelType(this IBikeInfoMutable bike) => $"{(!string.IsNullOrEmpty(bike.Description) ? $"{bike.WheelType}" : $"{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.FeedbackPending: 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. /// Holds whether report level is verbose or not. public static string GetShortErrorInfoText(this Exception exception, bool isReportLevelVerbose) { if (exception == null) { return string.Empty; } if (exception is AuthcookieNotDefinedException) return AppResources.ActivityTextAuthcookieNotDefinedException; if (!isReportLevelVerbose) { return AppResources.ActivityTextException; } // 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 aggregate 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{AppResources.ErrorNoWeb}" }); 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; } /// 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 whether site caching is on or off. /// Provides resource from embedded resources. 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 resource. break; default: // Add resource to cache. Barrel.Current.Add(key: resourceUrl, data: htmlContent, expireIn: isSiteCachingOn ? TimeSpan.FromDays(1) : TimeSpan.FromMilliseconds(1)); break; } return htmlContent ?? FromBody("An error occurred loading html- resource."); } public static string FromBody(string message) => $"Error Information{message}"; } }