diff --git a/PleaseReadMyAnswer.md b/PleaseReadMyAnswer.md new file mode 100644 index 0000000..487a491 --- /dev/null +++ b/PleaseReadMyAnswer.md @@ -0,0 +1,46 @@ +# Assessment Finished By Manric Vilegas + +## Task 1 [4 hours] + +Dear Cate + +I have finished second update about this project. +Please check it. + +## Task 2 [2 hours] + +- Design the User Interface (UI / UX): + +Decide on the layout and appearance of the news section within the home tab. +Consider using a ListView or RecyclerView to display the news articles in a scrollable list. + +- Fetching News Data: + +Determine the source(s) from where I will retrieve the news data. This could be an API, RSS feed, or a content management system (CMS) if the client has one. +Implement the necessary logic to fetch news data from the chosen source(s). +Use libraries like Newtonsoft.Json or System.Net.Http for making HTTP requests and handling JSON data. +- Parse and Store News Data: + +Receive the news data, parse it according to the provided format (JSON, XML, etc.). +Extract relevant information such as article title, description, publication date, and image URL. +Create a model class to represent the news articles and store this data in an appropriate data structure, such as a List. +- Displaying News Articles: + +Bind the list of news articles to the UI component (ListView/RecyclerView) to populate the view with the retrieved data. +Customize the item layout to display the relevant information, including the article title, description, and thumbnail image. +Handle user interaction events, such as tapping on a news article to open the full article or view more details. +- Periodic Content Updates: + +To provide weekly or fortnightly updates, And It is needed to trigger the news data fetching periodically. +Schedule background tasks or use services like Firebase Cloud Messaging (FCM) to notify the app about new content availability. +Update the news data in the app's storage and refresh the UI accordingly. +- Caching and Offline Support: + +Implement a caching mechanism to store previously fetched news articles on the device. +Consider using technologies like SQLite, Realm, or Xamarin.Essentials Preferences for local storage. +Enable offline support by allowing users to access previously loaded news articles when there is no internet connection. +- Error Handling and Exceptional Cases: + +Implement appropriate error handling mechanisms to handle cases such as network failures, server errors, or parsing issues. +Show relevant error messages or fallback content when news data cannot be retrieved. +Provide an option for users to refresh the news section manually if necessary. \ No newline at end of file diff --git a/UndoAssessment/UndoAssessment.Android/Resources/Resource.designer.cs b/UndoAssessment/UndoAssessment.Android/Resources/Resource.designer.cs index ca95bb7..80a8fd9 100644 --- a/UndoAssessment/UndoAssessment.Android/Resources/Resource.designer.cs +++ b/UndoAssessment/UndoAssessment.Android/Resources/Resource.designer.cs @@ -2,7 +2,6 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -15,7 +14,7 @@ namespace UndoAssessment.Droid { - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "13.2.0.93")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "12.2.4.160")] public partial class Resource { diff --git a/UndoAssessment/UndoAssessment.Android/UndoAssessment.Android.csproj b/UndoAssessment/UndoAssessment.Android/UndoAssessment.Android.csproj index 5c6316d..d1eff24 100644 --- a/UndoAssessment/UndoAssessment.Android/UndoAssessment.Android.csproj +++ b/UndoAssessment/UndoAssessment.Android/UndoAssessment.Android.csproj @@ -1,4 +1,4 @@ - + Debug @@ -16,7 +16,7 @@ Properties\AndroidManifest.xml Resources Assets - v13.0 + v12.0 true true Xamarin.Android.Net.AndroidClientHandler @@ -92,5 +92,5 @@ UndoAssessment - - + + \ No newline at end of file diff --git a/UndoAssessment/UndoAssessment/AppShell.xaml b/UndoAssessment/UndoAssessment/AppShell.xaml index 9b3603b..9a5998d 100644 --- a/UndoAssessment/UndoAssessment/AppShell.xaml +++ b/UndoAssessment/UndoAssessment/AppShell.xaml @@ -1,4 +1,4 @@ - + + diff --git a/UndoAssessment/UndoAssessment/AppShell.xaml.cs b/UndoAssessment/UndoAssessment/AppShell.xaml.cs index abbe3ed..0d98c59 100644 --- a/UndoAssessment/UndoAssessment/AppShell.xaml.cs +++ b/UndoAssessment/UndoAssessment/AppShell.xaml.cs @@ -11,6 +11,8 @@ public partial class AppShell : Xamarin.Forms.Shell public AppShell() { InitializeComponent(); + Routing.RegisterRoute(nameof(CatePage), typeof(CatePage)); + Routing.RegisterRoute(nameof(UserFormPage), typeof(UserFormPage)); Routing.RegisterRoute(nameof(ItemDetailPage), typeof(ItemDetailPage)); Routing.RegisterRoute(nameof(NewItemPage), typeof(NewItemPage)); } diff --git a/UndoAssessment/UndoAssessment/Models/ApiResponseModel.cs b/UndoAssessment/UndoAssessment/Models/ApiResponseModel.cs new file mode 100644 index 0000000..084d169 --- /dev/null +++ b/UndoAssessment/UndoAssessment/Models/ApiResponseModel.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace UndoAssessment.Models +{ + public class ApiResponseModel + { + public string message { get; set; } + public string date { get; set; } + public int errorCode { get; set; } + + public bool IsSuccess + { + get + { + return errorCode == 0; + } + } + + } +} diff --git a/UndoAssessment/UndoAssessment/Models/UserModel.cs b/UndoAssessment/UndoAssessment/Models/UserModel.cs new file mode 100644 index 0000000..6691156 --- /dev/null +++ b/UndoAssessment/UndoAssessment/Models/UserModel.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace UndoAssessment.Models +{ + public class UserModel + { + public String Name { get; set; } + public String Age { get; set; } + } +} diff --git a/UndoAssessment/UndoAssessment/Services/ApiService.cs b/UndoAssessment/UndoAssessment/Services/ApiService.cs new file mode 100644 index 0000000..f030561 --- /dev/null +++ b/UndoAssessment/UndoAssessment/Services/ApiService.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; +using UndoAssessment.Models; + +namespace UndoAssessment.Services +{ + public class ApiService + { + private static readonly String baseUrl = "https://malkarakundostagingpublicapi.azurewebsites.net/"; + private static readonly String success = baseUrl + "success"; + private static readonly String fail = baseUrl + "fail"; + + private static readonly HttpClient httpClient = new HttpClient(); + + public static async Task GetSuccess() + { + return await httpRequest(success); + } + + public static async Task GetFail() + { + return await httpRequest(fail); + } + + private static async Task httpRequest(string endpoint) + { + HttpResponseMessage response = await httpClient.GetAsync(endpoint); + String content = await response.Content.ReadAsStringAsync(); + + return JsonConvert.DeserializeObject(content); + } + } +} \ No newline at end of file diff --git a/UndoAssessment/UndoAssessment/Services/IDataStore.cs b/UndoAssessment/UndoAssessment/Services/IDataStore.cs index ca75e0e..d76717f 100644 --- a/UndoAssessment/UndoAssessment/Services/IDataStore.cs +++ b/UndoAssessment/UndoAssessment/Services/IDataStore.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using UndoAssessment.Models; namespace UndoAssessment.Services { @@ -11,6 +12,7 @@ public interface IDataStore Task DeleteItemAsync(string id); Task GetItemAsync(string id); Task> GetItemsAsync(bool forceRefresh = false); + UserModel User { get; set; } } } diff --git a/UndoAssessment/UndoAssessment/Services/MockDataStore.cs b/UndoAssessment/UndoAssessment/Services/MockDataStore.cs index f4a318a..ad299d9 100644 --- a/UndoAssessment/UndoAssessment/Services/MockDataStore.cs +++ b/UndoAssessment/UndoAssessment/Services/MockDataStore.cs @@ -56,5 +56,14 @@ public async Task> GetItemsAsync(bool forceRefresh = false) { return await Task.FromResult(items); } + + private UserModel user; + + public UserModel User + { + get { return user; } + set { user = value; } + } + } } diff --git a/UndoAssessment/UndoAssessment/UndoAssessment.csproj b/UndoAssessment/UndoAssessment/UndoAssessment.csproj index 95a3587..2d78cd0 100644 --- a/UndoAssessment/UndoAssessment/UndoAssessment.csproj +++ b/UndoAssessment/UndoAssessment/UndoAssessment.csproj @@ -1,12 +1,15 @@ - + netstandard2.0 true + true + True + \ No newline at end of file diff --git a/UndoAssessment/UndoAssessment/ViewModels/CateViewModel.cs b/UndoAssessment/UndoAssessment/ViewModels/CateViewModel.cs new file mode 100644 index 0000000..3e749fe --- /dev/null +++ b/UndoAssessment/UndoAssessment/ViewModels/CateViewModel.cs @@ -0,0 +1,113 @@ +using System; +using System.Windows.Input; +using Xamarin.Essentials; +using Xamarin.Forms; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Net; +using System.Net.Http; +using System.Runtime.Serialization; +using System.Threading.Tasks; + +using UndoAssessment.Models; +using UndoAssessment.Services; +using UndoAssessment.Views; + +namespace UndoAssessment.ViewModels +{ + public class CateViewModel : BaseViewModel + { + + #region Contructor + + public CateViewModel() + { + Title = "Assessment for Cate"; + + PopupUserInfoCommand = new Command(OnPopupUserInfoClicked); + ApiSuccessCommand = new Command(OnApiSuccessClicked); + ApiFailCommand = new Command(OnApiFailClicked); + } + + #endregion + + #region Functions + + public void OnAppearing() + { + OnPropertyChanged(nameof(User)); + OnPropertyChanged(nameof(UserVisible)); + } + + public bool UserVisible + { + get + { + return user != null; + } + } + + #endregion + + #region Variables + + private UserModel user => DataStore.User; + + public String User + { + get + { + return user == null + ? "" + : $"Name: {user.Name}\n" + $"Age: {user.Age}"; + } + } + + + #endregion + + #region Commands + + public ICommand OpenWebCommand { get; } + + public Command PopupUserInfoCommand { get; } + public Command ApiSuccessCommand { get; } + public Command ApiFailCommand { get; } + + private readonly String successText = "Success API"; + private readonly String failureText = "Failure API"; + private readonly String cancelText = "Cancel"; + + private void OnPopupUserInfoClicked() + { + Shell.Current.GoToAsync(nameof(UserFormPage)); + } + + private async void AlertResult(ApiResponseModel resp) + { + if (resp.IsSuccess) + { + await Application.Current.MainPage.DisplayAlert(successText, resp.message, cancelText); + } + else + { + await Application.Current.MainPage.DisplayAlert(failureText, resp.message, cancelText); + } + } + + private async void OnApiSuccessClicked() + { + ApiResponseModel resp = await ApiService.GetSuccess(); + AlertResult(resp); + } + + private async void OnApiFailClicked() + { + ApiResponseModel resp = await ApiService.GetFail(); + AlertResult(resp); + } + + #endregion + + } +} diff --git a/UndoAssessment/UndoAssessment/ViewModels/UserFormViewModel.cs b/UndoAssessment/UndoAssessment/ViewModels/UserFormViewModel.cs new file mode 100644 index 0000000..5230f48 --- /dev/null +++ b/UndoAssessment/UndoAssessment/ViewModels/UserFormViewModel.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows.Input; +using UndoAssessment.Models; +using Xamarin.Forms; + +namespace UndoAssessment.ViewModels +{ + public class UserFormViewModel : BaseViewModel + { + private String name; + private String age; + + public UserFormViewModel() + { + SaveCommand = new Command(OnSaveClicked, ValidateSave); + CancelCommand = new Command(OnCancelClicked); + PropertyChanged += (_a, _b) => SaveCommand.ChangeCanExecute(); + } + + private bool ValidateSave() + { + return !String.IsNullOrWhiteSpace(name) + && !String.IsNullOrWhiteSpace(age); + } + + public String Name + { + get => name; + set => SetProperty(ref name, value); + } + + public String Age + { + get => age; + set => SetProperty(ref age, value); + } + + public Command SaveCommand { get; } + public Command CancelCommand { get; } + + private async void OnSaveClicked() + { + DataStore.User = new UserModel { + Name = Name, + Age = Age + }; + + await Shell.Current.GoToAsync(".."); + } + + private async void OnCancelClicked() + { + await Shell.Current.GoToAsync(".."); + } + + } +} + diff --git a/UndoAssessment/UndoAssessment/Views/CatePage.xaml b/UndoAssessment/UndoAssessment/Views/CatePage.xaml new file mode 100644 index 0000000..74e1691 --- /dev/null +++ b/UndoAssessment/UndoAssessment/Views/CatePage.xaml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + #96d1ff + + + + + + + + + + + + + + + + diff --git a/UndoAssessment/UndoAssessment/Views/UserFormPage.xaml.cs b/UndoAssessment/UndoAssessment/Views/UserFormPage.xaml.cs new file mode 100644 index 0000000..06e54d4 --- /dev/null +++ b/UndoAssessment/UndoAssessment/Views/UserFormPage.xaml.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using Xamarin.Forms; +using Xamarin.Forms.Xaml; + +using UndoAssessment.Models; +using UndoAssessment.ViewModels; + +namespace UndoAssessment.Views +{ + public partial class UserFormPage : ContentPage + { + public Item Item { get; set; } + + public UserFormPage() + { + InitializeComponent(); + BindingContext = new UserFormViewModel(); + } + } +}