diff --git a/.editorconfig b/.editorconfig index 1d53b6a..d8797f6 100644 --- a/.editorconfig +++ b/.editorconfig @@ -31,7 +31,7 @@ insert_final_newline = false [*.{cs,vb}] # Organize usings -dotnet_separate_import_directive_groups = true +dotnet_separate_import_directive_groups = false:suggestion dotnet_sort_system_directives_first = true file_header_template = unset @@ -84,9 +84,9 @@ dotnet_remove_unnecessary_suppression_exclusions = none [*.cs] # var preferences -csharp_style_var_elsewhere = true:silent -csharp_style_var_for_built_in_types = true:silent -csharp_style_var_when_type_is_apparent = true:silent +csharp_style_var_elsewhere = true:suggestion +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion # Expression-bodied members csharp_style_expression_bodied_accessors = true:silent @@ -128,7 +128,10 @@ csharp_style_unused_value_assignment_preference = discard_variable:suggestion csharp_style_unused_value_expression_statement_preference = discard_variable:silent # 'using' directive preferences -csharp_using_directive_placement = outside_namespace:silent +csharp_using_directive_placement = outside_namespace:suggestion + +# Namespace preferences +csharp_style_namespace_declarations=file_scoped:suggestion #### C# Formatting Rules #### diff --git a/src/CyclingApp/Models/Activity.cs b/src/CyclingApp/Models/Activity.cs index dbd3580..de8b3e9 100644 --- a/src/CyclingApp/Models/Activity.cs +++ b/src/CyclingApp/Models/Activity.cs @@ -1,38 +1,44 @@ -namespace CyclingApp.Models +namespace CyclingApp.Models; + +/// +/// A cycling activity. +/// +public class Activity { - public class Activity - { - public Guid Id { get; } + /// + /// Unique identifier of the activity. + /// + public Guid Id { get; } - /// - /// Creation time of the acitivity. - /// - public DateTime CreationTime { get; } + /// + /// Creation time of the activity. + /// + public DateTime CreationTime { get; } - /// - /// Duration of the acitivity. - /// - public TimeSpan Duration { get; } + /// + /// Duration of the activity. + /// + public TimeSpan Duration { get; } - /// - /// Route of the activity. - /// - public ReadOnlyRoute Route { get; } + /// + /// Route of the activity. + /// + public ReadOnlyRoute Route { get; } - /// - /// Average speed during the activity in kilometers per hour. - /// - public double AvgSpeed { get; } + /// + /// Average speed during the activity in kilometers per hour. + /// + public double AvgSpeed { get; } - /// - /// Initializes a new instance of class. - /// - public Activity(Guid id, DateTime creationTime, TimeSpan duration, ReadOnlyRoute route) - { - Id = id; - CreationTime = creationTime; - Duration = duration; - Route = route; - } + /// + /// Initializes a new instance of class. + /// + public Activity(Guid id, DateTime creationTime, TimeSpan duration, ReadOnlyRoute route) + { + Id = id; + CreationTime = creationTime; + Duration = duration; + Route = route; + AvgSpeed = route.CalculateAvgSpeed(Duration); } } diff --git a/src/CyclingApp/Models/GeoLocation.cs b/src/CyclingApp/Models/GeoLocation.cs index cc9c434..8322d2a 100644 --- a/src/CyclingApp/Models/GeoLocation.cs +++ b/src/CyclingApp/Models/GeoLocation.cs @@ -1,79 +1,78 @@ -namespace CyclingApp.Models +namespace CyclingApp.Models; + +/// +/// Position and altitude on earth. +/// +public class GeoLocation { /// - /// Position and altitude on earth. + /// Longitude in decimal degrees. /// - public class GeoLocation - { - /// - /// Longitude in decimal degrees. - /// - public double Longitude { get; } + public double Longitude { get; } - /// - /// Latitude in decimal degrees. - /// - public double Latitude { get; } + /// + /// Latitude in decimal degrees. + /// + public double Latitude { get; } - /// - /// Accuracy of the and properties, expressed in meters. - /// - public double Accuracy { get; } + /// + /// Accuracy of the and properties, expressed in meters. + /// + public double Accuracy { get; } - /// - /// Altitude in meters, relative to nominal sea level. - /// - /// - /// Can be if the location source cannot provide the data. - /// - public double? Altitude { get; } + /// + /// Altitude in meters, relative to nominal sea level. + /// + /// + /// Can be if the location source cannot provide the data. + /// + public double? Altitude { get; } - /// - /// Accuracy of the altitude expressed in meters. - /// - /// - /// Can be if the original data source cannot provide the data. - /// - public double? AltitudeAccuracy { get; } + /// + /// Accuracy of the altitude expressed in meters. + /// + /// + /// Can be if the original data source cannot provide the data. + /// + public double? AltitudeAccuracy { get; } - /// - /// Direction towards which the device is facing. This value, specified in degrees, indicates how far off from - /// heading true north the device is. 0 degrees represents true north, and the direction is determined clockwise - /// (which means that east is 90 degrees and west is 270 degrees). If speed is 0, is - /// . - /// - /// - /// Can be if the original data source cannot provide the data. - /// - public double? Heading { get; } + /// + /// Direction towards which the device is facing. This value, specified in degrees, indicates how far off from + /// heading true north the device is. 0 degrees represents true north, and the direction is determined clockwise + /// (which means that east is 90 degrees and west is 270 degrees). If speed is 0, is + /// . + /// + /// + /// Can be if the original data source cannot provide the data. + /// + public double? Heading { get; } - /// - /// Velocity of the device in meters per second. - /// - /// - /// Can be if the original data source cannot provide the data. - /// - public double? Speed { get; } + /// + /// Velocity of the device in meters per second. + /// + /// + /// Can be if the original data source cannot provide the data. + /// + public double? Speed { get; } - /// - /// Time at which the location was retrieved. - /// - public DateTime Timestamp { get; } + /// + /// Time at which the location was retrieved. + /// + public DateTime Timestamp { get; } - /// - /// Initializes a new instance of class. - /// - public GeoLocation(DateTime timestamp, double longitude, double latitude, double accuracy, - double? altitude, double? altitudeAccuracy, double? heading, double? speed) - { - Timestamp = timestamp; - Longitude = longitude; - Latitude = latitude; - Accuracy = accuracy; - Altitude = altitude; - AltitudeAccuracy = altitudeAccuracy; - Heading = heading; - Speed = speed; - } + /// + /// Initializes a new instance of class. + /// + public GeoLocation(DateTime timestamp, double longitude, double latitude, double accuracy, + double? altitude, double? altitudeAccuracy, double? heading, double? speed) + { + Timestamp = timestamp; + Longitude = longitude; + Latitude = latitude; + Accuracy = accuracy; + Altitude = altitude; + AltitudeAccuracy = altitudeAccuracy; + Heading = heading; + Speed = speed; } } diff --git a/src/CyclingApp/Models/ReadOnlyRoute.cs b/src/CyclingApp/Models/ReadOnlyRoute.cs index f9936f5..1ae9916 100644 --- a/src/CyclingApp/Models/ReadOnlyRoute.cs +++ b/src/CyclingApp/Models/ReadOnlyRoute.cs @@ -1,9 +1,8 @@ -namespace CyclingApp.Models -{ - /// - /// A read-only route. - /// - /// The waypoints of the route. - public class ReadOnlyRoute(IList waypoints) : RouteBase(waypoints) - { } -} +namespace CyclingApp.Models; + +/// +/// A read-only route. +/// +/// The waypoints of the route. +public class ReadOnlyRoute(IList waypoints) : RouteBase(waypoints) +{ } diff --git a/src/CyclingApp/Models/Route.cs b/src/CyclingApp/Models/Route.cs index e9906cf..a9272bd 100644 --- a/src/CyclingApp/Models/Route.cs +++ b/src/CyclingApp/Models/Route.cs @@ -1,26 +1,25 @@ -namespace CyclingApp.Models +namespace CyclingApp.Models; + +/// +/// An editable route. +/// +public class Route() : RouteBase([]) { /// - /// An editable route. + /// Add a waypoint to the route. /// - public class Route() : RouteBase([]) + /// The waypoint to add. + public new void AddWaypoint(GeoLocation waypoint) { - /// - /// Add a waypoint to the route. - /// - /// The waypoint to add. - public new void AddWaypoint(GeoLocation waypoint) - { - base.AddWaypoint(waypoint); - } + base.AddWaypoint(waypoint); + } - /// - /// Remove waypoint from the route. - /// - /// The waypoint to remove. - public new void RemoveWaypoint(GeoLocation waypoint) - { - base.RemoveWaypoint(waypoint); - } + /// + /// Remove waypoint from the route. + /// + /// The waypoint to remove. + public new void RemoveWaypoint(GeoLocation waypoint) + { + base.RemoveWaypoint(waypoint); } } diff --git a/src/CyclingApp/Models/RouteBase.cs b/src/CyclingApp/Models/RouteBase.cs index 5f3a5d9..742691c 100644 --- a/src/CyclingApp/Models/RouteBase.cs +++ b/src/CyclingApp/Models/RouteBase.cs @@ -1,90 +1,88 @@ using System.Collections.ObjectModel; - using Geolocation; -namespace CyclingApp.Models +namespace CyclingApp.Models; + +/// +/// Base class for a route. +/// +public abstract class RouteBase { + private readonly List _waypoints; + /// - /// Base class for a route. + /// Waypoints of the route. /// - public abstract class RouteBase - { - private readonly List _waypoints; - - /// - /// Waypoints of the route. - /// - public ReadOnlyCollection Waypoints { get; } - - /// - /// Distance of the route in absolute meters. - /// - public double Distance { get; private set; } - - /// - /// Initializes a new instance of class. - /// - protected RouteBase(IList waypoints) - { - _waypoints = new(waypoints); - Waypoints = new(_waypoints); + public ReadOnlyCollection Waypoints { get; } - Distance = CalculateDistance(); - } - - /// - /// Calculate the average speed in kilometers per hour for the route. - /// - /// The duration it took for the route. - /// The average speed in kilometers per hour. - public double CalculateAvgSpeed(TimeSpan duration) - { - return Distance * 1000 / duration.TotalHours; - } + /// + /// Distance of the route in absolute meters. + /// + public double Distance { get; private set; } - /// - /// Add a waypoint to the route. - /// - /// The waypoint to add. - protected void AddWaypoint(GeoLocation waypoint) - { - _waypoints.Add(waypoint); + /// + /// Initializes a new instance of class. + /// + protected RouteBase(IList waypoints) + { + _waypoints = new(waypoints); + Waypoints = new(_waypoints); - Distance = CalculateDistance(); - } + Distance = CalculateDistance(); + } - /// - /// Remove waypoint from the route. - /// - /// The waypoint to remove. - protected void RemoveWaypoint(GeoLocation waypoint) - { - _ = _waypoints.Remove(waypoint); + /// + /// Calculate the average speed in kilometers per hour for the route. + /// + /// The duration it took for the route. + /// The average speed in kilometers per hour. + public double CalculateAvgSpeed(TimeSpan duration) + { + return Distance / 1000 / duration.TotalHours; + } - Distance = CalculateDistance(); - } + /// + /// Add a waypoint to the route. + /// + /// The waypoint to add. + protected void AddWaypoint(GeoLocation waypoint) + { + _waypoints.Add(waypoint); - private double CalculateDistance() - { - var distance = 0d; + Distance = CalculateDistance(); + } - if (_waypoints.Count < 2) - { - return distance; - } + /// + /// Remove waypoint from the route. + /// + /// The waypoint to remove. + protected void RemoveWaypoint(GeoLocation waypoint) + { + _ = _waypoints.Remove(waypoint); - var waypoints = _waypoints.OrderBy(x => x.Timestamp).ToList(); - for (var i = 0; i < waypoints.Count - 1; i++) - { - var origin = waypoints[i]; - var dest = waypoints[i + 1]; + Distance = CalculateDistance(); + } - distance += GeoCalculator.GetDistance(origin.Latitude, origin.Longitude, - dest.Latitude, dest.Longitude, - 0, DistanceUnit.Meters); - } + private double CalculateDistance() + { + var distance = 0d; + if (_waypoints.Count < 2) + { return distance; } + + var waypoints = _waypoints.OrderBy(x => x.Timestamp).ToList(); + for (var i = 0; i < waypoints.Count - 1; i++) + { + var origin = waypoints[i]; + var destination = waypoints[i + 1]; + + distance += GeoCalculator.GetDistance(origin.Latitude, origin.Longitude, + destination.Latitude, destination.Longitude, + 0, DistanceUnit.Meters); + } + + return distance; } } diff --git a/src/CyclingApp/Pages/Activity.razor b/src/CyclingApp/Pages/Activity.razor index a09676a..e025fc3 100644 --- a/src/CyclingApp/Pages/Activity.razor +++ b/src/CyclingApp/Pages/Activity.razor @@ -1,3 +1,36 @@ @page "/activity/{id:guid}" -Activity +Activity @_activity?.CreationTime.ToString("ddd, dd.MM.yy") + +
+
+ + + + @_activity?.CreationTime.ToString("ddd, dd.MM.yy") + + + + + + DISTANCE + @((_activity?.Route.Distance / 1000)?.ToString("N2")) + km + + + + + AVG SPEED + @_activity?.AvgSpeed.ToString("N2") + km/h + + + +
+ + TIME + @_activity?.Duration.ToString("hh\\:mm\\:ss") + + +
+
diff --git a/src/CyclingApp/Pages/Activity.razor.cs b/src/CyclingApp/Pages/Activity.razor.cs index 2f31fea..7d265fd 100644 --- a/src/CyclingApp/Pages/Activity.razor.cs +++ b/src/CyclingApp/Pages/Activity.razor.cs @@ -1,34 +1,49 @@ using CyclingApp.Services; - using Microsoft.AspNetCore.Components; -namespace CyclingApp.Pages +namespace CyclingApp.Pages; + +/// +/// Page that displays an . +/// +public partial class Activity { - public partial class Activity + /// + /// Route of the page. + /// + /// + /// Requires an additional activity ID as parameter. + /// + public const string Route = "/Activity"; + + private Models.Activity? _activity; + + /// + /// ID of the activity to show. + /// + [Parameter] + public Guid Id { get; set; } + + /// + /// Service to get an activity. + /// + [Inject] + protected IActivityService ActivityService { get; set; } = default!; + + /// + /// Manager to navigate the app. + /// + [Inject] + protected NavigationManager NavigationManager { get; set; } = default!; + + /// + protected override void OnParametersSet() + { + _activity = ActivityService.Activities.FirstOrDefault(x => x.Id == Id); + } + + private void NavigateToHome() { - /// - /// Route of the page. - /// - public const string Route = "/Activity"; - - private Models.Activity? _activity; - - /// - /// ID of the activity to show. - /// - [Parameter] - public Guid Id { get; set; } - - /// - /// Service to get an activity. - /// - [Inject] - protected IActivityService ActivityService { get; set; } = default!; - - /// - protected override void OnParametersSet() - { - _activity = ActivityService.Activities.FirstOrDefault(x => x.Id == Id); - } + NavigationManager.NavigateTo(Home.Route); } } diff --git a/src/CyclingApp/Pages/Home.razor b/src/CyclingApp/Pages/Home.razor index ce0a18e..91149fe 100644 --- a/src/CyclingApp/Pages/Home.razor +++ b/src/CyclingApp/Pages/Home.razor @@ -2,14 +2,40 @@ Home - - -
- - - New activity - - - - - \ No newline at end of file +
+
+ +
+ GOOD MORNING + Enjoy Your Biking +
+
+ @if (ActivityService.Activities.Any()) + { + Recent + + @foreach (var activity in ActivityService.Activities) + { +
+ + + @activity.CreationTime.ToString("ddd, dd.MM.yy") + + @($"{(activity.Route.Distance / 1000):N2} km") + @activity.Duration.ToString("hh\\:mm\\:ss") + + + +
+ } +
+ } + + + New activity + + + + +
+
\ No newline at end of file diff --git a/src/CyclingApp/Pages/Home.razor.cs b/src/CyclingApp/Pages/Home.razor.cs index cf4c1e8..9ff1aad 100644 --- a/src/CyclingApp/Pages/Home.razor.cs +++ b/src/CyclingApp/Pages/Home.razor.cs @@ -1,25 +1,37 @@ -using Microsoft.AspNetCore.Components; +using CyclingApp.Services; +using Microsoft.AspNetCore.Components; -namespace CyclingApp.Pages +namespace CyclingApp.Pages; + +/// +/// Home page of the app. +/// +public partial class Home { - public partial class Home - { - /// - /// Route of the page. - /// - public const string Route = "/"; + /// + /// Route of the page. + /// + public const string Route = "/"; + + /// + /// Service to get an activity. + /// + [Inject] + protected IActivityService ActivityService { get; set; } = default!; - [Inject] - protected NavigationManager NavigationManager { get; set; } = default!; + /// + /// Manager to navigate the app. + /// + [Inject] + protected NavigationManager NavigationManager { get; set; } = default!; - private void NavigateToTracker() - { - NavigationManager.NavigateTo(Tracker.Route); - } + private void NavigateToTracker() + { + NavigationManager.NavigateTo(Tracker.Route); + } - private void NavigateToActivity(Guid id) - { - NavigationManager.NavigateTo($"{Activity.Route}/{id}"); - } + private void NavigateToActivity(Guid id) + { + NavigationManager.NavigateTo($"{Activity.Route}/{id}"); } } diff --git a/src/CyclingApp/Pages/Tracker.razor b/src/CyclingApp/Pages/Tracker.razor index 6ea3161..87d55c5 100644 --- a/src/CyclingApp/Pages/Tracker.razor +++ b/src/CyclingApp/Pages/Tracker.razor @@ -1,65 +1,67 @@ @page "/tracker" -Activity +Track new activity - - - - - - DISTANCE - @_route.Distance - km - - - - - AVG SPEED - @_route.CalculateAvgSpeed(_stopWatch.Elapsed) - km/h - - - -
- - - TIME - @_currentTime.ToString("hh\\:mm\\:ss") - - @if (_stopWatch.IsRunning || _isPaused) - { - - @if (_isPaused) - { - - } - else - { - - } - - } - - @if (!_stopWatch.IsRunning) - { +
+
+ + + + + DISTANCE + @((_route.Distance / 1000).ToString("N2")) + km + + + + + AVG SPEED + @_route.CalculateAvgSpeed(_stopWatch.Elapsed).ToString("N2") + km/h + + + +
- @if (!_isPaused) + + TIME + @_currentTime.ToString("hh\\:mm\\:ss") + + @if (_stopWatch.IsRunning || _isPaused) { - - + + @if (_isPaused) + { + + } + else + { + + } } - - @if (_isPaused) - { - - } - else + + @if (!_stopWatch.IsRunning) + { + + @if (!_isPaused) { - + + + } - - - } - - \ No newline at end of file + + @if (_isPaused) + { + + } + else + { + + } + + + } + +
+
\ No newline at end of file diff --git a/src/CyclingApp/Pages/Tracker.razor.cs b/src/CyclingApp/Pages/Tracker.razor.cs index 5085af8..af86444 100644 --- a/src/CyclingApp/Pages/Tracker.razor.cs +++ b/src/CyclingApp/Pages/Tracker.razor.cs @@ -1,143 +1,181 @@ using System.Diagnostics; - using CyclingApp.Models; - +using CyclingApp.Services; using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; +using Thinktecture.Blazor.ScreenWakeLock; + +namespace CyclingApp.Pages; -namespace CyclingApp.Pages +/// +/// Page to track a new activity. +/// +public partial class Tracker : IAsyncDisposable { - public partial class Tracker : IAsyncDisposable + /// + /// Route of the page. + /// + public const string Route = "/tracker"; + + private readonly Stopwatch _stopWatch = new(); + private readonly Timer _stopWatchWatcher; + + private bool _isPaused = false; + private GeolocationPositionError? _positionError; + private TimeSpan _currentTime; + private double _watchId; + private Route _route; + private DateTime _creationTime; + + /// + /// Service to get an activity. + /// + [Inject] + protected IActivityService ActivityService { get; set; } = default!; + + /// + /// Service to interact with the Geolocation Api. + /// + [Inject] + protected IGeolocationService GeolocationService { get; set; } = default!; + + /// + /// Service to interact with the Screen Wake Lock Api. + /// + [Inject] + protected IScreenWakeLockService ScreenWakeLockService { get; set; } = default!; + + /// + /// Manager to navigate the app. + /// + [Inject] + protected NavigationManager NavigationManager { get; set; } = default!; + + /// + /// Initializes a new instance of the class. + /// + public Tracker() { - /// - /// Route of the page. - /// - public const string Route = "/tracker"; - - private readonly Stopwatch _stopWatch = new(); - private readonly Timer _stopWatchWatcher; - - private bool _isPaused = false; - private GeolocationPositionError? _positionError; - private TimeSpan _currentTime; - private double _watchId; - private Route _route; - - [Inject] - protected IGeolocationService GeolocationService { get; set; } = default!; - - //[Inject] - //protected IScreenWakeLockService ScreenWakeLockService { get; set; } = default - - [Inject] - protected NavigationManager NavigationManager { get; set; } = default!; + _stopWatchWatcher = new(WatchStopWatch); + _route = new(); + } - public Tracker() + /// + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender is false) { - _stopWatchWatcher = new(WatchStopWatch); - _route = new(); + return; } - protected override async Task OnAfterRenderAsync(bool firstRender) - { - if (firstRender is false) + _watchId = await GeolocationService.WatchPositionAsync( + this, + nameof(HandlePositionChanged), + nameof(HandlePositionError), + new PositionOptions { - return; - } - - _watchId = await GeolocationService.WatchPositionAsync( - this, - nameof(HandlePositionChanged), - nameof(HandlePositionError), - new PositionOptions - { - EnableHighAccuracy = true, - MaximumAge = 0, - Timeout = 15_000 - }); - } + EnableHighAccuracy = true, + MaximumAge = 0, + Timeout = 15_000 + }); + } - [JSInvokable] - public void HandlePositionChanged(GeolocationPosition pos) + /// + /// Handles a new position received from the Geolocation Api. + /// + // Required to be public in order to be invokable from JavaScript. + [JSInvokable] + public void HandlePositionChanged(GeolocationPosition pos) + { + if (_stopWatch.IsRunning) { - if (_stopWatch.IsRunning) - { - var waypoint = new GeoLocation(pos.TimestampAsUtcDateTime, pos.Coords.Longitude, pos.Coords.Latitude, - pos.Coords.Accuracy, pos.Coords.Altitude, pos.Coords.AltitudeAccuracy, - pos.Coords.Heading, pos.Coords.Speed); - _route.AddWaypoint(waypoint); - } - _positionError = null; - StateHasChanged(); + var waypoint = new GeoLocation(pos.TimestampAsUtcDateTime, pos.Coords.Longitude, pos.Coords.Latitude, + pos.Coords.Accuracy, pos.Coords.Altitude, pos.Coords.AltitudeAccuracy, + pos.Coords.Heading, pos.Coords.Speed); + _route.AddWaypoint(waypoint); } + _positionError = null; + StateHasChanged(); + } - [JSInvokable] - public void HandlePositionError(GeolocationPositionError err) - { - _positionError = err; - StateHasChanged(); - } + /// + /// Handles a position error received from the Geolocation Api. + /// + // Required to be public in order to be invokable from JavaScript. + [JSInvokable] + public void HandlePositionError(GeolocationPositionError err) + { + _positionError = err; + StateHasChanged(); + } - public async ValueTask DisposeAsync() - { - await GeolocationService.ClearWatchAsync(_watchId); - await _stopWatchWatcher.DisposeAsync(); - GC.SuppressFinalize(this); - } + /// + public async ValueTask DisposeAsync() + { + await GeolocationService.ClearWatchAsync(_watchId); + await _stopWatchWatcher.DisposeAsync(); + GC.SuppressFinalize(this); + } - private void NavigateToHome() + private void NavigateToHome() + { + NavigationManager.NavigateTo(Home.Route); + } + + private void Start() + { + if (_isPaused) { - NavigationManager.NavigateTo(Home.Route); + throw new InvalidOperationException("Cannot start while paused"); } - private void Start() - { - if (_isPaused) - { - throw new InvalidOperationException("Cannot start while paused"); - } + //_ = ScreenWakeLockService.RequestWakeLockAsync(); - //_ = ScreenWakeLockService.RequestWakeLockAsync(); + _route = new(); + _creationTime = DateTime.UtcNow; + _stopWatch.Restart(); - _route = new(); - _stopWatch.Restart(); + _ = _stopWatchWatcher.Change(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + } - _ = _stopWatchWatcher.Change(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + private void Resume() + { + if (!_isPaused) + { + throw new InvalidOperationException("Cannot resume when not paused"); } - private void Resume() - { - if (!_isPaused) - { - throw new InvalidOperationException("Cannot resume when not paused"); - } + _stopWatch.Start(); + _isPaused = false; - _stopWatch.Start(); - _isPaused = false; + _ = _stopWatchWatcher.Change(TimeSpan.Zero, TimeSpan.FromSeconds(1)); + } - _ = _stopWatchWatcher.Change(TimeSpan.Zero, TimeSpan.FromSeconds(1)); - } + private void Pause() + { + _stopWatch.Stop(); + _ = _stopWatchWatcher.Change(Timeout.Infinite, Timeout.Infinite); + WatchStopWatch(null); + _isPaused = true; + } - private void Pause() - { - _stopWatch.Stop(); - _ = _stopWatchWatcher.Change(Timeout.Infinite, Timeout.Infinite); - _isPaused = true; - } + private void Stop() + { + _stopWatch.Stop(); + _ = _stopWatchWatcher.Change(Timeout.Infinite, Timeout.Infinite); + WatchStopWatch(null); + _isPaused = false; - private void Stop() - { - _stopWatch.Stop(); - _ = _stopWatchWatcher.Change(Timeout.Infinite, Timeout.Infinite); - _isPaused = false; + //_ = ScreenWakeLockService.RequestWakeLockAsync(); - //_ = ScreenWakeLockService.RequestWakeLockAsync(); - } + var route = new ReadOnlyRoute(_route.Waypoints); + var activity = ActivityService.CreateActivity(_creationTime, _stopWatch.Elapsed, route); + NavigationManager.NavigateTo($"{Activity.Route}/{activity.Id}"); + } - private void WatchStopWatch(object? state) - { - _currentTime = _stopWatch.Elapsed; - StateHasChanged(); - } + private void WatchStopWatch(object? state) + { + _currentTime = _stopWatch.Elapsed; + StateHasChanged(); } } diff --git a/src/CyclingApp/Program.cs b/src/CyclingApp/Program.cs index 111d91b..1515025 100644 --- a/src/CyclingApp/Program.cs +++ b/src/CyclingApp/Program.cs @@ -1,37 +1,33 @@ using CyclingApp.Services; - using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; - using MudBlazor.Services; +namespace CyclingApp; -namespace CyclingApp +public class Program { - public class Program + public static async Task Main(string[] args) { - public static async Task Main(string[] args) - { - var builder = WebAssemblyHostBuilder.CreateDefault(args); - builder.RootComponents.Add("#app"); - builder.RootComponents.Add("head::after"); + var builder = WebAssemblyHostBuilder.CreateDefault(args); + builder.RootComponents.Add("#app"); + builder.RootComponents.Add("head::after"); - // MudBlazor - _ = builder.Services.AddMudServices(); + // MudBlazor + _ = builder.Services.AddMudServices(); - // Blazor.Geolocation - _ = builder.Services.AddGeolocationServices(); + // Blazor.Geolocation + _ = builder.Services.AddGeolocationServices(); - // Blazor.ScreenWakeLock - _ = builder.Services.AddScreenWakeLockServices(); + // Blazor.ScreenWakeLock + _ = builder.Services.AddScreenWakeLockServices(); - // Services - _ = builder.Services.AddSingleton(); + // Services + _ = builder.Services.AddSingleton(); - // HttpClient - _ = builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); + // HttpClient + _ = builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); - await builder.Build().RunAsync(); - } + await builder.Build().RunAsync(); } } diff --git a/src/CyclingApp/Resources/Theme.cs b/src/CyclingApp/Resources/Theme.cs index 0cbbd36..c35a150 100644 --- a/src/CyclingApp/Resources/Theme.cs +++ b/src/CyclingApp/Resources/Theme.cs @@ -1,43 +1,48 @@ using MudBlazor; -namespace CyclingApp.Resources +namespace CyclingApp.Resources; + +/// +/// Contains themes of the app. +/// +public static class Themes { - public static class Themes + /// + /// Default theme. + /// + public static MudTheme Default => new() { - public static MudTheme Default => new() + PaletteDark = new PaletteDark { - PaletteDark = new PaletteDark - { - // Primary - Primary = "#28EB67", - PrimaryContrastText = "#000000", + // Primary + Primary = "#28EB67", + PrimaryContrastText = "#000000", - // Dark - Dark = "#4B4D52", - DarkLighten = "#393A40", - DarkDarken = "#191A1B", - DarkContrastText = "#FFFFFF", + // Dark + Dark = "#4B4D52", + DarkLighten = "#393A40", + DarkDarken = "#191A1B", + DarkContrastText = "#FFFFFF", - // Surface - Surface = "#191A1B", + // Surface + Surface = "#191A1B", - // Background - Background = "#000000", - //BackgroundGrey = "", + // Background + Background = "#000000", + //BackgroundGrey = "", - // Text - TextPrimary = "#FFFFFF", - //TextSecondary = "" - //TextDisabled = "", - }, - Typography = new Typography + // Text + TextPrimary = "#FFFFFF", + //TextSecondary = "" + //TextDisabled = "", + }, + Typography = new Typography + { + Default = new() { - Default = new() - { - FontFamily = ["sui-generis", "sans-serif"], - FontWeight = 500 - } + FontFamily = ["sui-generis", "sans-serif"], + FontWeight = 500 } - }; - } + } + }; } diff --git a/src/CyclingApp/Services/ActivityService.cs b/src/CyclingApp/Services/ActivityService.cs index 76d9bd5..cf437f7 100644 --- a/src/CyclingApp/Services/ActivityService.cs +++ b/src/CyclingApp/Services/ActivityService.cs @@ -1,33 +1,68 @@ using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; - using CyclingApp.Models; -namespace CyclingApp.Services +namespace CyclingApp.Services; + +internal class ActivityService : IActivityService { - internal class ActivityService : IActivityService - { - public ReadOnlyObservableCollection Activities { get; } - private readonly ObservableCollection _activities; + public ReadOnlyObservableCollection Activities { get; } + private readonly ObservableCollection _activities; - public ActivityService() - { - _activities = []; - Activities = new(_activities); - } + public ActivityService() + { + _activities = [ + new Activity(Guid.NewGuid(), DateTime.UtcNow.AddDays(-7), TimeSpan.FromMinutes(4), new ReadOnlyRoute(new List + { + new(DateTime.UtcNow, 13.376842926876462, 52.518598063383926, 1, 100, 1, 45, 12), + new(DateTime.UtcNow.AddMinutes(1), 13.37771344544642,52.51628190312695 , 1, 100, 1, 45, 12), + new(DateTime.UtcNow.AddMinutes(2), 13.400707818550298, 52.51815134482483, 1, 100, 1, 45, 12), + })), + new Activity(Guid.NewGuid(), DateTime.UtcNow.AddDays(-6), TimeSpan.FromMinutes(4), new ReadOnlyRoute(new List + { + new(DateTime.UtcNow, 13.376842926876462, 52.518598063383926, 1, 100, 1, 45, 12), + new(DateTime.UtcNow.AddMinutes(1), 13.37771344544642,52.51628190312695 , 1, 100, 1, 45, 12), + new(DateTime.UtcNow.AddMinutes(2), 13.400707818550298, 52.51815134482483, 1, 100, 1, 45, 12), + })), + new Activity(Guid.NewGuid(), DateTime.UtcNow.AddDays(-5), TimeSpan.FromMinutes(4), new ReadOnlyRoute(new List + { + new(DateTime.UtcNow, 13.376842926876462, 52.518598063383926, 1, 100, 1, 45, 12), + new(DateTime.UtcNow.AddMinutes(1), 13.37771344544642,52.51628190312695 , 1, 100, 1, 45, 12), + new(DateTime.UtcNow.AddMinutes(2), 13.400707818550298, 52.51815134482483, 1, 100, 1, 45, 12), + })), + new Activity(Guid.NewGuid(), DateTime.UtcNow.AddDays(-4), TimeSpan.FromMinutes(4), new ReadOnlyRoute(new List + { + new(DateTime.UtcNow, 13.376842926876462, 52.518598063383926, 1, 100, 1, 45, 12), + new(DateTime.UtcNow.AddMinutes(1), 13.37771344544642,52.51628190312695 , 1, 100, 1, 45, 12), + new(DateTime.UtcNow.AddMinutes(2), 13.400707818550298, 52.51815134482483, 1, 100, 1, 45, 12), + })), + new Activity(Guid.NewGuid(), DateTime.UtcNow.AddDays(-3), TimeSpan.FromMinutes(4), new ReadOnlyRoute(new List + { + new(DateTime.UtcNow, 13.376842926876462, 52.518598063383926, 1, 100, 1, 45, 12), + new(DateTime.UtcNow.AddMinutes(1), 13.37771344544642,52.51628190312695 , 1, 100, 1, 45, 12), + new(DateTime.UtcNow.AddMinutes(2), 13.400707818550298, 52.51815134482483, 1, 100, 1, 45, 12), + })), + new Activity(Guid.NewGuid(), DateTime.UtcNow, TimeSpan.FromMinutes(4), new ReadOnlyRoute(new List + { + new(DateTime.UtcNow, 13.376842926876462, 52.518598063383926, 1, 100, 1, 45, 12), + new(DateTime.UtcNow.AddMinutes(1), 13.37771344544642,52.51628190312695 , 1, 100, 1, 45, 12), + new(DateTime.UtcNow.AddMinutes(2), 13.400707818550298, 52.51815134482483, 1, 100, 1, 45, 12), + })) + ]; + Activities = new(_activities); + } - public Activity CreateActivity(DateTime timestamp, TimeSpan duration, ReadOnlyRoute route) - { - var activity = new Activity(Guid.NewGuid(), timestamp, duration, route); - _activities.Add(activity); - return activity; - } + public Activity CreateActivity(DateTime creationTIme, TimeSpan duration, ReadOnlyRoute route) + { + var activity = new Activity(Guid.NewGuid(), creationTIme, duration, route); + _activities.Add(activity); + return activity; + } - public bool TryDeleteActivity(Guid id, [NotNullWhen(true)] out Activity? activity) - { - activity = Activities.FirstOrDefault(x => x.Id == id); - return activity is not null - && _activities.Remove(activity); - } + public bool TryDeleteActivity(Guid id, [NotNullWhen(true)] out Activity? activity) + { + activity = Activities.FirstOrDefault(x => x.Id == id); + return activity is not null + && _activities.Remove(activity); } } diff --git a/src/CyclingApp/Services/IActivityService.cs b/src/CyclingApp/Services/IActivityService.cs index 3991296..62040d9 100644 --- a/src/CyclingApp/Services/IActivityService.cs +++ b/src/CyclingApp/Services/IActivityService.cs @@ -1,16 +1,27 @@ using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; - using CyclingApp.Models; -namespace CyclingApp.Services +namespace CyclingApp.Services; + +/// +/// Service to manage activities. +/// +public interface IActivityService { - public interface IActivityService - { - ReadOnlyObservableCollection Activities { get; } + /// + /// All activities available. + /// + ReadOnlyObservableCollection Activities { get; } - Activity CreateActivity(DateTime timestamp, TimeSpan duration, ReadOnlyRoute route); + /// + /// Create a new activity. + /// + /// The creation time of the activity. + /// The duration of the activity. + /// The route of the activity. + /// + Activity CreateActivity(DateTime creationTime, TimeSpan duration, ReadOnlyRoute route); - bool TryDeleteActivity(Guid id, [NotNullWhen(true)] out Activity? activity); - } + bool TryDeleteActivity(Guid id, [NotNullWhen(true)] out Activity? activity); } \ No newline at end of file