Merge branch 'vnext' of github.com:pazof/yavsc into vnext

This commit is contained in:
2016-11-25 22:19:18 +01:00
38 changed files with 1334 additions and 130 deletions

View File

@ -88,7 +88,7 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\MonoAndroid\v7.0\Mono.Android.dll</HintPath>
</Reference>
<Reference Include="Mono.Android.Export, Version=1.0.6110.33687, Culture=neutral, PublicKeyToken=84e04ff9cfb79065, processorArchitecture=MSIL">
<Reference Include="Mono.Android.Export, Version=1.0.6149.16186, Culture=neutral, PublicKeyToken=84e04ff9cfb79065, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\MonoAndroid\v7.0\Mono.Android.Export.dll</HintPath>
</Reference>
@ -343,6 +343,7 @@
<Compile Include="Markdown\MarkdownViewRenderer.cs" />
<Compile Include="Markdown\MarkdownWebChromeClient.cs" />
<Compile Include="OAuth2\YaOAuth2Authenticator.cs" />
<Compile Include="Rendering\ImageButtonRenderer.cs" />
<Compile Include="Resources\Resource.Designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SendFilesActivity.cs" />

View File

@ -85,6 +85,7 @@ namespace Yavsc.Helpers
public async Task<JsonValue> InvokeJson(object query)
{
JsonValue jsonDoc = null;
try
{
using (Stream streamQuery = request.GetRequestStream())
@ -109,7 +110,12 @@ namespace Yavsc.Helpers
// TODO err logging
Debug.Print($"Web request failed: {request.ToString()}\n" + ex.ToString());
}
return jsonDoc;
catch (Exception ex)
{
Debug.Print($"Web request failed: {request.ToString()}\n" + ex.ToString());
}
return jsonDoc;
}
}
}

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="fr.pschneider.bas" android:installLocation="auto">
<uses-sdk android:minSdkVersion="15" />
<application android:allowBackup="true" android:label="Booking Star" android:icon="@drawable/icon" android:theme="@style/MainTheme">
<application android:allowBackup="true" android:label="Booking Star" android:icon="@drawable/icon" android:theme="@style/MainTheme" android:configChanges="navigation|screenLayout|orientation|screenSize|smallestScreenSize">
<meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="AIzaSyBLSEDhZixwpHDsWmO2pKwgGDJReoTuQ7A" />
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
<receiver android:name="com.google.android.gms.gcm.GcmReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND">
@ -11,13 +11,13 @@
<category android:name="fr.pschneider.bas" />
</intent-filter>
</receiver>
<activity android:name="fr.pschneider.bas.MainActivity" android:label="Book a Star">
<activity android:name="fr.pschneider.bas.MainActivity" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="fr.pschneider.bas.SendFilesActivity" android:label="@string/app_name">
<activity android:name="fr.pschneider.bas.SendFilesActivity" android:label="@string/send_to_app_name">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
@ -63,4 +63,8 @@
<uses-permission android:name="android.permission.PERSISTENT_ACTIVITY" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_SOCIAL_STREAM" />
</manifest>

View File

@ -0,0 +1,296 @@
// ***********************************************************************
// Assembly : XLabs.Forms.Droid
// Author : XLabs Team
// Created : 12-27-2015
//
// Last Modified By : XLabs Team
// Last Modified On : 01-04-2016
// ***********************************************************************
// <copyright file="ImageButtonRenderer.cs" company="XLabs Team">
// Copyright (c) XLabs Team. All rights reserved.
// </copyright>
// <summary>
// This project is licensed under the Apache 2.0 license
// https://github.com/XLabs/Xamarin-Forms-Labs/blob/master/LICENSE
//
// XLabs is a open source project that aims to provide a powerfull and cross
// platform set of controls tailored to work with Xamarin Forms.
// </summary>
// ***********************************************************************
//
using System;
using System.ComponentModel;
using System.Threading.Tasks;
using Android.Graphics;
using Android.Graphics.Drawables;
using Android.Views;
using XLabs.Enums;
using XLabs.Forms.Extensions;
using Color = Xamarin.Forms.Color;
using View = Android.Views.View;
using BookAStar.Rendering;
using BookAStar.Views;
using Xamarin.Forms.Platform.Android.AppCompat;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using System.IO;
using BookAStar.Helpers;
using System.Reflection;
using System.Threading;
[assembly: ExportRenderer(typeof(ImageButton), typeof(ImageButtonRenderer))]
namespace BookAStar.Rendering
{
/// <summary>
/// Draws a button on the Android platform with the image shown in the right
/// position with the right size.
/// </summary>
public partial class ImageButtonRenderer : Xamarin.Forms.Platform.Android.AppCompat.ButtonRenderer
{
private static float _density = float.MinValue;
/// <summary>
/// Sets up the button including the image.
/// </summary>
/// <param name="e">The event arguments.</param>
private ImageButton ImageButton
{
get { return (ImageButton)Element; }
}
protected override async void OnElementChanged(ElementChangedEventArgs<Button> e)
{
base.OnElementChanged(e);
_density = Resources.DisplayMetrics.Density;
var targetButton = Control;
if (targetButton != null) targetButton.SetOnTouchListener(TouchListener.Instance.Value);
if (Element != null && Element.Font != Font.Default && targetButton != null) targetButton.Typeface = Element.Font.ToExtendedTypeface(Context);
if (Element != null && ImageButton.Source != null) await SetImageSourceAsync(targetButton, ImageButton).ConfigureAwait(false);
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected override void Dispose (bool disposing)
{
base.Dispose (disposing);
if (disposing && Control != null) {
Control.Dispose ();
}
}
int imageWitdh = 0;
int imageHeight = 0;
int imageWitdhRequest = 0;
int imageHeightRequest = 0;
// this is called before OnElementChanged ...
/*
public override SizeRequest GetDesiredSize(int widthConstraint, int heightConstraint)
{
switch (ImageButton.Orientation)
{
case ImageOrientation.ImageOnBottom:
case ImageOrientation.ImageOnTop:
return base.GetDesiredSize(widthConstraint, heightConstraint+ imageHeightRequest);
case ImageOrientation.ImageToRight:
case ImageOrientation.ImageToLeft:
return base.GetDesiredSize(widthConstraint + imageWitdhRequest, heightConstraint);
default:
return base.GetDesiredSize(widthConstraint, heightConstraint);
}
}*/
/// <summary>
/// Sets the image source.
/// </summary>
/// <param name="targetButton">The target button.</param>
/// <param name="model">The model.</param>
/// <returns>A <see cref="Task"/> for the awaited operation.</returns>
private async Task SetImageSourceAsync(Android.Widget.Button targetButton, ImageButton model)
{
if (targetButton == null || targetButton.Handle == IntPtr.Zero || model == null) return;
// const int Padding = 10;
var source = model.IsEnabled ? model.Source : model.DisabledSource ?? model.Source;
using (var bitmap = await GetBitmapAsync(source))
{
if (bitmap == null)
targetButton.SetCompoundDrawables(null, null, null, null);
else
{
var drawable = new BitmapDrawable(bitmap);
var tintColor = model.IsEnabled ? model.ImageTintColor : model.DisabledImageTintColor;
if (tintColor != Color.Transparent)
{
drawable.SetTint(tintColor.ToAndroid());
drawable.SetTintMode(PorterDuff.Mode.SrcIn);
}
imageWitdh = drawable.Bitmap.Width;
imageHeight = drawable.Bitmap.Height;
imageWitdhRequest = (int)model.ImageWidthRequest;
imageHeightRequest = (int)model.ImageHeightRequest;
if (imageHeightRequest <= 0) imageHeightRequest = imageHeight;
if (imageWitdhRequest <= 0) imageWitdhRequest = imageWitdh;
using (var scaledDrawable = GetScaleDrawable(drawable, imageWitdh, imageHeight))
{
Drawable left = null;
Drawable right = null;
Drawable top = null;
Drawable bottom = null;
int padding = 2; // model.Padding;
targetButton.CompoundDrawablePadding = RequestToPixels(padding);
targetButton.Gravity = GravityFlags.CenterHorizontal | GravityFlags.CenterVertical;
switch (model.Orientation)
{
case ImageOrientation.ImageToLeft:
left = scaledDrawable;
if (ImageButton.HeightRequest < imageHeightRequest)
ImageButton.HeightRequest = imageHeightRequest;
break;
case ImageOrientation.ImageToRight:
right = scaledDrawable;
if (ImageButton.HeightRequest < imageHeightRequest)
ImageButton.HeightRequest = imageHeightRequest;
break;
case ImageOrientation.ImageOnTop:
top = scaledDrawable;
if (ImageButton.WidthRequest < imageWitdhRequest)
ImageButton.WidthRequest = imageWitdhRequest;
break;
case ImageOrientation.ImageOnBottom:
bottom = scaledDrawable;
if (ImageButton.WidthRequest < imageWitdhRequest)
ImageButton.WidthRequest = imageWitdhRequest;
break;
case ImageOrientation.ImageCentered:
top = scaledDrawable;
if (ImageButton.HeightRequest < imageHeightRequest)
ImageButton.HeightRequest = imageHeightRequest;
if (ImageButton.WidthRequest < imageWitdhRequest)
ImageButton.WidthRequest = imageWitdhRequest;
break;
}
targetButton.SetCompoundDrawables(left, top, right, bottom);
// this.MeasureChildren(model.ImageWidthRequest, model.ImageHeightRequest);
}
}
}
}
/// <summary>
/// Gets a <see cref="Bitmap"/> for the supplied <see cref="ImageSource"/>.
/// </summary>
/// <param name="source">The <see cref="ImageSource"/> to get the image for.</param>
/// <returns>A loaded <see cref="Bitmap"/>.</returns>
private async Task<Bitmap> GetBitmapAsync(ImageSource imagesource)
{
var uriImageLoader = imagesource as UriImageSource;
if (uriImageLoader != null && uriImageLoader.Uri != null)
{
using (var client = UserHelpers.CreateClient())
{
using (var response = await client.GetAsync(uriImageLoader.Uri))
{
var data = await response.Content.ReadAsByteArrayAsync();
return await BitmapFactory.DecodeByteArrayAsync(data, 0, data.Length);
}
}
}
var resImageLoader = imagesource as StreamImageSource;
if (resImageLoader != null && resImageLoader.Stream != null)
{
return await BitmapFactory.DecodeStreamAsync(await resImageLoader.Stream(CancellationToken.None));
}
return null;
}
/// <summary>
/// Called when the underlying model's properties are changed.
/// </summary>
/// <param name="sender">The Model used.</param>
/// <param name="e">The event arguments.</param>
protected override async void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == ImageButton.SourceProperty.PropertyName ||
e.PropertyName == ImageButton.DisabledSourceProperty.PropertyName ||
e.PropertyName == VisualElement.IsEnabledProperty.PropertyName ||
e.PropertyName == ImageButton.ImageTintColorProperty.PropertyName ||
e.PropertyName == ImageButton.DisabledImageTintColorProperty.PropertyName)
{
await SetImageSourceAsync(Control, ImageButton).ConfigureAwait(false);
}
base.OnElementPropertyChanged(sender, e);
}
/// <summary>
/// Returns a <see cref="Drawable"/> with the correct dimensions from an
/// Android resource id.
/// </summary>
/// <param name="drawable">An android <see cref="Drawable"/>.</param>
/// <param name="width">The width to scale to.</param>
/// <param name="height">The height to scale to.</param>
/// <returns>A scaled <see cref="Drawable"/>.</returns>
private Drawable GetScaleDrawable(Drawable drawable, int width, int height)
{
var returnValue = new ScaleDrawable(drawable, 0, 100, 100).Drawable;
returnValue.SetBounds(0, 0, RequestToPixels(width), RequestToPixels(height));
return returnValue;
}
/// <summary>
/// Returns a drawable dimension modified according to the current display DPI.
/// </summary>
/// <param name="sizeRequest">The requested size in relative units.</param>
/// <returns>Size in pixels.</returns>
public int RequestToPixels(int sizeRequest)
{
if (_density == float.MinValue)
{
if (Resources.Handle == IntPtr.Zero || Resources.DisplayMetrics.Handle == IntPtr.Zero)
_density = 1.0f;
else
_density = Resources.DisplayMetrics.Density;
}
return (int)(sizeRequest * _density);
}
}
//Hot fix for the layout positioning issue on Android as described in http://forums.xamarin.com/discussion/20608/fix-for-button-layout-bug-on-android
class TouchListener : Java.Lang.Object, View.IOnTouchListener
{
public static readonly Lazy<TouchListener> Instance = new Lazy<TouchListener>(() => new TouchListener());
/// <summary>
/// Make TouchListener a singleton.
/// </summary>
private TouchListener()
{ }
public bool OnTouch(View v, MotionEvent e)
{
var buttonRenderer = v.Tag as Xamarin.Forms.Platform.Android.AppCompat.ButtonRenderer;
if (buttonRenderer != null && e.Action == MotionEventActions.Down) buttonRenderer.Control.Text = buttonRenderer.Element.Text;
return false;
}
}
}

View File

@ -5341,26 +5341,26 @@ namespace BookAStar.Droid
// aapt resource value: 0x7f07004c
public const int ApplicationName = 2131165260;
// aapt resource value: 0x7f070061
public const int Date = 2131165281;
// aapt resource value: 0x7f070062
public const int Date = 2131165282;
// aapt resource value: 0x7f070060
public const int Description = 2131165280;
// aapt resource value: 0x7f070061
public const int Description = 2131165281;
// aapt resource value: 0x7f07004b
public const int Hello = 2131165259;
// aapt resource value: 0x7f070062
public const int Location = 2131165282;
// aapt resource value: 0x7f070063
public const int Pricing = 2131165283;
public const int Location = 2131165283;
// aapt resource value: 0x7f07005e
public const int Settings = 2131165278;
// aapt resource value: 0x7f070064
public const int Pricing = 2131165284;
// aapt resource value: 0x7f07005f
public const int Title = 2131165279;
public const int Settings = 2131165279;
// aapt resource value: 0x7f070060
public const int Title = 2131165280;
// aapt resource value: 0x7f070034
public const int abc_action_bar_home_description = 2131165236;
@ -5437,8 +5437,8 @@ namespace BookAStar.Droid
// aapt resource value: 0x7f070049
public const int bottom_sheet_behavior = 2131165257;
// aapt resource value: 0x7f07005d
public const int cancel = 2131165277;
// aapt resource value: 0x7f07005e
public const int cancel = 2131165278;
// aapt resource value: 0x7f07004a
public const int character_counter_pattern = 2131165258;
@ -5548,8 +5548,8 @@ namespace BookAStar.Droid
// aapt resource value: 0x7f070052
public const int fullscreen_button = 2131165266;
// aapt resource value: 0x7f070067
public const int google_app_id = 2131165287;
// aapt resource value: 0x7f070068
public const int google_app_id = 2131165288;
// aapt resource value: 0x7f07004f
public const int invalid_date = 2131165263;
@ -5614,8 +5614,8 @@ namespace BookAStar.Droid
// aapt resource value: 0x7f070055
public const int pause = 2131165269;
// aapt resource value: 0x7f070066
public const int picture = 2131165286;
// aapt resource value: 0x7f070067
public const int picture = 2131165287;
// aapt resource value: 0x7f070056
public const int seekbar = 2131165270;
@ -5623,6 +5623,9 @@ namespace BookAStar.Droid
// aapt resource value: 0x7f070057
public const int select_an_action = 2131165271;
// aapt resource value: 0x7f07005b
public const int send_to_app_name = 2131165275;
// aapt resource value: 0x7f070047
public const int status_bar_notification_info_overflow = 2131165255;
@ -5638,17 +5641,17 @@ namespace BookAStar.Droid
// aapt resource value: 0x7f070059
public const int time_duration = 2131165273;
// aapt resource value: 0x7f070064
public const int url = 2131165284;
// aapt resource value: 0x7f070065
public const int url_hint = 2131165285;
public const int url = 2131165285;
// aapt resource value: 0x7f070066
public const int url_hint = 2131165286;
// aapt resource value: 0x7f07005d
public const int yavscAccountTypeName = 2131165277;
// aapt resource value: 0x7f07005c
public const int yavscAccountTypeName = 2131165276;
// aapt resource value: 0x7f07005b
public const int yavscIdentRemoved = 2131165275;
public const int yavscIdentRemoved = 2131165276;
static String()
{

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Booking star</string>
<string name="send_to_app_name">Vers les étoiles</string>
<string name="yavscIdentRemoved">Identification supprimée</string>
<string name="yavscAccountTypeName">Booking star</string>
<string name="cancel">Annuler</string>

View File

@ -1,4 +1,8 @@
using System;
using Microsoft.AspNet.SignalR.Client;
using System.Net;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Forms;
using XLabs.Forms.Mvvm;
using XLabs.Forms.Pages;
@ -12,27 +16,23 @@ using XLabs.Enums;
namespace BookAStar
{
using System.Threading.Tasks;
using Data;
using Interfaces;
using Model;
using Model.UI;
using Pages;
using Plugin.Connectivity;
using Microsoft.AspNet.SignalR.Client;
using Model.Social.Messaging;
using ViewModels.Messaging;
using ViewModels.UserProfile;
using Pages.UserProfile;
using ViewModels.EstimateAndBilling;
using System.Net;
public partial class App : Application // superclass new in 1.3
{
public static IPlatform PlatformSpecificInstance { get; set; }
public static string AppName { get; set; }
[Obsolete("Instead using this, use new static properties.")]
public static App CurrentApp { get { return Current as App; } }
@ -63,7 +63,7 @@ namespace BookAStar
MainSettings.UserChanged += MainSettings_UserChanged;
CrossConnectivity.Current.ConnectivityChanged += (conSender, args) =>
{ App.IsConnected = args.IsConnected; };
SetupHubConnection();
MainSettings_UserChanged(this, null);
if (CrossConnectivity.Current.IsConnected)
StartHubConnection();
}
@ -149,6 +149,7 @@ namespace BookAStar
ViewFactory.Register<EditBillingLinePage, BillingLineViewModel>();
ViewFactory.Register<EditEstimatePage, EditEstimateViewModel>();
ViewFactory.Register<UserFiles, DirectoryInfoViewModel>();
ViewFactory.Register<UserProfilePage, UserProfileViewModel>();
ConfigManager = new GenericConfigSettingsMgr(s =>
MainSettings.AppSettings.GetValueOrDefault<string>(s, MainSettings.SettingsDefault), null);
}
@ -174,7 +175,20 @@ namespace BookAStar
BookQueriesPage bQueriesPage;
AccountChooserPage accChooserPage;
HomePage home;
HomePage homePage;
UserProfilePage userProfilePage;
ChatPage chatPage;
private void ShowPage(Page page)
{
if (masterDetail.Detail.Navigation.NavigationStack.Contains(page))
{
if (masterDetail.Detail.Navigation.NavigationStack.Last() == page) return;
masterDetail.Detail.Navigation.RemovePage(page);
page.Parent = null;
}
masterDetail.Detail.Navigation.PushAsync(page);
}
private void BuildMainPage()
{
@ -186,9 +200,15 @@ namespace BookAStar
Icon = "icon.png",
BindingContext = new BookQueriesViewModel()
};
home = new HomePage() { Title = "Accueil", Icon = "icon.png" };
homePage = new HomePage() { Title = "Accueil", Icon = "icon.png" };
userProfilePage = new UserProfilePage { Title = "Profile utilisateur", Icon = "ic_corp_icon.png",
BindingContext = new UserProfileViewModel() };
chatPage = new ChatPage
{
Title = "Chat",
Icon = "",
BindingContext = new ChatViewModel()
};
// var mainPage = new NavigationPage(bQueriesPage);
masterDetail = new ExtendedMasterDetailPage()
@ -202,17 +222,18 @@ namespace BookAStar
BindingContext = new DashboardViewModel()
};
// masterDetail.Detail = home;
masterDetail.Detail = new NavigationPage(home);
masterDetail.Detail = new NavigationPage(homePage);
ToolbarItem tiSetts = new ToolbarItem()
{
// FIXME what for? Priority = 0,
Text = "Paramètres",
Icon = "ic_corp_icon.png",
Command = new Command(
() => { NavigationService.NavigateTo<AccountChooserPage>(); }
)
() =>
{
ShowPage(userProfilePage);
} )
};
ToolbarItem tiHome = new ToolbarItem()
@ -220,8 +241,9 @@ namespace BookAStar
Text = "Accueil",
Icon = "icon.png",
Command = new Command(
() => { NavigationService.NavigateTo<ChatPage>(); }
)
() => {
ShowPage(homePage);
})
};
ToolbarItem tiPubChat= new ToolbarItem()
@ -229,18 +251,19 @@ namespace BookAStar
Text = "Chat",
Icon = "chat_icon_s.png",
Command = new Command(
() => { NavigationService.NavigateTo<ChatPage>(); }
() => { ShowPage(chatPage); }
)
};
masterDetail.ToolbarItems.Add(tiHome);
masterDetail.ToolbarItems.Add(tiSetts);
masterDetail.ToolbarItems.Add(tiPubChat);
this.MainPage = masterDetail;
NavigationService = new NavigationService(masterDetail.Detail.Navigation);
}
public static Task<string> DisplayActionSheet(string title, string cancel, string destruction, string [] buttons)
{
var currentPage = ((NavigationPage)Current.MainPage).CurrentPage;
var currentPage = CurrentApp.masterDetail.Detail.Navigation.NavigationStack.Last();
return currentPage.DisplayActionSheet(title, cancel, destruction, buttons);
}
@ -259,11 +282,6 @@ namespace BookAStar
}
private void TiPubChat_Clicked(object sender, EventArgs e)
{
}
public static INavigationService NavigationService { protected set; get; }
public static bool isConnected;
public static bool IsConnected { get { return isConnected; }
@ -323,20 +341,21 @@ namespace BookAStar
}
);
});
MainSettings_UserChanged(this, null);
}
private void MainSettings_UserChanged(object sender, EventArgs e)
{
if (MainSettings.CurrentUser == null)
{
chatHubConnection.Headers.Clear();
chatHubConnection.Dispose();
chatHubConnection = null;
chatHubProxy = null;
}
else
{
var token = MainSettings.CurrentUser.YavscTokens.AccessToken;
chatHubConnection.Headers.Add(
"Authorization", $"Bearer {token}");
SetupHubConnection();
chatHubConnection.Headers.Add("Authorization", $"Bearer {token}");
}
}

View File

@ -0,0 +1,44 @@
using Xamarin.Forms;
namespace BookAStar.Behaviors
{
public class IntegerEntryBehavior : Behavior<Entry>
{
public static readonly BindableProperty MinProperty = BindableProperty.Create("Min", typeof(int), typeof(IntegerEntryBehavior), 0);
public static readonly BindableProperty MaxProperty = BindableProperty.Create("Max", typeof(int), typeof(IntegerEntryBehavior), 0);
public int Min
{
get { return (int)GetValue(MinProperty); }
set { SetValue(MinProperty, value); }
}
public int Max
{
get { return (int)GetValue(MaxProperty); }
set { SetValue(MaxProperty, value); }
}
protected override void OnAttachedTo(Entry bindable)
{
bindable.TextChanged += bindable_TextChanged;
}
private void bindable_TextChanged(object sender, TextChangedEventArgs e)
{
int val;
if (int.TryParse(e.NewTextValue, out val))
{
IsValid = (Min > Max) || (Max >= val && val >= Min);
}
else IsValid = false;
}
protected override void OnDetachingFrom(Entry bindable)
{
bindable.TextChanged -= bindable_TextChanged;
}
public bool IsValid { get; private set; }
}
}

View File

@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms;
namespace BookAStar.Behaviors
{
@ -54,7 +49,7 @@ namespace BookAStar.Behaviors
private void bindable_TextChanged(object sender, TextChangedEventArgs e)
{
//if (MaxLength != null && MaxLength.HasValue)
if (e.NewTextValue.Length > 0 && e.NewTextValue.Length > MaxLength)
if (e.NewTextValue.Length > MaxLength)
((Entry)sender).Text = e.NewTextValue.Substring(0, MaxLength);
}
@ -63,5 +58,6 @@ namespace BookAStar.Behaviors
bindable.TextChanged -= bindable_TextChanged;
}
}
}

View File

@ -43,6 +43,7 @@
<Compile Include="Attributes\CurrencyAttribute.cs" />
<Compile Include="Attributes\DisplayAttribute.cs" />
<Compile Include="Behaviors\EmailValidatorBehavior.cs" />
<Compile Include="Behaviors\IntegerEntryBehavior.cs" />
<Compile Include="Behaviors\MaxLengthValidator.cs" />
<Compile Include="Behaviors\DecimalValidatorBehavior.cs" />
<Compile Include="Behaviors\PickerBehavior.cs" />
@ -58,6 +59,15 @@
<Compile Include="Model\Social\Messaging\ChatStatus.cs" />
<Compile Include="Model\Social\Messaging\PrivateMessage.cs" />
<Compile Include="Model\UI\PageState.cs" />
<Compile Include="Pages\EstimatePages\EstimatesClientPage.xaml.cs">
<DependentUpon>EstimatesClientPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\EstimatePages\EstimatesProviderPage.xaml.cs">
<DependentUpon>EstimatesProviderPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\EstimatePages\ViewEstimatePage.xaml.cs">
<DependentUpon>ViewEstimatePage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\UserProfile\UserFiles.xaml.cs">
<DependentUpon>UserFiles.xaml</DependentUpon>
</Compile>
@ -79,8 +89,9 @@
<Compile Include="Pages\DocSigning.xaml.cs">
<DependentUpon>DocSigning.xaml</DependentUpon>
</Compile>
<Compile Include="ViewModels\Signing\EstimateSignaturePad.cs" />
<Compile Include="ViewModels\Signing\DocSigningViewModel.cs" />
<Compile Include="ViewModels\Signing\SignaturePadConfigViewModel.cs" />
<Compile Include="ViewModels\UserProfile\UserProfileViewModel.cs" />
<Compile Include="ViewModels\UserProfile\DirectoryInfoViewModel.cs" />
<Compile Include="Views\EnumPicker.cs" />
<Compile Include="Converters\BooleanToObjectConverter.cs" />
@ -109,16 +120,16 @@
<Compile Include="Interfaces\IBillingLine.cs" />
<Compile Include="Interfaces\IEstimate.cs" />
<Compile Include="Pages\BlogPages\BlogPage.cs" />
<Compile Include="Pages\Estimate\BookQueriesPage.xaml.cs">
<Compile Include="Pages\EstimatePages\BookQueriesPage.xaml.cs">
<DependentUpon>BookQueriesPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\Estimate\BookQueryPage.xaml.cs">
<Compile Include="Pages\EstimatePages\BookQueryPage.xaml.cs">
<DependentUpon>BookQueryPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\ChatPage.xaml.cs">
<DependentUpon>ChatPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\Estimate\EditBillingLinePage.xaml.cs">
<Compile Include="Pages\EstimatePages\EditBillingLinePage.xaml.cs">
<DependentUpon>EditBillingLinePage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\UserProfile\DashboardPage.xaml.cs">
@ -169,7 +180,7 @@
<Compile Include="Model\Auth\Tokens.cs" />
<Compile Include="Model\Auth\User.cs" />
<Compile Include="Model\Workflow\Estimate.cs" />
<Compile Include="Pages\Estimate\EditEstimatePage.xaml.cs">
<Compile Include="Pages\EstimatePages\EditEstimatePage.xaml.cs">
<DependentUpon>EditEstimatePage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\Oooops\HomePage.xaml.cs">
@ -184,6 +195,7 @@
<Compile Include="ViewModels\EstimateAndBilling\EditEstimateViewModel.cs" />
<Compile Include="ViewModels\UserProfile\UserLoginViewModel.cs" />
<Compile Include="ViewModels\ViewModelBase.cs" />
<Compile Include="Views\ImageButton.cs" />
<Compile Include="Views\MarkdownView.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Pages\Oooops\SearchPage.xaml.cs">
@ -249,7 +261,7 @@
<Private>True</Private>
</Reference>
<Reference Include="Mono.Android">
<HintPath>..\..\..\..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\MonoAndroid\v6.0\Mono.Android.dll</HintPath>
<HintPath>..\..\..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\MonoAndroid\v7.0\Mono.Android.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>..\..\packages\Newtonsoft.Json.9.0.1\lib\portable-net45+wp80+win8+wpa81\Newtonsoft.Json.dll</HintPath>
@ -334,7 +346,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Pages\Estimate\BookQueryPage.xaml">
<EmbeddedResource Include="Pages\EstimatePages\BookQueryPage.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
<SubType>Designer</SubType>
</EmbeddedResource>
@ -346,19 +358,19 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Pages\Estimate\EditEstimatePage.xaml">
<EmbeddedResource Include="Pages\EstimatePages\EditEstimatePage.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Pages\Estimate\BookQueriesPage.xaml">
<EmbeddedResource Include="Pages\EstimatePages\BookQueriesPage.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Pages\Estimate\EditBillingLinePage.xaml">
<EmbeddedResource Include="Pages\EstimatePages\EditBillingLinePage.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
<SubType>Designer</SubType>
</EmbeddedResource>
@ -448,6 +460,24 @@
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Pages\EstimatePages\EstimatesProviderPage.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Pages\EstimatePages\EstimatesClientPage.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Pages\EstimatePages\ViewEstimatePage.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
<Import Project="..\..\packages\Xamarin.Forms.2.3.2.127\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+MonoTouch10+Xamarin.iOS10\Xamarin.Forms.targets" Condition="Exists('..\..\packages\Xamarin.Forms.2.3.2.127\build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+MonoTouch10+Xamarin.iOS10\Xamarin.Forms.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">

View File

@ -13,7 +13,6 @@ namespace BookAStar.Helpers
public static class UserHelpers
{
public static ImageSource Avatar(string avatarPath)
{
var result = avatarPath == null ?

View File

@ -2,6 +2,7 @@
using BookAStar.Helpers;
using BookAStar.Model.Interfaces;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
@ -76,6 +77,9 @@ namespace BookAStar.Model.Workflow
return Bill?.Aggregate((decimal)0, (t, l) => t + l.Count * l.UnitaryCost) ?? (decimal)0;
}
}
public DateTime LatestValidationDate { get; set; }
public DateTime ClientApprouvalDate { get; set; }
}
}

View File

@ -1,6 +1,22 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:signature="clr-namespace:SignaturePad.Forms;assembly=SignaturePad.Forms"
xmlns:views="clr-namespace:BookAStar.Views;assembly=BookAStar"
x:Class="BookAStar.ViewModels.Signing.DocSigning">
<Label Text="{Binding MainText}" VerticalOptions="Center" HorizontalOptions="Center" />
<StackLayout>
<views:MarkdownView x:Name="mdView"
Editable="False"
HorizontalOptions="FillAndExpand"
Markdown="{Binding Document}"
VerticalOptions="Start" />
<signature:SignaturePadView x:Name="padView"
HeightRequest="150" WidthRequest="240"
BackgroundColor="White"
CaptionText="Caption This" CaptionTextColor="Black"
ClearText="Clear Me!" ClearTextColor="Red"
PromptText="Prompt Here" PromptTextColor="Red"
SignatureLineColor="Aqua" StrokeColor="Black" StrokeWidth="2" />
<Button Clicked="OnGetStats" Text="Get Signature Stats" />
</StackLayout>
</ContentPage>

View File

@ -1,4 +1,5 @@
using System;
using SignaturePad.Forms;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -14,5 +15,45 @@ namespace BookAStar.ViewModels.Signing
{
InitializeComponent();
}
private async void OnGetStats(object sender, EventArgs e)
{
var points = padView.Points.ToArray();
var image = await padView.GetImageStreamAsync(SignatureImageFormat.Png);
var pointCount = points.Count();
var imageSize = image.Length / 1000;
var linesCount = points.Count(p => p == Point.Zero) + (points.Length > 0 ? 1 : 0);
image.Dispose();
await DisplayAlert("Stats", $"The signature has {linesCount} lines or {pointCount} points, and is {imageSize:#,###.0}KB (in memory) when saved as a PNG.", "Cool");
}
private async void OnChangeTheme(object sender, EventArgs e)
{
var action = await DisplayActionSheet("Change Theme", "Cancel", null, "White", "Black", "Aqua");
switch (action)
{
case "White":
padView.BackgroundColor = Color.White;
padView.StrokeColor = Color.Black;
padView.ClearTextColor = Color.Black;
padView.ClearText = "Clear Markers";
break;
case "Black":
padView.BackgroundColor = Color.Black;
padView.StrokeColor = Color.White;
padView.ClearTextColor = Color.White;
padView.ClearText = "Clear Chalk";
break;
case "Aqua":
padView.BackgroundColor = Color.Aqua;
padView.StrokeColor = Color.Red;
padView.ClearTextColor = Color.Black;
padView.ClearText = "Clear The Aqua";
break;
}
}
}
}

View File

@ -25,7 +25,8 @@
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<ScrollView>
<StackLayout Padding="10,10,10,10" x:Name="mainLayout">
<ListView RefreshCommand="{Binding RefreshQueries}" IsPullToRefreshEnabled="True"
@ -48,7 +49,7 @@
</StackLayout>
<StackLayout Orientation="Vertical" HorizontalOptions="FillAndExpand">
<Label LineBreakMode="WordWrap" Text="{Binding Location.Address}"/>
<Label Text="{Binding Previsionnal}"/>
<Label Text="{Binding Previsionnal}" />
<Label Text="{Binding Id}" HorizontalTextAlignment="End"/>
</StackLayout>
</StackLayout>
@ -59,5 +60,6 @@
</ListView>
</StackLayout>
</ScrollView>
</ContentPage.Content>
</ContentPage>

View File

@ -27,7 +27,13 @@
</StackLayout>
<StackLayout Orientation="Vertical" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" >
<maps:Map x:Name="map" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"></maps:Map>
<Button Text="{Binding EditEstimateButtonText}" Clicked="OnEditEstimate" VerticalOptions="End"/>
<StackLayout Orientation="Horizontal">
<Button Text="{x:Static local:Strings.ViewEstimate}"
BorderRadius="50" BorderWidth="2" BorderColor="Aqua" x:Name ="btn"
IsEnabled="{Binding EstimationDone}"
Clicked="OnViewEstimate" Image="exclam.png" />
<Button Text="{Binding EditEstimateButtonText}" Clicked="OnEditEstimate" />
</StackLayout>
</StackLayout>
</StackLayout>
</ContentPage>

View File

@ -1,13 +1,14 @@
using System;
using System.Linq;
using Xamarin.Forms;
using Xamarin.Forms.Maps;
namespace BookAStar.Pages
{
using Data;
using EstimatePages;
using Model;
using Model.Workflow;
using System.Linq;
using ViewModels.EstimateAndBilling;
public partial class BookQueryPage : ContentPage
@ -71,23 +72,38 @@ namespace BookAStar.Pages
{
DataManager.Current.Contacts.Merge(BookQuery.Client);
DataManager.Current.Contacts.SaveEntity();
estimateToEdit = new Estimate()
estimateToEdit = new Estimate
{
ClientId = BookQuery.Client.UserId,
CommandId = BookQuery.Id,
OwnerId = MainSettings.CurrentUser.Id,
Id = 0,
Description = "# **Hello Estimate!**"
Id = 0
};
editEstimateViewModel = new EditEstimateViewModel(estimateToEdit, LocalState.New);
editEstimateViewModel = new EditEstimateViewModel(estimateToEdit);
}
else
editEstimateViewModel = new EditEstimateViewModel(estimateToEdit, LocalState.UpToDate);
editEstimateViewModel = new EditEstimateViewModel(estimateToEdit);
DataManager.Current.EstimationCache.Add(editEstimateViewModel);
}
App.NavigationService.NavigateTo<EditEstimatePage>(true,
editEstimateViewModel);
}
private async void OnViewEstimate(object sender, EventArgs ev)
{
var bookQueryViewModel = (BookQueryViewModel) BindingContext;
var buttons = bookQueryViewModel.Estimates.Select(e => $"{e.Id} / {e.Title} / {e.Total}").ToArray();
var action = await App.DisplayActionSheet("Estimations validées", "Annuler", null, buttons);
if (buttons.Contains(action))
{
var index = Array.IndexOf(buttons,action);
var estimate = bookQueryViewModel.Estimates[index];
App.NavigationService.NavigateTo<ViewEstimatePage>(true,
new EditEstimateViewModel(estimate));
}
}
protected override void OnSizeAllocated(double width, double height)

View File

@ -50,9 +50,6 @@
<behaviors:EditorMaxLengthValidator x:Name="descriptionValidator" MaxLength="512" />
</Editor.Behaviors>
</Editor>
<Image HorizontalOptions="End" Style="{Binding Source={x:Reference descriptionValidator},
Path=IsValid,
Converter={StaticResource boolToStyleImage}}" />
</StackLayout>
<Label Text="Durée de la prestation"
@ -77,8 +74,19 @@
</StackLayout>
<Label Text="Quantité facturée" Style="{StaticResource InputLabelStyle}"></Label>
<StackLayout Orientation="Horizontal">
<Entry Text="{Binding Count, Mode=TwoWay}" Placeholder="Quantité" Keyboard="Numeric"
Style="{StaticResource BigEntry}"/>
Style="{StaticResource BigEntry}">
<Entry.Behaviors>
<behaviors:IntegerEntryBehavior x:Name="countValidator" Min="0" Max="10" />
</Entry.Behaviors>
</Entry>
<Image x:Name="countSuccessErrorImage"
Style="{Binding Source={x:Reference countValidator},
Path=IsValid,
Converter={StaticResource boolToStyleImage}}" />
</StackLayout>
<Label Text="Prix unitaire" Style="{StaticResource InputLabelStyle}"></Label>
<StackLayout Orientation="Horizontal">
<Entry Text="{Binding UnitaryCostText, Mode=TwoWay}" Placeholder="Prix"
@ -94,12 +102,12 @@
Converter={StaticResource boolToStyleImage}}" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Button Text="Supprimer"
Command="{Binding DeleteCommand}"
Clicked="OnDeleteClicked"></Button>
<Button Text="Términé"
Command="{Binding ValidateCommand}"
Clicked="OnValidateClicked"></Button>
<Button Text="Supprimer"
Command="{Binding DeleteCommand}"
Clicked="OnDeleteClicked"></Button>
</StackLayout>
</StackLayout>
</ScrollView>

View File

@ -15,6 +15,7 @@
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<ScrollView>
<StackLayout Padding="10,10,10,10" x:Name="mainLayout">
<Grid MinimumHeightRequest="12">
@ -77,4 +78,5 @@
</StackLayout>
</StackLayout>
</ScrollView>
</ContentPage.Content>
</ContentPage>

View File

@ -49,9 +49,9 @@ namespace BookAStar.Pages
bill.Add(com);
DataManager.Current.EstimationCache.SaveEntity();
})};
lineView.PropertyChanged += LineView_PropertyChanged;
App.NavigationService.NavigateTo<EditBillingLinePage>(
true,
new object[] { lineView } );
true, lineView );
}
protected void OnEditLine(object sender, ItemTappedEventArgs e)
{
@ -63,12 +63,14 @@ namespace BookAStar.Pages
DataManager.Current.EstimationCache.SaveEntity();
})
};
lineView.PropertyChanged += LineView_PropertyChanged;
lineView.PropertyChanged += LineView_PropertyChanged;
App.NavigationService.NavigateTo<EditBillingLinePage>(
true,
new object[] { lineView });
true, lineView );
}
private void LineView_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
DataManager.Current.EstimationCache.SaveEntity();

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="BookAStar.Pages.EstimatesClientPage">
<Label Text="{Binding MainText}" VerticalOptions="Center" HorizontalOptions="Center" />
</ContentPage>

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace BookAStar.Pages
{
public partial class EstimatesClientPage : ContentPage
{
public EstimatesClientPage()
{
InitializeComponent();
}
}
}

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="BookAStar.Pages.EstimatesProviderPage">
<ScrollView>
<StackLayout Padding="10,10,10,10" x:Name="mainLayout">
<ListView RefreshCommand="{Binding RefreshCommand}" IsPullToRefreshEnabled="True"
ItemsSource="{Binding Estimates}" x:Name="estimates" ItemTapped="OnViewDetail" HasUnevenRows="true" RowHeight="80">
<ListView.ItemTemplate HeightRequest="80" VerticalOptions="StartAndExpand">
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout Orientation="Horizontal" Padding="10,10,10,10" VerticalOptions="StartAndExpand">
<StackLayout Orientation="Vertical"
HeightRequest="80" VerticalOptions="StartAndExpand">
<StackLayout Orientation="Vertical" >
<Image Source="{Binding Client.Avatar}" />
<Label Text="{Binding Client.UserName}"
Style="{StaticResource labelStyle}"></Label>
</StackLayout>
<Label LineBreakMode="WordWrap" Text="{Binding EventDate, StringFormat='{0:dddd d MMMM à HH:mm}'}" FontSize="12" FontFamily="Italic"/>
</StackLayout>
<StackLayout Orientation="Vertical" HorizontalOptions="FillAndExpand">
<Label LineBreakMode="WordWrap" Text="{Binding Location.Address}"/>
<Label Text="{Binding Previsionnal}"/>
<Label Text="{Binding Id}" HorizontalTextAlignment="End"/>
</StackLayout>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ScrollView>
</ContentPage>

View File

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace BookAStar.Pages
{
using Model.Workflow;
using ViewModels.EstimateAndBilling;
public partial class EstimatesProviderPage : ContentPage
{
public EstimatesProviderPage()
{
InitializeComponent();
}
private void OnViewDetail(object sender, ItemTappedEventArgs e)
{
Estimate data = e.Item as Estimate;
App.NavigationService.NavigateTo<EditEstimatePage>(
true,
new EditEstimateViewModel(data));
}
}
}

View File

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="BookAStar.Pages.EstimatePages.ViewEstimatePage"
xmlns:views="clr-namespace:BookAStar.Views;assembly=BookAStar"
xmlns:local="clr-namespace:BookAStar;assembly=BookAStar"
Style="{StaticResource PageStyle}">
<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="Label">
<Setter Property="Style" Value="{StaticResource ContentLabelStyle}" />
</Style>
<Style TargetType="Button">
<Setter Property="Style" Value="{StaticResource ButtonStyle}" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<ScrollView>
<StackLayout Padding="10,10,10,10" x:Name="mainLayout">
<Grid MinimumHeightRequest="12">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Text="{Binding Client.UserName}" ></Label>
<Label Grid.Row="0" Grid.Column="1" Text="{Binding Query.Location.Address}" ></Label>
<Label Grid.Row="0" Grid.Column="2" Text="{Binding Query.EventDate, StringFormat='{0:dddd d MMMM yyyy à hh:mm}'}" ></Label>
</Grid>
<Label Text="{Binding Title}" />
<Label Text="{Binding Description}" />
<views:MarkdownView x:Name="mdview"
HorizontalOptions="FillAndExpand"
Markdown="{Binding Description}"
/>
<ListView x:Name="billListView" ItemsSource="{Binding Bill}"
MinimumHeightRequest="40" HasUnevenRows="true" VerticalOptions="FillAndExpand"
HeightRequest="40" >
<ListView.ItemTemplate>
<DataTemplate >
<ViewCell>
<ViewCell.View>
<Grid MinimumHeightRequest="12">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="30" />
<ColumnDefinition Width="90" />
</Grid.ColumnDefinitions>
<Label Text="{Binding Description}"
Grid.Row="0" Grid.Column="0" ></Label>
<Label Text="{Binding Duration, StringFormat=\{0\}}"
Grid.Row="0" Grid.Column="1" ></Label>
<Label Text="{Binding Count}"
Grid.Row="0" Grid.Column="2" ></Label>
<Label Text="{Binding UnitaryCost}"
Grid.Row="0" Grid.Column="3"
FontFamily="Monospace"></Label>
</Grid>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Label FormattedText="{Binding FormattedTotal}"/>
</StackLayout>
</ScrollView>
</ContentPage.Content>
</ContentPage>

View File

@ -0,0 +1,25 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace BookAStar.Pages.EstimatePages
{
using ViewModels.EstimateAndBilling;
public partial class ViewEstimatePage : ContentPage
{
public ViewEstimatePage(EditEstimateViewModel model)
{
InitializeComponent();
billListView.ItemSelected += (sender, e) => {
((ListView)sender).SelectedItem = null;
};
BindingContext = model;
}
}
}

View File

@ -34,6 +34,7 @@
<Button Text="{Binding PerformerStatus}" Clicked="OnViewPerformerStatus" />
<Button Text="{Binding UserQueries}" Clicked="OnViewUserQueries"
VisualElement.IsVisible="{Binding UserIsPro}"/>
<Button Text="{Binding UserFilesLabel}" Clicked="OnManageFiles" />
<StackLayout Orientation="Horizontal" VisualElement.IsVisible="{Binding HaveAnUser}">
<Label Text="Recevoir les notifications push" StyleClass="Header" />

View File

@ -1,7 +1,56 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="BookAStar.Pages.UserProfile.UserProfilePage">
<Label Text="{Binding UserName}" VerticalOptions="Center" HorizontalOptions="Center" />
x:Class="BookAStar.Pages.UserProfile.UserProfilePage"
xmlns:local="clr-namespace:BookAStar;assembly=BookAStar"
xmlns:views="clr-namespace:BookAStar.Views;assembly=BookAStar"
xmlns:extensions="clr-namespace:BookAStar.Extensions;assembly=BookAStar"
xmlns:controls="clr-namespace:XLabs.Forms.Controls;assembly=XLabs.Forms"
Style="{StaticResource PageStyle}">
<ScrollView>
<ScrollView.Padding>
<OnPlatform x:TypeArguments="Thickness"
Android="20,20,20,20"
WinPhone="20,20,20,20"
iOS="20,40,20,20" />
</ScrollView.Padding>
<StackLayout BoxView.Color="{StaticResource ContentBackgroundColor}" Spacing="10,10,10,10" >
<StackLayout VisualElement.IsVisible="{Binding HaveAnUser}">
<views:ImageButton
x:Name="AvatarButton"
Source="{Binding Avatar}"
IsEnabled="true"
BackgroundColor="#01abdf"
Orientation="ImageToLeft"
Text="{Binding UserName}"
TextColor="#ffffff"
></views:ImageButton>
<StackLayout Orientation="Horizontal">
<Label Text="Recevoir les notifications push" StyleClass="Header" />
<Switch IsToggled="{Binding ReceivePushNotifications, Mode=TwoWay}"
HorizontalOptions="End" />
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Utiliser ma position" StyleClass="Header" />
<Switch HorizontalOptions="End" IsToggled="{Binding AllowUseMyPosition, Mode=TwoWay}"/>
</StackLayout>
<StackLayout VisualElement.IsVisible="{Binding IsAPerformer}">
<StackLayout Orientation="Horizontal" VerticalOptions="Start"
VisualElement.IsVisible="{Binding UserIsPro}" >
<Label Text="Ne recevoir de demande de devis que de la part de professionnels uniquement" />
<Switch HorizontalOptions="End" IsToggled="{Binding AllowProBookingOnly, Mode=TwoWay}"/>
</StackLayout>
<Button Text="{Binding PerformerStatus}" Clicked="OnViewPerformerStatus" />
</StackLayout>
</StackLayout>
</StackLayout>
</ScrollView>
</ContentPage>

View File

@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using BookAStar.ViewModels.UserProfile;
using System;
using Xamarin.Forms;
@ -13,6 +11,28 @@ namespace BookAStar.Pages.UserProfile
public UserProfilePage()
{
InitializeComponent();
AvatarButton.Clicked += AvatarButton_Clicked;
}
private void AvatarButton_Clicked (object sender, EventArgs e)
{
throw new NotImplementedException();
}
public void OnManageFiles(object sender, EventArgs e)
{
ShowPage<UserFiles>(null, true);
}
public void OnViewPerformerStatus(object sender, EventArgs e)
{
ShowPage<AccountChooserPage>(null, true);
}
private void ShowPage<T>(object[] args, bool animate = false) where T : Page
{
App.NavigationService.NavigateTo<T>(animate, args);
App.MasterPresented = false;
}
}
}

View File

@ -61,6 +61,24 @@ namespace BookAStar {
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à Faire un devis.
/// </summary>
public static string DoEstimate {
get {
return ResourceManager.GetString("DoEstimate", resourceCulture);
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à Editer le devis.
/// </summary>
public static string EditEstimate {
get {
return ResourceManager.GetString("EditEstimate", resourceCulture);
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à Star.
/// </summary>
@ -97,15 +115,6 @@ namespace BookAStar {
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à sc.
/// </summary>
public static string String1 {
get {
return ResourceManager.GetString("String1", resourceCulture);
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à Incontournable.
/// </summary>
@ -123,5 +132,14 @@ namespace BookAStar {
return ResourceManager.GetString("TwoStars", resourceCulture);
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à Voir le devis.
/// </summary>
public static string ViewEstimate {
get {
return ResourceManager.GetString("ViewEstimate", resourceCulture);
}
}
}
}

View File

@ -117,6 +117,12 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="DoEstimate" xml:space="preserve">
<value>Faire un devis</value>
</data>
<data name="EditEstimate" xml:space="preserve">
<value>Editer le devis</value>
</data>
<data name="FiveStars" xml:space="preserve">
<value>Star</value>
<comment>Da star, da one</comment>
@ -131,13 +137,13 @@
<data name="OneStar" xml:space="preserve">
<value>Étoile montante</value>
</data>
<data name="String1" xml:space="preserve">
<value>sc</value>
</data>
<data name="ThreeStars" xml:space="preserve">
<value>Incontournable</value>
</data>
<data name="TwoStars" xml:space="preserve">
<value>À ne manquer sous aucun prétexte</value>
</data>
<data name="ViewEstimate" xml:space="preserve">
<value>Voir le devis</value>
</data>
</root>

View File

@ -9,11 +9,15 @@ namespace BookAStar.ViewModels.EstimateAndBilling
using Interfaces;
using Model;
using Model.Social;
using Model.Workflow;
using System.Collections.ObjectModel;
using System.Linq;
class BookQueryViewModel : ViewModel, IBookQueryData
{
public BookQueryViewModel()
{
}
public BookQueryViewModel(BookQueryData data)
{
@ -23,6 +27,10 @@ namespace BookAStar.ViewModels.EstimateAndBilling
EventDate = data.EventDate;
Previsionnal = data.Previsionnal;
Id = data.Id;
estimates = new ObservableCollection<Estimate>(
DataManager.Current.Estimates.Where(
e => e.Query.Id == Id
));
this.data = data;
}
private BookQueryData data;
@ -44,11 +52,26 @@ namespace BookAStar.ViewModels.EstimateAndBilling
return DataManager.Current.EstimationCache.LocalGet(this.Id);
}
}
private ObservableCollection<Estimate> estimates;
public ObservableCollection<Estimate> Estimates {
get {
return estimates;
} }
public bool EstimationDone
{
get
{
return Estimates != null && Estimates.Count>0;
}
}
public string EditEstimateButtonText
{
get
{
return DraftEstimate != null ? "Editer le devis" : "Faire un devis" ;
return DraftEstimate != null ?
Strings.EditEstimate : Strings.DoEstimate;
}
}
}

View File

@ -23,20 +23,20 @@ namespace BookAStar.ViewModels.EstimateAndBilling
/// </summary>
/// <param name="data"></param>
/// <param name="localState"></param>
public EditEstimateViewModel(Estimate data, LocalState localState )
public EditEstimateViewModel(Estimate data)
{
Data = data;
State = localState;
}
private void Bill_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
Data.Bill = new List<BillingLine>( Bill );
NotifyPropertyChanged("FormattedTotal");
NotifyPropertyChanged("Bill");
}
private Estimate data;
public Estimate Data { get { return data; } set {
data = value;
SetProperty<Estimate>(ref data, value);
if (data.AttachedFiles == null) data.AttachedFiles = new List<string>();
if (data.AttachedGraphics == null) data.AttachedGraphics = new List<string>();
if (data.Bill == null) data.Bill = new List<BillingLine>();
@ -44,6 +44,11 @@ namespace BookAStar.ViewModels.EstimateAndBilling
AttachedGraphicList = new ObservableCollection<string>(data.AttachedGraphics);
Bill = new ObservableCollection<BillingLine>(data.Bill);
Bill.CollectionChanged += Bill_CollectionChanged;
Title = Data.Title;
Description = Data.Description;
NotifyPropertyChanged("FormattedTotal");
NotifyPropertyChanged("Query");
NotifyPropertyChanged("CLient");
} }
[JsonIgnore]
@ -65,19 +70,20 @@ namespace BookAStar.ViewModels.EstimateAndBilling
}
string newDesc;
[JsonIgnore]
private string description;
public string Description
{
get
{
return Data.Description;
return description;
}
set
{
SetProperty<string>(ref newDesc, value, "Description");
Data.Description = newDesc;
SetProperty<string>(ref description, value);
Data.Description = description;
}
}
@ -87,7 +93,7 @@ namespace BookAStar.ViewModels.EstimateAndBilling
{
get
{
return Data.Title;
return title;
}
set

View File

@ -7,8 +7,11 @@ using XLabs.Forms.Mvvm;
namespace BookAStar.ViewModels.Signing
{
class EstimateSignaturePad: ViewModel
class DocSigningViewModel: ViewModel
{
/// <summary>
/// The doc to sign, in Markdown format
/// </summary>
public string Document { get; set; }
}
}

View File

@ -0,0 +1,227 @@
using System.Collections.ObjectModel;
using System.Linq;
using Xamarin.Forms;
using XLabs.Forms.Behaviors;
using XLabs.Forms.Controls;
using XLabs.Forms.Mvvm;
using XLabs.Ioc;
using XLabs.Platform.Services;
namespace BookAStar.ViewModels.UserProfile
{
using Data;
using Helpers;
using Model.Auth.Account;
using Pages.UserProfile;
internal class UserProfileViewModel : ViewModel
{
public bool IsAPerformer
{
get
{
return User?.Roles.Contains("Performer") ?? false;
}
}
public string UserFilesLabel
{
get; set;
}
int rating;
public int Rating
{
get
{
return rating;
}
set
{
SetProperty<int>(ref rating, value, "Rating");
}
}
public string UserId
{
get
{
return User?.Id;
}
}
public bool AllowUseMyPosition
{
get
{
return MainSettings.AllowGPSUsage;
}
set
{
MainSettings.AllowGPSUsage = value;
}
}
public bool AllowProBookingOnly
{
get
{
return MainSettings.AllowProBookingOnly;
}
set
{
MainSettings.AllowProBookingOnly = value;
}
}
public bool ReceivePushNotifications
{
get
{
return MainSettings.PushNotifications;
}
set
{
MainSettings.PushNotifications = value;
}
}
private long queryCount;
private User user;
public long QueryCount
{
get
{
return queryCount;
}
}
public User User
{
get { return user; }
protected set
{
SetProperty<User>(ref user, value, "User");
if (user!=null)
{
user.PropertyChanged += User_PropertyChanged;
}
UpdateUserMeta();
}
}
private ImageSource avatar;
public ImageSource Avatar { get {
return avatar;
} }
public ObservableCollection<User> Accounts { get; protected set; }
private string performerStatus;
public string PerformerStatus
{
get
{
return performerStatus;
}
}
string userQueries;
public string UserQueries
{
get
{
return userQueries;
}
}
public string UserName
{
get
{
return User?.UserName;
}
}
private bool userIsPro = false;
public UserProfileViewModel()
{
Accounts = MainSettings.AccountList;
User = MainSettings.CurrentUser;
UpdateUserMeta();
Rating = 2;
UserNameGesture = new RelayGesture((g, x) =>
{
if (g.GestureType == GestureType.LongPress)
{
NavigationService.NavigateTo("accountChooser");
}
});
MainSettings.UserChanged += MainSettings_UserChanged;
}
private void MainSettings_UserChanged(object sender, System.EventArgs e)
{
User = MainSettings.CurrentUser;
UpdateUserMeta();
}
bool haveAnUser;
public bool HaveAnUser
{
get { return User!=null; }
}
public bool UserIsPro
{
get { return User?.Roles?.Contains("Performer") ?? false ; }
}
private void UpdateUserMeta ()
{
string newStatusString;
long newQueryCount;
bool newUserIsPro;
ImageSource newAvatar;
string newQueriesButtonText;
bool newHaveAnUser = user == null;
if (newHaveAnUser) {
newQueryCount = 0;
newUserIsPro = false;
newStatusString = null;
newAvatar = null;
newQueriesButtonText = null;
}
else
{
newUserIsPro = UserIsPro;
newQueryCount = newUserIsPro ? DataManager.Current.BookQueries.Count : 0;
newStatusString = newUserIsPro ?
$"Profile professionel renseigné" :
"Profile professionel non renseigné";
newQueriesButtonText = newUserIsPro ?
$"{newQueryCount} demandes valides en cours" :
"Profile professionel non renseigné";
newAvatar = UserHelpers.Avatar(user.Avatar);
}
SetProperty<bool>(ref haveAnUser, newHaveAnUser, "HaveAnUser");
SetProperty<bool>(ref userIsPro, newUserIsPro, "UserIsPro");
SetProperty<string>(ref performerStatus, newStatusString, "PerformerStatus");
SetProperty<string>(ref userQueries, newQueriesButtonText, "UserQueries");
SetProperty<long>(ref queryCount, newQueryCount, "QueryCount");
SetProperty<ImageSource>(ref avatar, newAvatar, "Avatar");
}
private void User_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
UpdateUserMeta();
}
public RelayGesture UserNameGesture { get; set; }
}
}

View File

@ -0,0 +1,157 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using XLabs.Enums;
namespace BookAStar.Views
{
public class ImageButton : Button
{
/// <summary>
/// Backing field for the Image property.
/// </summary>
public static readonly BindableProperty SourceProperty = BindableProperty.Create(
(Expression<Func<ImageButton, ImageSource>>)(w => w.Source),
null,
BindingMode.OneWay,
null,
(bindable, oldvalue, newvalue) => ((VisualElement)bindable).ToString());
/// <summary>
/// Backing field for the Image property.
/// </summary>
public static readonly BindableProperty DisabledSourceProperty = BindableProperty.Create(
(Expression<Func<ImageButton, ImageSource>>)(w => w.DisabledSource),
null,
BindingMode.OneWay,
null,
(bindable, oldvalue, newvalue) => ((VisualElement)bindable).ToString());
/// <summary>
/// Backing field for the image width property.
/// </summary>
public static readonly BindableProperty ImageWidthRequestProperty =
BindableProperty.Create<ImageButton, int>(
p => p.ImageWidthRequest, default(int));
/// <summary>
/// Backing field for the image height property.
/// </summary>
public static readonly BindableProperty ImageHeightRequestProperty =
BindableProperty.Create<ImageButton, int>(
p => p.ImageHeightRequest, default(int));
/// <summary>
/// Backing field for the orientation property.
/// </summary>
public static readonly BindableProperty OrientationProperty =
BindableProperty.Create<ImageButton, ImageOrientation>(
p => p.Orientation, ImageOrientation.ImageToLeft);
/// <summary>
/// Backing field for the tint color property.
/// </summary>
public static readonly BindableProperty ImageTintColorProperty =
BindableProperty.Create<ImageButton, Color>(
p => p.ImageTintColor, Color.Transparent);
/// <summary>
/// Backing field for the disbaled tint color property.
/// </summary>
public static readonly BindableProperty DisabledImageTintColorProperty =
BindableProperty.Create<ImageButton, Color>(
p => p.DisabledImageTintColor, Color.Transparent);
/// <summary>
/// Gets or sets the ImageSource to use with the control.
/// </summary>
/// <value>
/// The Source property gets/sets the value of the backing field, SourceProperty.
/// </value>
[TypeConverter(typeof(ImageSourceConverter))]
public ImageSource Source
{
get { return (ImageSource)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
/// <summary>
/// Gets or sets the ImageSource to use with the control.
/// </summary>
/// <value>
/// The Source property gets/sets the value of the backing field, SourceProperty.
/// </value>
[TypeConverter(typeof(ImageSourceConverter))]
public ImageSource DisabledSource
{
get { return (ImageSource)GetValue(DisabledSourceProperty); }
set { SetValue(DisabledSourceProperty, value); }
}
/// <summary>
/// Gets or sets The orientation of the image relative to the text.
/// </summary>
/// <value>
/// The Orientation property gets/sets the value of the backing field, OrientationProperty.
/// </value>
public ImageOrientation Orientation
{
get { return (ImageOrientation)GetValue(OrientationProperty); }
set { SetValue(OrientationProperty, value); }
}
/// <summary>
/// Gets or sets the requested height of the image. If less than or equal to zero than a
/// height of 50 will be used.
/// </summary>
/// <value>
/// The ImageHeightRequest property gets/sets the value of the backing field, ImageHeightRequestProperty.
/// </value>
public int ImageHeightRequest
{
get { return (int)GetValue(ImageHeightRequestProperty); }
set { SetValue(ImageHeightRequestProperty, value); }
}
/// <summary>
/// Gets or sets the requested width of the image. If less than or equal to zero than a
/// width of 50 will be used.
/// </summary>
/// <value>
/// The ImageHeightRequest property gets/sets the value of the backing field, ImageHeightRequestProperty.
/// </value>
public int ImageWidthRequest
{
get { return (int)GetValue(ImageWidthRequestProperty); }
set { SetValue(ImageWidthRequestProperty, value); }
}
/// <summary>
/// Gets or sets the tint color of the image
/// </summary>
/// <value>
/// The ImageTintColor property gets/sets the value of the backing field, ImageTintColorProperty.
/// </value>
public Color ImageTintColor
{
get { return (Color)GetValue(ImageTintColorProperty); }
set { SetValue(ImageTintColorProperty, value); }
}
/// <summary>
/// Gets or sets the tint color of the image when the button is disabled
/// </summary>
/// <value>
/// The DisabledImageTintColor property gets/sets the value of the backing field, DisabledImageTintColorProperty.
/// </value>
public Color DisabledImageTintColor
{
get { return (Color)GetValue(DisabledImageTintColorProperty); }
set { SetValue(DisabledImageTintColorProperty, value); }
}
}
}