Initial version.

This commit is contained in:
Oliver Hauff 2021-05-13 20:16:41 +02:00
parent e4fb48f6ab
commit 10dbeb5a90
737 changed files with 61885 additions and 0 deletions

View file

@ -0,0 +1,69 @@
using System.ComponentModel;
using Xamarin.Forms;
using TINK.Services.CopriApi.ServerUris;
using TINK.Model.User.Account;
namespace TINK.ViewModel.Contact
{
public class HelpContactViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
/// <summary> Holds value wether site caching is on or off.</summary>
bool IsSiteCachingOn { get; }
/// <summary> Constructs view model.</summary>
/// <param name="isSiteCachingOn">Set of user permissions</param>
public HelpContactViewModel(
string hostName,
bool isSiteCachingOn)
{
HostName = hostName;
IsSiteCachingOn = isSiteCachingOn;
}
/// <summary> Holds the name of the host.</summary>
private string HostName { get; }
/// <summary> Called when page is shown. </summary>
public async void OnAppearing()
{
RentBikeText = new HtmlWebViewSource
{
Html = HostName.GetIsCopri()
? ViewModelResourceHelper.GetSource("HtmlResouces.V02.InfoRentBike.html")
: await ViewModelHelper.GetSource($"https://{HostName}/{CopriHelper.SHAREE_SILTEFOLDERNAME}/tariff_info.html", IsSiteCachingOn)
};
TypesOfBikesText = new HtmlWebViewSource
{
Html = HostName.GetIsCopri()
? ViewModelResourceHelper.GetSource("HtmlResouces.V02.InfoTypesOfBikes.html")
: await ViewModelHelper.GetSource($"https://{HostName}/{CopriHelper.SHAREE_SILTEFOLDERNAME}/bike_info.html", IsSiteCachingOn)
};
}
private HtmlWebViewSource rentBikeText;
private HtmlWebViewSource typesOfBikesText;
public HtmlWebViewSource RentBikeText
{
get => rentBikeText;
set
{
rentBikeText = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(RentBikeText)));
}
}
public HtmlWebViewSource TypesOfBikesText
{
get => typesOfBikesText;
set
{
typesOfBikesText = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(TypesOfBikesText)));
}
}
}
}

View file

@ -0,0 +1,192 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using TINK.Services.CopriApi.ServerUris;
using TINK.View;
using TINK.View.Account;
using TINK.View.Contact;
using TINK.View.Info;
using TINK.View.Login;
using TINK.View.Map;
using TINK.View.MyBikes;
using TINK.View.Root;
using TINK.View.Settings;
using Xamarin.Forms;
using TINK.ViewModel.MasterDetail;
using TINK.View.MasterDetail;
using TINK.Services;
using TINK.View.Themes;
namespace TINK.ViewModel.Root
{
/// <summary>
/// View model for flyout menu entries. Respositility
/// - populates entries depending on app state on showing menu
/// - updates menu denpending on app state change, i.e. user logs in/ out.
/// </summary>
public class RootPageFlyoutViewModel : INotifyPropertyChanged
{
/// <summary>
/// Dictionary to manage collection of menu items.
/// </summary>
private Dictionary<Type, int> m_oEntryDictionary;
public event PropertyChangedEventHandler PropertyChanged;
/// <summary> Updates menu entries depending on state. </summary>
private void UpdateMenuEntries()
{
for (int l_iIndex = MenuItems.Count - 1; l_iIndex >= 0; l_iIndex--)
{
MenuItems.Clear();
m_oEntryDictionary.Clear();
}
CheckAddItem(typeof(MapPage));
if (App.ModelRoot.ActiveUser.IsLoggedIn)
{
CheckAddItem(typeof(MyBikesPage));
CheckAddItem(typeof(AccountPage));
}
else
{
CheckAddItem(typeof(LoginPage));
}
if (App.ModelRoot.Uris.ActiveUri.Host.GetIsCopri()
|| App.ModelRoot.ActiveUser.IsLoggedIn)
{
CheckAddItem(typeof(SettingsPage));
}
CheckAddItem(typeof(FeesAndBikesPage)); // Fees and bikes
CheckAddItem(typeof(ContactPage)); // Feedback and contact
CheckAddItem(typeof(TabbedPageInfo)); // About sharee.bike
}
/// <summary>Adds a menu item to master detail menu.</summary>
/// <param name="p_oType">Type decribing entry to be added.</param>
private void CheckAddItem(Type p_oType)
{
if (m_oEntryDictionary.ContainsKey(p_oType))
{
// Nothing to do because has already been added.
return;
}
m_oEntryDictionary.Add(
p_oType,
m_oEntryDictionary.Count);
MenuItems.Add(new RootPageFlyoutMenuItem {
TargetType = p_oType,
GlyphCode = Helper.GetGlyphCode(p_oType),
Title = Helper.GetCaption(p_oType),
Id = m_oEntryDictionary[p_oType] }) ;
}
/// <summary> Removes a page from menu.</summary>
/// <param name="p_oType"></param>
private void RemoveItem(Type p_oType)
{
if (!m_oEntryDictionary.ContainsKey(p_oType))
{
// Nothing to do because item was never added/ already removed.
return;
}
MenuItems.Remove(MenuItems[m_oEntryDictionary[p_oType]]);
m_oEntryDictionary.Remove(p_oType);
}
public RootPageFlyoutViewModel()
{
MenuItems = new ObservableCollection<RootPageFlyoutMenuItem>();
m_oEntryDictionary = new Dictionary<Type, int>();
// Update menu entries on login/ logout actions.
App.ModelRoot.ActiveUser.StateChanged += (sender, eventargs) => OnUpdateRequired();
// Update flyout view model whenever theme is switched.
App.ModelRoot.Themes.PropertyChanged += (sender, eventargs) =>
{
if (!(sender is ServicesContainerMutable<object> themes))
return;
MasterDetailMenuTitlte = GetMasterDetailMenuTitle(themes.Active);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MasterDetailMenuTitlte)));
};
MasterDetailMenuTitlte = GetMasterDetailMenuTitle(App.ModelRoot.Themes.Active);
UpdateMenuEntries();
}
/// <summary>
/// Gets the flyout title from theme name
/// </summary>
/// <param name="theme">Name of theme.</param>
/// <returns>Flyout title.</returns>
private string GetMasterDetailMenuTitle(object theme)
{
if (!(theme is ITheme active))
return "sharee.bike";
return $"sharee.bike{(!string.IsNullOrEmpty(active.OperatorInfo) ? ($"\r\n{active.OperatorInfo}") : string.Empty)}";
}
/// <summary>
/// Holds the title of the fylout page.
/// </summary>
public string MasterDetailMenuTitlte { get; private set; }
/// <summary>
/// Provides collection of menu items for master page.
/// </summary>
public ObservableCollection<RootPageFlyoutMenuItem> MenuItems
{
get; private set;
}
/// <summary>
/// Gets or sets the selected item.
/// </summary>
public MainPageMenuItem SelectedMenuItem
{
get;
set;
}
/// <summary>
/// Command object which is invoked from view when an item is selected.
/// </summary>
public Command MenuItemSelected
{
get
{
return new Command(OnUpdateRequired);
}
}
/// <summary> Manges updates required due to selection of a menu or login events.</summary>
public void OnUpdateRequired()
{
UpdateMenuEntries();
if (SelectedMenuItem == null)
{
// Nothing to do if no menu entry is selected.
return;
}
}
}
}

View file

@ -0,0 +1,106 @@
using System;
using TINK.MultilingualResources;
using TINK.View.Account;
using TINK.View.Contact;
using TINK.View.Info;
using TINK.View.Login;
using TINK.View.Map;
using TINK.View.MyBikes;
using TINK.View.Settings;
using TINK.ViewModel.Info;
namespace TINK.ViewModel.MasterDetail
{
public static class Helper
{
/// <summary>
/// Gets a description for a map page used as menu item entry and caption.
/// </summary>
/// <param name="type">Type of page to get caption for.</param>
/// <returns></returns>
public static string GetCaption(Type type)
{
if (type == typeof(MapPage)) // Bikes sites
{
return AppResources.MarkingMapPage;
}
else if (type == typeof(MyBikesPage)) // My Bikes
{
return AppResources.MarkingMyBikes;
}
else if (type == typeof(AccountPage)) // Account
{
return AppResources.MarkingAccount;
}
else if (type == typeof(LoginPage)) // Login
{
return AppResources.MarkingLogin;
}
else if (type == typeof(SettingsPage)) // Settings
{
return AppResources.MarkingSettings;
}
else if (type == typeof(FeesAndBikesPage))
{
return AppResources.MarkingFeesAndBikes;
}
else if (type == typeof(ContactPage))
{
return AppResources.MarkingFeedbackAndContact;
}
else if (type == typeof(TabbedPageInfo))
{
return string.Format(AppResources.MarkingAbout, ContactPageViewModel.GetAppName(App.ModelRoot.Uris.ActiveUri));
}
else
{
return type.Name;
}
}
/// <summary>
/// Gets a description for a map page used as menu item entry and caption.
/// </summary>
/// <param name="type">Type of page to get caption for.</param>
/// <returns></returns>
public static string GetGlyphCode(Type type)
{
if (type == typeof(MapPage)) // Bikes sites
{
return "\uf5a0";
}
else if (type == typeof(MyBikesPage)) // My Bikes
{
return "\uf206";
}
else if (type == typeof(AccountPage)) // Account
{
return "\uf007";
}
else if (type == typeof(LoginPage)) // Login
{
return "\uf2f6";
}
else if (type == typeof(SettingsPage)) // Settings
{
return "\uf013";
}
else if (type == typeof(FeesAndBikesPage))
{
return "\uf153";
}
else if (type == typeof(ContactPage))
{
return "\uf095";
}
else if (type == typeof(TabbedPageInfo))
{
return "\uf05a";
}
else
{
return type.Name;
}
}
}
}

View file

@ -0,0 +1,219 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using TINK.Services;
using TINK.Services.CopriApi.ServerUris;
using TINK.View;
using TINK.View.Account;
using TINK.View.Contact;
using TINK.View.Info;
using TINK.View.Login;
using TINK.View.Map;
using TINK.View.MyBikes;
using TINK.View.Settings;
using TINK.View.Themes;
using Xamarin.Forms;
namespace TINK.ViewModel
{
/// <summary>
/// View model managing master detail menu.
/// </summary>
class MasterPageViewModel : INotifyPropertyChanged
{
/// <summary>
/// Dictionary to manage collection of menu items.
/// </summary>
private Dictionary<Type, int> m_oEntryDictionary;
/// <summary> Updates menu entries depending on state. </summary>
private void UpdateMenuEntries()
{
var l_oSelectedMenuItem = SelectedMenuItem ?? new MainPageMenuItem(0, typeof(MapPage));
for (int l_iIndex = MenuItems.Count - 1; l_iIndex >= 0; l_iIndex--)
{
MenuItems.Clear();
m_oEntryDictionary.Clear();
}
CheckAddItem(typeof(MapPage));
if (App.ModelRoot.ActiveUser.IsLoggedIn)
{
CheckAddItem(typeof(MyBikesPage));
CheckAddItem(typeof(AccountPage));
}
else
{
CheckAddItem(typeof(LoginPage));
}
if (App.ModelRoot.Uris.ActiveUri.Host.GetIsCopri()
|| App.ModelRoot.ActiveUser.IsLoggedIn)
{
CheckAddItem(typeof(SettingsPage));
}
CheckAddItem(typeof(FeesAndBikesPage)); // Fees and bikes
CheckAddItem(typeof(ContactPage)); // Feedback and contact
CheckAddItem(typeof(TabbedPageInfo)); // About sharee.bike
}
/// <summary>Adds a menu item to master detail menu.</summary>
/// <param name="p_oType">Type decribing entry to be added.</param>
private void CheckAddItem(Type p_oType)
{
if (m_oEntryDictionary.ContainsKey(p_oType))
{
// Nothing to do because has already been added.
return;
}
m_oEntryDictionary.Add(
p_oType,
m_oEntryDictionary.Count);
MenuItems.Add(
new MainPageMenuItem(m_oEntryDictionary[p_oType],
p_oType));
}
/// <summary> Removes a page from menu.</summary>
/// <param name="p_oType"></param>
private void RemoveItem(Type p_oType)
{
if (!m_oEntryDictionary.ContainsKey(p_oType))
{
// Nothing to do because item was never added/ already removed.
return;
}
MenuItems.Remove(MenuItems[m_oEntryDictionary[p_oType]]);
m_oEntryDictionary.Remove(p_oType);
}
public MasterPageViewModel()
{
MenuItems = new ObservableCollection<MainPageMenuItem>();
m_oEntryDictionary = new Dictionary<Type, int>();
App.ModelRoot.ActiveUser.StateChanged += (sender, eventargs) => OnUpdateRequired();
// Update flyout view model whenever theme is switched.
App.ModelRoot.Themes.PropertyChanged += (sender, eventargs) =>
{
if (!(sender is ServicesContainerMutable<object> themes))
return;
MasterDetailMenuTitlte = GetMasterDetailMenuTitle(themes.Active);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MasterDetailMenuTitlte)));
};
MasterDetailMenuTitlte = GetMasterDetailMenuTitle(App.ModelRoot.Themes.Active);
UpdateMenuEntries();
}
/// <summary>
/// Gets the flyout title from theme name
/// </summary>
/// <param name="theme">Name of theme.</param>
/// <returns>Flyout title.</returns>
private string GetMasterDetailMenuTitle(object theme)
{
if (!(theme is ITheme active))
return "sharee.bike";
return $"sharee.bike{(!string.IsNullOrEmpty(active.OperatorInfo) ? ($"\r\n{active.OperatorInfo}") : string.Empty)}";
}
private string masterDetailMenuTitlte;
/// <summary>
/// Holds the title of the fylout page.
/// </summary>
public string MasterDetailMenuTitlte
{
get
{
return masterDetailMenuTitlte;
}
set
{
if (masterDetailMenuTitlte == value)
return;
masterDetailMenuTitlte = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MasterDetailMenuTitlte)));
}
}
/// <summary>
/// Provides collection of menu items for master page.
/// </summary>
public ObservableCollection<MainPageMenuItem> MenuItems
{
get; private set;
}
/// <summary>
/// View service used to manage menu state.
/// </summary>
public ShowPageDelegate ShowPageViewService { get; set; }
/// <summary>
/// Gets or sets the selected item.
/// </summary>
public MainPageMenuItem SelectedMenuItem
{
get;
set;
}
/// <summary>
/// Command object which is invoked from view when an item is selected.
/// </summary>
public Command MenuItemSelected
{
get
{
return new Command(OnUpdateRequired);
}
}
/// <summary> Manges updates required due to selection of a menu or login events.</summary>
public void OnUpdateRequired()
{
if (ShowPageViewService == null)
return;
UpdateMenuEntries();
if (SelectedMenuItem == null)
{
// Nothing to do if no menu entry is selected.
return;
}
ShowPageViewService(SelectedMenuItem.TargetType);
}
#region INotifyPropertyChanged Implementation
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged == null)
return;
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}

View file

@ -0,0 +1,69 @@
using TINK.MultilingualResources;
using TINK.ViewModel.Info;
using TINK.Services.CopriApi.ServerUris;
using System.ComponentModel;
using TINK.View.Themes;
using TINK.Services;
namespace TINK.ViewModel.RootShell
{
public class AppShellViewModel : INotifyPropertyChanged
{
public AppShellViewModel()
{
App.ModelRoot.ActiveUser.StateChanged += (sender, eventargs) =>
{
// Login state changed. Update related menu entries.
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsMyBikesPageVisible)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsAccountPageVisible)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsLoginPageVisible)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsSettingsPageVisible)));
};
// Update flyout view model whenever theme is switched.
App.ModelRoot.Themes.PropertyChanged += (sender, eventargs) =>
{
if (!(sender is ServicesContainerMutable<object> themes))
return;
MasterDetailMenuTitlte = GetMasterDetailMenuTitle(themes.Active);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MasterDetailMenuTitlte)));
};
MasterDetailMenuTitlte = GetMasterDetailMenuTitle(App.ModelRoot.Themes.Active);
}
/// <summary>
/// Gets the flyout title from theme name
/// </summary>
/// <param name="theme">Name of theme.</param>
/// <returns>Flyout title.</returns>
private string GetMasterDetailMenuTitle(object theme)
{
if (!(theme is ITheme active))
return "sharee.bike";
return $"{(!string.IsNullOrEmpty(active.OperatorInfo) ? ($"{active.OperatorInfo}") : "sharee.bike")}";
}
/// <summary>
/// Holds the title of the fylout page.
/// </summary>
public string MasterDetailMenuTitlte { get; private set; }
public event PropertyChangedEventHandler PropertyChanged;
public bool IsMyBikesPageVisible => App.ModelRoot.ActiveUser.IsLoggedIn;
public bool IsAccountPageVisible => App.ModelRoot.ActiveUser.IsLoggedIn;
public bool IsLoginPageVisible => !App.ModelRoot.ActiveUser.IsLoggedIn;
public bool IsSettingsPageVisible => App.ModelRoot.Uris.ActiveUri.Host.GetIsCopri()
|| App.ModelRoot.ActiveUser.IsLoggedIn;
public string TabbedPageIngoTitle => string.Format(AppResources.MarkingAbout, ContactPageViewModel.GetAppName(App.ModelRoot.Uris.ActiveUri));
}
}

View file

@ -0,0 +1,42 @@
using Serilog;
using System.IO;
using System.Reflection;
using System.Text;
namespace TINK.ViewModel
{
public static class ViewModelResourceHelper
{
/// <summary> Get ressource prefix depending on platform.</summary>
public static string RessourcePrefix
{
get
{
#if __IOS__
return "TINK.iOS.";
#endif
#if __ANDROID__
return "TINK.Droid.";
#endif
#if WINDOWS_UWP
return "TINK.WinPhone.";
#endif
}
}
/// <summary> Gets an an embedded html ressource.</summary>
/// <param name="resrouceName">Name of resource to get.</param>
/// <returns></returns>
public static string GetSource(string resrouceName)
{
var l_oRessourceName = RessourcePrefix + resrouceName;
Log.Verbose($"Using this resource prefix { RessourcePrefix}.");
// note that the prefix includes the trailing period '.' that is required
var assembly = typeof(ViewModelResourceHelper).GetTypeInfo().Assembly;
var stream = assembly.GetManifestResourceStream(l_oRessourceName);
return stream != null
? (new StreamReader(stream, Encoding.UTF8)).ReadToEnd()
: string.Format("<!DOCTYPE html><html lang=\"de\"><body>An error occurred loading html- ressource {0}.</body>", l_oRessourceName);
}
}
}