un positionnement de paramètre workflow des pros

This commit is contained in:
2017-01-13 16:31:40 +01:00
parent 7140a70278
commit bad14bbcd8
322 changed files with 25906 additions and 3857 deletions

View File

@ -9,7 +9,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BookAStar.iOS", "BookAStar\
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BookAStar", "BookAStar\BookAStar\BookAStar.csproj", "{A0815650-0A0A-47B0-8826-771F0E1AD137}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BookAStar", "BookAStar\BookAStar\BookAStar.csproj", "{A0815650-0A0A-47B0-8826-771F0E1AD137}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yavsc.Client", "Yavsc.Client\Yavsc.Client.csproj", "{67F9D3A8-F71E-4428-913F-C37AE82CDB24}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YavscLib", "YavscLib\YavscLib.csproj", "{67F9D3A8-F71E-4428-913F-C37AE82CDB24}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution

View File

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.Accounts;
namespace BookAStar.Droid.Accounts
{
class YavscAccountAuthenticator : AbstractAccountAuthenticator
{
public YavscAccountAuthenticator(Context context): base(context)
{
}
public override Bundle AddAccount(AccountAuthenticatorResponse response, string accountType, string authTokenType, string[] requiredFeatures, Bundle options)
{
throw new NotImplementedException();
}
public override Bundle ConfirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options)
{
throw new NotImplementedException();
}
public override Bundle EditProperties(AccountAuthenticatorResponse response, string accountType)
{
throw new NotImplementedException();
}
public override Bundle GetAuthToken(AccountAuthenticatorResponse response, Account account, string authTokenType, Bundle options)
{
throw new NotImplementedException();
}
public override string GetAuthTokenLabel(string authTokenType)
{
throw new NotImplementedException();
}
public override Bundle HasFeatures(AccountAuthenticatorResponse response, Account account, string[] features)
{
throw new NotImplementedException();
}
public override Bundle UpdateCredentials(AccountAuthenticatorResponse response, Account account, string authTokenType, Bundle options)
{
throw new NotImplementedException();
}
}
}

View File

@ -31,7 +31,7 @@
<DebugType>full</DebugType> <DebugType>full</DebugType>
<Optimize>false</Optimize> <Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath> <OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;DEV</DefineConstants> <DefineConstants>TRACE;DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>0</WarningLevel> <WarningLevel>0</WarningLevel>
<AndroidUseSharedRuntime>True</AndroidUseSharedRuntime> <AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>
@ -328,6 +328,7 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Accounts\YavscAccountAuthenticator.cs" />
<Compile Include="Helpers\Settings.cs" /> <Compile Include="Helpers\Settings.cs" />
<Compile Include="Helpers\SimpleJsonPostMethod.cs" /> <Compile Include="Helpers\SimpleJsonPostMethod.cs" />
<Compile Include="Helpers\YavscHelpers.cs" /> <Compile Include="Helpers\YavscHelpers.cs" />
@ -342,11 +343,14 @@
<Compile Include="Markdown\MarkdownViewModel.cs" /> <Compile Include="Markdown\MarkdownViewModel.cs" />
<Compile Include="Markdown\MarkdownViewRenderer.cs" /> <Compile Include="Markdown\MarkdownViewRenderer.cs" />
<Compile Include="Markdown\MarkdownWebChromeClient.cs" /> <Compile Include="Markdown\MarkdownWebChromeClient.cs" />
<Compile Include="Markdown\MDContextMenu.cs" />
<Compile Include="Markdown\MDWebView.cs" />
<Compile Include="OAuth2\YaOAuth2Authenticator.cs" /> <Compile Include="OAuth2\YaOAuth2Authenticator.cs" />
<Compile Include="Rendering\ImageButtonRenderer.cs" /> <Compile Include="Rendering\ImageButtonRenderer.cs" />
<Compile Include="Resources\Resource.Designer.cs" /> <Compile Include="Resources\Resource.Designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SendFilesActivity.cs" /> <Compile Include="SendFilesActivity.cs" />
<Compile Include="Services\AccountChooserService.cs" />
<Compile Include="Services\YavscChooserTargetService.cs" /> <Compile Include="Services\YavscChooserTargetService.cs" />
<Compile Include="Services\GcmListenerService.cs" /> <Compile Include="Services\GcmListenerService.cs" />
<Compile Include="Services\GcmRegistrationIntentService.cs" /> <Compile Include="Services\GcmRegistrationIntentService.cs" />
@ -372,6 +376,9 @@
<AndroidResource Include="Resources\layout\EditEstimate.axml"> <AndroidResource Include="Resources\layout\EditEstimate.axml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
</AndroidResource> </AndroidResource>
<AndroidResource Include="Resources\menu\md_menu.axml">
<SubType>Designer</SubType>
</AndroidResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<AndroidResource Include="Resources\drawable\icon.png" /> <AndroidResource Include="Resources\drawable\icon.png" />
@ -381,15 +388,17 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<AndroidResource Include="Resources\layout\Tabbar.axml" /> <AndroidResource Include="Resources\layout\Tabbar.axml" />
<AndroidResource Include="Resources\layout\Toolbar.axml" /> <AndroidResource Include="Resources\layout\Toolbar.axml">
<SubType>Designer</SubType>
</AndroidResource>
<AndroidResource Include="Resources\values\styles.xml"> <AndroidResource Include="Resources\values\styles.xml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
</AndroidResource> </AndroidResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Yavsc.Client\Yavsc.Client.csproj"> <ProjectReference Include="..\..\YavscLib\YavscLib.csproj">
<Project>{67f9d3a8-f71e-4428-913f-c37ae82cdb24}</Project> <Project>{67f9d3a8-f71e-4428-913f-c37ae82cdb24}</Project>
<Name>Yavsc.Client</Name> <Name>YavscLib</Name>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\BookAStar\BookAStar.csproj"> <ProjectReference Include="..\BookAStar\BookAStar.csproj">
<Project>{A0815650-0A0A-47B0-8826-771F0E1AD137}</Project> <Project>{A0815650-0A0A-47B0-8826-771F0E1AD137}</Project>
@ -474,7 +483,9 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
</AndroidResource> </AndroidResource>
<AndroidResource Include="Resources\values\dimens.xml" /> <AndroidResource Include="Resources\values\dimens.xml" />
<AndroidResource Include="Resources\values\strings.xml" /> <AndroidResource Include="Resources\values\strings.xml">
<SubType>Designer</SubType>
</AndroidResource>
<AndroidResource Include="Resources\drawable\glyphish_07_map_marker.png" /> <AndroidResource Include="Resources\drawable\glyphish_07_map_marker.png" />
<AndroidResource Include="Resources\drawable\glyphish_13_target.png" /> <AndroidResource Include="Resources\drawable\glyphish_13_target.png" />
<AndroidResource Include="Resources\drawable\glyphish_74_location.png" /> <AndroidResource Include="Resources\drawable\glyphish_74_location.png" />
@ -499,6 +510,14 @@
<ItemGroup> <ItemGroup>
<AndroidResource Include="Resources\drawable\peer_to_peer.png" /> <AndroidResource Include="Resources\drawable\peer_to_peer.png" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\xml\authenticator.xml">
<SubType>Designer</SubType>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\xml\account_preferences.xml" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" /> <Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup> <PropertyGroup>

View File

@ -251,8 +251,8 @@ namespace BookAStar.Droid
{ {
Task.Run(async () => Task.Run(async () =>
{ {
App.ShowBookQuery( var query = DataManager.Instance.BookQueries.LocalGet(queryId);
await DataManager.Current.BookQueries.Get(queryId)); App.ShowBookQuery(query);
}); });
} }
} }
@ -319,15 +319,16 @@ namespace BookAStar.Droid
return manager.FindAccountsForService(Constants.ApplicationName); return manager.FindAccountsForService(Constants.ApplicationName);
}); });
} }
YaOAuth2Authenticator auth = new YaOAuth2Authenticator(
clientId: "d9be5e97-c19d-42e4-b444-0e65863b19e1",
clientSecret: "blouh",
scope: "profile",
authorizeUrl: new Uri("http://dev.pschneider.fr/authorize"),
redirectUrl: new Uri("http://dev.pschneider.fr/oauth/success"),
accessTokenUrl: new Uri("http://dev.pschneider.fr/token"));
public void AddAccount() public void AddAccount()
{ {
var auth = new YaOAuth2Authenticator(
clientId: "d9be5e97-c19d-42e4-b444-0e65863b19e1",
clientSecret: "blouh",
scope: "profile",
authorizeUrl: new Uri(Constants.AuthorizeUrl),
redirectUrl: new Uri(Constants.RedirectUrl),
accessTokenUrl: new Uri(Constants.AccessTokenUrl));
Intent loginIntent = auth.GetUI(this); Intent loginIntent = auth.GetUI(this);
var accStore = AccountStore.Create(this); var accStore = AccountStore.Create(this);
auth.Completed += (sender, eventArgs) => auth.Completed += (sender, eventArgs) =>
@ -348,65 +349,48 @@ namespace BookAStar.Droid
{ {
using (var client = new HttpClient()) using (var client = new HttpClient())
{ {
// get me // get me
// var request = new OAuth2Request("GET", new Uri(Constants.UserInfoUrl), null, eventArgs.Account); using (var request = new HttpRequestMessage(HttpMethod.Get, Constants.UserInfoUrl))
var request = new HttpRequestMessage(HttpMethod.Get, Constants.UserInfoUrl);
request.Headers.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", tokens.AccessToken);
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
string userJson = await response.Content.ReadAsStringAsync();
JObject jactiveUser = JObject.Parse(userJson);
Account acc = eventArgs.Account;
var uid = jactiveUser["Id"].Value<string>();
var username = jactiveUser["UserName"].Value<string>();
var roles = jactiveUser["Roles"].Values<string>().ToList();
var emails = jactiveUser["EMails"].Values<string>().ToList();
var newuser = new User
{ {
UserName = username, request.Headers.Authorization =
EMails = emails, new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", tokens.AccessToken);
Roles = roles,
Id = uid,
YavscTokens = tokens
};
MainSettings.SaveUser(newuser); using (var response = await client.SendAsync(request))
accStore.Save(acc, Constants.ApplicationName); {
response.EnsureSuccessStatusCode();
string userJson = await response.Content.ReadAsStringAsync();
JObject jactiveUser = JObject.Parse(userJson);
Account acc = eventArgs.Account;
var uid = jactiveUser["Id"].Value<string>();
var username = jactiveUser["UserName"].Value<string>();
var roles = jactiveUser["Roles"].Values<string>().ToList();
var emails = jactiveUser["EMails"].Values<string>().ToList();
var avatar = jactiveUser["Avatar"].Value<string>();
var address = jactiveUser["Avatar"].Value<string>();
var newuser = new User
{
UserName = username,
EMails = emails,
Roles = roles,
Id = uid,
YavscTokens = tokens,
Avatar = avatar,
Address = address
};
MainSettings.SaveUser(newuser);
accStore.Save(acc, Constants.ApplicationName);
}
}
} }
} });
);
} }
}; };
auth.Error += Auth_Error; auth.Error += Auth_Error;
StartActivity(loginIntent); StartActivity(loginIntent);
} }
public static void PopulateUserWithAccount(User u, Account a)
{
u.YavscTokens = new Model.Auth.Account.Tokens
{
AccessToken =
a.Properties["access_token"],
RefreshToken =
a.Properties["refresh_token"],
ExpiresIn =
int.Parse(a.Properties["expires_in"]),
TokenType = a.Properties["token_type"]
};
u.UserName = a.Username;
}
private void Auth_Error(object sender, AuthenticatorErrorEventArgs e) private void Auth_Error(object sender, AuthenticatorErrorEventArgs e)
{ {
// TODO handle // TODO handle

View File

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.Support.V7.App;
using XLabs.Ioc;
using XLabs.Platform.Mvvm;
using XLabs.Forms;
using static Android.Views.View;
namespace BookAStar.Droid.Markdown
{
class MDContextMenu : AppCompatDialog
{
private ActionMode mActionMode = null;
public MDContextMenu(Context context) : base(context)
{
}
public override void OnActionModeStarted(ActionMode mode)
{
if (mActionMode == null)
{
mActionMode = mode;
var menu = mode.Menu;
// Remove the default menu items (select all, copy, paste, search)
menu.Clear();
// If you want to keep any of the defaults,
// remove the items you don't want individually:
// menu.removeItem(android.R.id.[id_of_item_to_remove])
// Inflate your own menu items
mode.MenuInflater.Inflate(Resource.Menu.md_menu, menu);
}
mActionMode = mode;
base.OnActionModeStarted(mode);
}
public override void OnActionModeFinished(ActionMode mode)
{
base.OnActionModeFinished(mode);
}
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
}
public void OnCreateContextMenu(IContextMenu menu, View v, IContextMenuContextMenuInfo menuInfo)
{
/* if (menuInfo!=null)
{
var info = menuInfo.ToString();
}
menu.Add(0, 0, 0, "test");
var subMenu = menu.AddSubMenu(
0, 1, 1, "...");
subMenu.Add(0, 3, 0, "nkjnkjn");
var app = Resolver.Resolve<IXFormsApp>() as IXFormsApp<XFormsCompatApplicationDroid>;
var mgr = ClipboardManager.FromContext(app.AppContext);
if (mgr.HasText)
menu.Add(0, 0, 0, "Coller!");*/
//base.OnCreateContextMenu(menu, v, menuInfo);
}
}
}

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.Webkit;
namespace BookAStar.Droid.Markdown
{
class MDWebView : WebView
{
public MDWebView (Context context) : base (context)
{
}
public override ActionMode StartActionMode(ActionMode.ICallback callback)
{
return base.StartActionMode(callback);
}
}
}

View File

@ -17,7 +17,7 @@ using System.Linq;
using System.Text; using System.Text;
[System.CodeDom.Compiler.GeneratedCodeAttribute("RazorTemplatePreprocessor", "4.2.0.703")] [System.CodeDom.Compiler.GeneratedCodeAttribute("RazorTemplatePreprocessor", "4.2.1.62")]
public partial class MarkdownEditor : MarkdownEditorBase public partial class MarkdownEditor : MarkdownEditorBase
{ {

View File

@ -9,6 +9,7 @@ using Android.OS;
using Android.Runtime; using Android.Runtime;
using Android.Views; using Android.Views;
using Android.Widget; using Android.Widget;
using Android.Graphics;
namespace BookAStar.Droid.Markdown namespace BookAStar.Droid.Markdown
{ {
@ -17,6 +18,7 @@ namespace BookAStar.Droid.Markdown
protected static MarkdownDeep.Markdown markdown = new MarkdownDeep.Markdown(); protected static MarkdownDeep.Markdown markdown = new MarkdownDeep.Markdown();
public string Content { get; set; } public string Content { get; set; }
public bool Editable { get; set; } public bool Editable { get; set; }
public string GetHtml() public string GetHtml()
{ {
return markdown.Transform(Content); return markdown.Transform(Content);

View File

@ -12,6 +12,11 @@ using Android.Views;
namespace BookAStar.Droid namespace BookAStar.Droid
{ {
using Markdown; using Markdown;
using XLabs.Forms;
using XLabs.Ioc;
using XLabs.Platform.Mvvm;
using static View;
public class MarkdownViewRenderer : ViewRenderer<MarkdownView, WebView> public class MarkdownViewRenderer : ViewRenderer<MarkdownView, WebView>
{ {
private WebView editorView; private WebView editorView;
@ -35,7 +40,9 @@ namespace BookAStar.Droid
{ {
if (view == null || xview == null) return; if (view == null || xview == null) return;
var vch = view.ContentHeight; var vch = view.ContentHeight;
xview.HeightRequest = vch > xview.MinimumHeightRequest ? vch : xview.MinimumHeightRequest; // var oldH = xview.Height;
var newH = vch > xview.MinimumHeightRequest ? vch : xview.MinimumHeightRequest;
xview.HeightRequest = newH;
} }
protected override void OnElementChanged(ElementChangedEventArgs<MarkdownView> e) protected override void OnElementChanged(ElementChangedEventArgs<MarkdownView> e)
@ -55,10 +62,14 @@ namespace BookAStar.Droid
{ {
// Subscribe // Subscribe
editorTemplate.Model = new Markdown.MarkdownViewModel editorTemplate.Model = new Markdown.MarkdownViewModel
{ Content = e.NewElement.Markdown, Editable = e.NewElement.Editable }; {
Content = e.NewElement.Markdown, Editable = e.NewElement.Editable
};
var html = editorTemplate.GenerateString(); var html = editorTemplate.GenerateString();
EditorView.LoadDataWithBaseURL("file:///android_asset/", EditorView.LoadDataWithBaseURL("file:///android_asset/",
html, "text/html", "utf-8", null); html, "text/html", "utf-8", null);
EditorView.SetBackgroundColor(e.NewElement.BackgroundColor.ToAndroid());
} }
} }
@ -69,7 +80,7 @@ namespace BookAStar.Droid
Control.LoadUrl(string.Format("javascript: {0}", script)); Control.LoadUrl(string.Format("javascript: {0}", script));
} }
} }
MDContextMenu contextMenu;
private WebView CreateNativeControl() private WebView CreateNativeControl()
{ {
editorView = new WebView(Context); editorView = new WebView(Context);
@ -89,9 +100,18 @@ namespace BookAStar.Droid
EditorView.Settings.DomStorageEnabled = true; EditorView.Settings.DomStorageEnabled = true;
EditorView.AddJavascriptInterface(new JsBridgeMarkdown(this), "jsBridge"); EditorView.AddJavascriptInterface(new JsBridgeMarkdown(this), "jsBridge");
EditorView.ViewTreeObserver.PreDraw += ViewTreeObserver_PreDraw; EditorView.ViewTreeObserver.PreDraw += ViewTreeObserver_PreDraw;
//var app = Resolver.Resolve<IXFormsApp>() as IXFormsApp<XFormsCompatApplicationDroid>;
//contextMenu = new MDContextMenu(app.AppContext);
//EditorView.SetOnCreateContextMenuListener(contextMenu);
return EditorView; return EditorView;
} }
private void EditorView_Touch(object sender, TouchEventArgs e)
{
}
private void ViewTreeObserver_PreDraw(object sender, ViewTreeObserver.PreDrawEventArgs e) private void ViewTreeObserver_PreDraw(object sender, ViewTreeObserver.PreDrawEventArgs e)
{ {
AdjustHeightRequest(Element, Control); AdjustHeightRequest(Element, Control);

View File

@ -368,7 +368,7 @@ public class YaOAuth2Authenticator : WebRedirectAuthenticator
var query = queryValues.FormEncode(); var query = queryValues.FormEncode();
var req = WebRequest.Create(accessTokenUrl); var req = WebRequest.Create(accessTokenUrl);
// (req as HttpWebRequest).Accept = "application/json"; (req as HttpWebRequest).Accept = "application/json";
req.Method = "POST"; req.Method = "POST";
var body = Encoding.UTF8.GetBytes(query); var body = Encoding.UTF8.GetBytes(query);
req.ContentLength = body.Length; req.ContentLength = body.Length;

View File

@ -30,6 +30,12 @@
<action android:name="android.service.chooser.ChooserTargetService" /> <action android:name="android.service.chooser.ChooserTargetService" />
</intent-filter> </intent-filter>
</service> </service>
<service android:name="fr.pschneider.bas.AccountChooserService">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data android:name="android.accounts.AccountAuthenticator" android:resource="@xml/authenticator" />
</service>
</application> </application>
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAVE_LOCK" /> <uses-permission android:name="android.permission.WAVE_LOCK" />

View File

@ -200,7 +200,7 @@ namespace BookAStar.Rendering
var uriImageLoader = imagesource as UriImageSource; var uriImageLoader = imagesource as UriImageSource;
if (uriImageLoader != null && uriImageLoader.Uri != null) if (uriImageLoader != null && uriImageLoader.Uri != null)
{ {
using (var client = UserHelpers.CreateClient()) using (var client = UserHelpers.CreateJsonClient())
{ {
using (var response = await client.GetAsync(uriImageLoader.Uri)) using (var response = await client.GetAsync(uriImageLoader.Uri))
{ {

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?attr/colorPrimary" android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
android:popupTheme="@style/ThemeOverlay.AppCompat.Light" android:popupTheme="@style/ThemeOverlay.AppCompat.Dark"
tools:context=".MainActivity" tools:context=".MainActivity"
app:layout_scrollFlags="scroll|enterAlways" app:layout_scrollFlags="scroll|enterAlways"
/> />

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/copy"
android:title="@string/copy"/>
<group android:id="@+id/group">
<item android:id="@+id/past"
android:title="@string/past"
android:showAsAction="ifRoom|withText"/>
</group>
<item android:id="@+id/submenu_character"
android:title="@string/character" >
<menu>
<item android:id="@+id/bold"
android:title="@string/bold" />
<item android:id="@+id/italic"
android:title="@string/italic" />
<item android:id="@+id/underline"
android:title="@string/underline" />
</menu>
</item>
<item android:id="@+id/submenu_paragraph"
android:title="@string/paragraph" >
<menu>
<item android:id="@+id/header1"
android:onClick="onGroupItemClick"
android:title="@string/header1" />
<item android:id="@+id/header2"
android:onClick="onGroupItemClick"
android:title="@string/header2" />
</menu>
</item>
</menu>

View File

@ -14,5 +14,25 @@
<string name="url">url</string> <string name="url">url</string>
<string name="url_hint">url_hint</string> <string name="url_hint">url_hint</string>
<string name="picture">picture</string> <string name="picture">picture</string>
<string name="google_app_id">325408689282</string> <string name="google_app_id">325408689282</string>
<string name="pref_screen_title">Comptes Booking Star</string>
<string name="account_authenticator_label">Comptes Booking Star</string>
<string name="copy">Copier</string>
<string name="past">Coller</string>
<string name="character">Caractère</string>
<string name="bold">Gras</string>
<string name="italic">Italique</string>
<string name="underline">Sousligné</string>
<string name="paragraph">Paragraphe</string>
<string name="header1">Titre</string>
<string name="header2">Sous-titre</string>
<string name="accounts">Comptes</string>
<string name="bookingstar_accounts_pref_screen_summary">
Préférences des comptes Booking Star
</string>
</resources> </resources>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8" ?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="@string/accounts" />
<PreferenceScreen
android:key="bookingstar_accounts_pref_screen"
android:title="@string/pref_screen_title"
android:summary="@string/bookingstar_accounts_pref_screen_summary">
<intent
android:action="bookingstar_accounts_pref_screen.ACTION"
android:targetPackage="bookingstar_accounts_pref_screen.package"
android:targetClass="bookingstar_accounts_pref_screen.class" />
</PreferenceScreen>
</PreferenceScreen>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8" ?>
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="typeOfAuthenticator"
android:icon="@drawable/icon"
android:smallIcon="@drawable/icon"
android:label="@string/account_authenticator_label"
android:accountPreferences="@xml/account_preferences"
/>

View File

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using BookAStar.Droid.OAuth;
namespace BookAStar.Droid.Services
{
[Service(
Name = "fr.pschneider.bas.AccountChooserService",
Label = "Yavsc accounts service",
Icon = "@drawable/icon",
Exported = true,
Enabled = true
)]
[IntentFilter(new String[] { "android.accounts.AccountAuthenticator" })]
class AccountChooserService : Service
{
public static YaOAuth2Authenticator authenticator;
public override void OnCreate()
{
base.OnCreate();
}
public override IBinder OnBind(Intent intent)
{
throw new NotImplementedException();
}
}
}

View File

@ -9,6 +9,9 @@ namespace BookAStar.Droid.Services
using Model.Social; using Model.Social;
using Newtonsoft.Json; using Newtonsoft.Json;
using Model; using Model;
using Model.Social;
using Data;
using System.Linq;
namespace ClientApp namespace ClientApp
{ {
@ -44,18 +47,27 @@ namespace BookAStar.Droid.Services
if (topic == "BookQuery") if (topic == "BookQuery")
{ {
DateTime eventdate; DateTime eventdate;
var sdatestr = data.GetString("EventDate"); var sdatestr = data.GetString("EventDate");
DateTime.TryParse(sdatestr, out eventdate); DateTime.TryParse(sdatestr, out eventdate);
var locationJson = data.GetString("Location"); var locationJson = data.GetString("Location");
var location = JsonConvert.DeserializeObject<Location>(locationJson); var location = JsonConvert.DeserializeObject<Location>(locationJson);
var cid = long.Parse(data.GetString("Id")); var cid = long.Parse(data.GetString("Id"));
var bq = new BookQueryData var clientJson = data.GetString("Client");
var client = JsonConvert.DeserializeObject<ClientProviderInfo>(clientJson);
var bq = new BookQuery
{ {
Id = cid, Id = cid,
Location = location Location = location,
Client = client,
Reason = data.GetString("Reason")
}; };
var dateString = data.GetString("EventDate");
DateTime evDate;
if (DateTime.TryParse(dateString, out evDate))
{
bq.EventDate = evDate;
}
SendBookQueryNotification(bq); SendBookQueryNotification(bq);
} }
@ -82,14 +94,17 @@ namespace BookAStar.Droid.Services
notificationManager.Notify(0, notificationBuilder.Build()); notificationManager.Notify(0, notificationBuilder.Build());
} }
void SendBookQueryNotification(BookQueryData bquery) void SendBookQueryNotification(BookQuery bquery)
{ {
var bookquerynotifications = MainSettings.AddBookQueryNotification(bquery); DataManager.Instance.BookQueries.Merge(bquery);
var bookquerynotifications = DataManager.Instance.BookQueries.Where(
q=> ! q.Read && q.EventDate > DateTime.Now
).ToArray();
var count = bookquerynotifications.Length; var count = bookquerynotifications.Length;
var multiple = count > 1; var multiple = count > 1;
var title = var title =
multiple ? $"{count} demandes" : bquery.Client.UserName; multiple ? $"{count} demandes" : bquery.Client.UserName;
var message = $"{bquery.EventDate} {bquery.Client.UserName} {bquery.Location.Address}"; var message = $"{bquery.EventDate} {bquery.Client.UserName} {bquery.Location.Address}\n {bquery.Reason}";
var intent = new Intent(this, typeof(MainActivity)); var intent = new Intent(this, typeof(MainActivity));
intent.AddFlags(ActivityFlags.ClearTop); intent.AddFlags(ActivityFlags.ClearTop);

View File

@ -7,7 +7,6 @@ using Android.Widget;
namespace BookAStar.Droid namespace BookAStar.Droid
{ {
[Service] [Service]
public class MyGcmIntentService : IntentService public class MyGcmIntentService : IntentService
{ {
@ -31,6 +30,7 @@ namespace BookAStar.Droid
intent.SetClass(context, typeof(MyGcmIntentService)); intent.SetClass(context, typeof(MyGcmIntentService));
context.StartService(intent); context.StartService(intent);
} }
static object locker = new object(); static object locker = new object();
protected override void OnHandleIntent(Intent intent) protected override void OnHandleIntent(Intent intent)
@ -74,16 +74,28 @@ namespace BookAStar.Droid
void SendNotification (string message) void SendNotification (string message)
{ {
var intent = new Intent (this, typeof(MainActivity)); /* Bundle valuesForActivity = new Bundle();
valuesForActivity.PutInt("count", count); */
var intent = new Intent (this, typeof(MainActivity));
intent.AddFlags (ActivityFlags.ClearTop); intent.AddFlags (ActivityFlags.ClearTop);
var pendingIntent = PendingIntent.GetActivity (this, 0, intent, PendingIntentFlags.OneShot); var pendingIntent = PendingIntent.GetActivity (this, 0, intent, PendingIntentFlags.OneShot);
// Construct a back stack for cross-task navigation:
TaskStackBuilder stackBuilder = TaskStackBuilder.Create(this);
stackBuilder.AddParentStack(Java.Lang.Class.FromType(typeof(MainActivity)));
stackBuilder.AddNextIntent(intent);
var notificationBuilder = new Notification.Builder(this) // Create the PendingIntent with the back stack:
PendingIntent resultPendingIntent =
stackBuilder.GetPendingIntent(0, PendingIntentFlags.UpdateCurrent);
var notificationBuilder = new Notification.Builder(this)
.SetAutoCancel(true)
.SetSmallIcon (Resource.Drawable.icon) .SetSmallIcon (Resource.Drawable.icon)
.SetContentTitle ("GCM Message") .SetContentTitle ("GCM Message")
.SetContentText (message) .SetContentText (message)
.SetAutoCancel (true) .SetContentIntent(resultPendingIntent) // Start 2nd activity when the intent is clicked.
.SetContentIntent (pendingIntent); ;
var notificationManager = (NotificationManager) GetSystemService(Context.NotificationService); var notificationManager = (NotificationManager) GetSystemService(Context.NotificationService);
notificationManager.Notify (0, notificationBuilder.Build()); notificationManager.Notify (0, notificationBuilder.Build());

View File

@ -171,10 +171,6 @@
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Yavsc.Client\Yavsc.Client.csproj">
<Project>{67F9D3A8-F71E-4428-913F-C37AE82CDB24}</Project>
<Name>Yavsc.Client</Name>
</ProjectReference>
<ProjectReference Include="..\BookAStar\BookAStar.csproj"> <ProjectReference Include="..\BookAStar\BookAStar.csproj">
<Project>{A0815650-0A0A-47B0-8826-771F0E1AD137}</Project> <Project>{A0815650-0A0A-47B0-8826-771F0E1AD137}</Project>
<Name>BookAStar</Name> <Name>BookAStar</Name>

View File

@ -9,23 +9,31 @@
<ResourceDictionary> <ResourceDictionary>
<Color x:Key="PageBackgroundColor">#FFAAAAFF</Color> <Color x:Key="PageBackgroundColor">#FFAAAAFF</Color>
<Color x:Key="ContentBackgroundColor">#80AFAFAF</Color> <Color x:Key="DashboardPageBackgroundColor">#FFCCCCFF</Color>
<Color x:Key="ContentBackgroundColor">#80FFFFFF</Color>
<Color x:Key="BackgroundColor">#FFFFFFFF</Color> <Color x:Key="BackgroundColor">#FFFFFFFF</Color>
<Color x:Key="LabelBackgroundColor">#FFFFFFFF</Color> <Color x:Key="LabelBackgroundColor">#FFFFFFFF</Color>
<Color x:Key="PageForegroundColor">#000000</Color> <Color x:Key="PageForegroundColor">#000000</Color>
<Color x:Key="TextColor">#000000</Color> <Color x:Key="TextColor">#000000</Color>
<Color x:Key="LabelColor">#000000</Color> <Color x:Key="LabelColor">#000000</Color>
<Color x:Key="ErrorTextColor">#500000</Color>
<Color x:Key="HeadingTextColor">Black</Color> <Color x:Key="HeadingTextColor">Black</Color>
<Color x:Key="NormalTextColor">Blue</Color> <Color x:Key="NormalTextColor">Blue</Color>
<Color x:Key="GroupingTextColor">#5050ff</Color> <Color x:Key="GroupingTextColor">#5050ff</Color>
<Color x:Key="OddColor">#207AFAFA</Color> <Color x:Key="OddColor">#207AFAFA</Color>
<Color x:Key="EmphasisTextColor">#800080</Color>
<Color x:Key="QuietTextColor">#404040</Color>
<Color x:Key="DisconnectedUserBgColor">#909090</Color>
<Color x:Key="ConnectedUserBgColor">#ffffff</Color>
<OnPlatform x:TypeArguments="Font" Android="Large" iOS="Large" WinPhone="Large" x:Key="HeaderFont" /> <OnPlatform x:TypeArguments="Font" Android="Large" iOS="Large" WinPhone="Large" x:Key="HeaderFont" />
<OnPlatform x:TypeArguments="Color" Android="Red" iOS="Red" WinPhone="Red" x:Key="EmphasisTextColor" /> <OnPlatform x:TypeArguments="x:Double" Android="22" iOS="22" WinPhone="22" x:Key="LargeFontSize" />
<OnPlatform x:TypeArguments="Font" Android="90" iOS="90" WinPhone="90" x:Key="LargeFontSize" /> <OnPlatform x:TypeArguments="x:Double" Android="16" iOS="16" WinPhone="16" x:Key="MediumFontSize" />
<OnPlatform x:TypeArguments="Font" Android="50" iOS="50" WinPhone="50" x:Key="MediumFontSize" /> <OnPlatform x:TypeArguments="x:Double" Android="14" iOS="14" WinPhone="14" x:Key="SmallFontSize" />
<OnPlatform x:TypeArguments="Font" Android="40" iOS="40" WinPhone="40" x:Key="SmallFontSize" />
<OnPlatform x:TypeArguments="x:Double" Android="130" iOS="130" WinPhone="130" x:Key="BigUserAvatarSize" /> <OnPlatform x:TypeArguments="x:Double" Android="130" iOS="130" WinPhone="130" x:Key="BigUserAvatarSize" />
<Style x:Key="LabelPageHeadingStyle" TargetType="Label"> <Style x:Key="LabelPageHeadingStyle" TargetType="Label">
@ -43,12 +51,19 @@
<Setter Property="VerticalOptions" Value="Start" /> <Setter Property="VerticalOptions" Value="Start" />
</Style> </Style>
<Style x:Key="ErrorLabelStyle" BasedOn="{StaticResource InputLabelStyle}" TargetType="Label">
<Setter Property="FontSize" Value="Large" />
<Setter Property="FontAttributes" Value="Bold" />
<Setter Property="TextColor" Value="{StaticResource ErrorTextColor}" />
</Style>
<Style x:Key="LabelStyle" TargetType="Label"> <Style x:Key="LabelStyle" TargetType="Label">
<Setter Property="TextColor" Value="{StaticResource LabelColor}" /> <Setter Property="TextColor" Value="{StaticResource LabelColor}" />
<Setter Property="LineBreakMode" Value="WordWrap" /> <Setter Property="LineBreakMode" Value="WordWrap" />
<Setter Property="HorizontalOptions" Value="FillAndExpand" />
</Style> </Style>
<Style x:Key="BigLabel" BasedOn="{StaticResource LabelStyle}" TargetType="Label"> <Style x:Key="BigLabelStyle" BasedOn="{StaticResource LabelStyle}" TargetType="Label">
<Setter Property="FontSize" Value="Large" /> <Setter Property="FontSize" Value="Large" />
</Style> </Style>
<Style x:Key="ContentLabelStyle" BasedOn="{StaticResource LabelStyle}" TargetType="Label"> <Style x:Key="ContentLabelStyle" BasedOn="{StaticResource LabelStyle}" TargetType="Label">
@ -80,10 +95,13 @@
</Style> </Style>
<Style x:Key="PageStyle" TargetType="ContentPage"> <Style x:Key="PageStyle" TargetType="ContentPage">
<Setter Property="Padding" Value="5,5,5,5" /> <Setter Property="Padding" Value="15,15,15,15" />
<Setter Property="BackgroundColor" Value="{StaticResource PageBackgroundColor}" /> <Setter Property="BackgroundColor" Value="{StaticResource PageBackgroundColor}" />
</Style> </Style>
<Style x:Key="DashboardPageStyle" TargetType="ContentPage">
<Setter Property="Padding" Value="10,10,10,10" />
<Setter Property="BackgroundColor" Value="{StaticResource DashboardPageBackgroundColor}" />
</Style>
</ResourceDictionary> </ResourceDictionary>
</Application.Resources> </Application.Resources>

View File

@ -19,7 +19,6 @@ namespace BookAStar
using Data; using Data;
using Interfaces; using Interfaces;
using Model; using Model;
using Model.UI;
using Pages; using Pages;
using Plugin.Connectivity; using Plugin.Connectivity;
using Model.Social.Messaging; using Model.Social.Messaging;
@ -27,6 +26,11 @@ namespace BookAStar
using ViewModels.UserProfile; using ViewModels.UserProfile;
using Pages.UserProfile; using Pages.UserProfile;
using ViewModels.EstimateAndBilling; using ViewModels.EstimateAndBilling;
using Pages.EstimatePages;
using ViewModels;
using Pages.Chat;
using System.Collections.Generic;
using Model.Social;
public partial class App : Application // superclass new in 1.3 public partial class App : Application // superclass new in 1.3
{ {
@ -36,12 +40,13 @@ namespace BookAStar
[Obsolete("Instead using this, use new static properties.")] [Obsolete("Instead using this, use new static properties.")]
public static App CurrentApp { get { return Current as App; } } public static App CurrentApp { get { return Current as App; } }
private static ExtendedMasterDetailPage masterDetail;
public static bool MasterPresented public static bool MasterPresented
{ {
get get
{ return CurrentApp.masterDetail.IsPresented; } { return App.masterDetail.IsPresented; }
internal set internal set
{ CurrentApp.masterDetail.IsPresented = value; } { masterDetail.IsPresented = value; }
} }
public void Init() public void Init()
@ -63,9 +68,10 @@ namespace BookAStar
MainSettings.UserChanged += MainSettings_UserChanged; MainSettings.UserChanged += MainSettings_UserChanged;
CrossConnectivity.Current.ConnectivityChanged += (conSender, args) => CrossConnectivity.Current.ConnectivityChanged += (conSender, args) =>
{ App.IsConnected = args.IsConnected; }; { App.IsConnected = args.IsConnected; };
SetupHubConnection();
MainSettings_UserChanged(this, null); MainSettings_UserChanged(this, null);
if (CrossConnectivity.Current.IsConnected)
StartHubConnection(); StartConnexion();
} }
// omg // omg
@ -77,7 +83,6 @@ namespace BookAStar
// Called on rotation after OnSuspended // Called on rotation after OnSuspended
private void OnClosing(object sender, EventArgs e) private void OnClosing(object sender, EventArgs e)
{ {
} }
// FIXME Never called. // FIXME Never called.
@ -92,17 +97,22 @@ namespace BookAStar
// TODO special startup pages as // TODO special startup pages as
// notification details or wizard setup page // notification details or wizard setup page
} }
private static INavigation Navigation
{
get
{
return masterDetail.Detail.Navigation;
}
}
// Called on rotation // Called on rotation
private void OnSuspended(object sender, EventArgs e) private void OnSuspended(object sender, EventArgs e)
{ {
// TODO save the navigation stack StopConnection();
int position = 0; int position = 0;
DataManager.Instance.AppState.Clear();
foreach (Page page in MainPage.Navigation.NavigationStack) foreach (Page page in Navigation.NavigationStack)
{ {
DataManager.Instance.AppState.Add(
DataManager.Current.AppState.Add(
new PageState new PageState
{ {
Position = position++, Position = position++,
@ -110,22 +120,29 @@ namespace BookAStar
BindingContext = page.BindingContext BindingContext = page.BindingContext
}); });
} }
DataManager.Current.AppState.SaveEntity(); DataManager.Instance.AppState.SaveEntity();
} }
// called on app startup, after OnStartup, not on rotation // called on app startup, after OnStartup, not on rotation
private void OnAppResumed(object sender, EventArgs e) private void OnAppResumed(object sender, EventArgs e)
{ {
StartConnexion();
// TODO restore the navigation stack // TODO restore the navigation stack
base.OnResume(); base.OnResume();
foreach (var pageState in DataManager.Current.AppState) foreach (var pageState in DataManager.Instance.AppState)
{ {
var pageType = Type.GetType(pageState.PageType); if (pageState.PageType != null)
NavigationService.NavigateTo( {
pageType, true, pageState.BindingContext); var pageType = Type.GetType(pageState.PageType);
if (pageState.BindingContext != null)
NavigationService.NavigateTo(
pageType, false, pageState.BindingContext);
else NavigationService.NavigateTo(
pageType, false);
}
} }
DataManager.Current.AppState.Clear(); DataManager.Instance.AppState.Clear();
DataManager.Current.AppState.SaveEntity(); DataManager.Instance.AppState.SaveEntity();
} }
// FIXME Not called? // FIXME Not called?
@ -139,9 +156,6 @@ namespace BookAStar
private void Configure(IXFormsApp app) private void Configure(IXFormsApp app)
{ {
ViewFactory.EnableCache = true; ViewFactory.EnableCache = true;
ViewFactory.Register<ChatPage, ChatViewModel>(
r=> new ChatViewModel { ChatUser = MainSettings.UserName }
);
ViewFactory.Register<DashboardPage, DashboardViewModel>( ViewFactory.Register<DashboardPage, DashboardViewModel>(
resolver => new DashboardViewModel()); resolver => new DashboardViewModel());
ViewFactory.Register<BookQueryPage, BookQueryViewModel>(); ViewFactory.Register<BookQueryPage, BookQueryViewModel>();
@ -150,12 +164,11 @@ namespace BookAStar
ViewFactory.Register<EditEstimatePage, EditEstimateViewModel>(); ViewFactory.Register<EditEstimatePage, EditEstimateViewModel>();
ViewFactory.Register<UserFiles, DirectoryInfoViewModel>(); ViewFactory.Register<UserFiles, DirectoryInfoViewModel>();
ViewFactory.Register<UserProfilePage, UserProfileViewModel>(); ViewFactory.Register<UserProfilePage, UserProfileViewModel>();
ViewFactory.Register<EstimateSigningPage, EditEstimateViewModel>();
ConfigManager = new GenericConfigSettingsMgr(s => ConfigManager = new GenericConfigSettingsMgr(s =>
MainSettings.AppSettings.GetValueOrDefault<string>(s, MainSettings.SettingsDefault), null); MainSettings.AppSettings.GetValueOrDefault<string>(s, MainSettings.SettingsDefault), null);
} }
ExtendedMasterDetailPage masterDetail;
public App(IPlatform instance) public App(IPlatform instance)
{ {
// This declaration became obsolete by introduction // This declaration became obsolete by introduction
@ -176,33 +189,58 @@ namespace BookAStar
BookQueriesPage bQueriesPage; BookQueriesPage bQueriesPage;
AccountChooserPage accChooserPage; AccountChooserPage accChooserPage;
HomePage homePage; HomePage homePage;
UserProfilePage userProfilePage;
private static UserProfilePage userProfilePage;
public static UserProfilePage UserProfilePage
{ get { return userProfilePage; } }
ChatPage chatPage; ChatPage chatPage;
private void ShowPage(Page page) public static void ShowPage(Page page)
{ {
if (masterDetail.Detail.Navigation.NavigationStack.Contains(page)) if (Navigation.NavigationStack.Contains(page))
{ {
if (masterDetail.Detail.Navigation.NavigationStack.Last() == page) return; if (Navigation.NavigationStack.Last() == page) return;
masterDetail.Detail.Navigation.RemovePage(page); Navigation.RemovePage(page);
page.Parent = null; page.Parent = null;
} }
masterDetail.Detail.Navigation.PushAsync(page); Navigation.PushAsync(page);
} }
private void BuildMainPage() private void BuildMainPage()
{ {
// TODO
// in case of App resume,
// do not create new BindingContext's,
// but use those from the AppState property
accChooserPage = new AccountChooserPage(); accChooserPage = new AccountChooserPage();
var bookQueries = new BookQueriesViewModel();
var userprofile = new UserProfileViewModel();
bQueriesPage = new BookQueriesPage bQueriesPage = new BookQueriesPage
{ {
Title = "Demandes", Title = "Demandes",
Icon = "icon.png", Icon = "icon.png",
BindingContext = new BookQueriesViewModel() BindingContext = bookQueries
}; };
homePage = new HomePage() { Title = "Accueil", Icon = "icon.png" };
userProfilePage = new UserProfilePage { Title = "Profile utilisateur", Icon = "ic_corp_icon.png", homePage = new HomePage() {
BindingContext = new UserProfileViewModel() }; Title = "Accueil",
Icon = "icon.png" };
homePage.BindingContext = new HomeViewModel {
BookQueries = bookQueries,
UserProfile = userprofile };
userProfilePage = new UserProfilePage {
Title = "Profile utilisateur",
Icon = "ic_corp_icon.png",
BindingContext = userprofile
};
chatPage = new ChatPage chatPage = new ChatPage
{ {
Title = "Chat", Title = "Chat",
@ -259,11 +297,11 @@ namespace BookAStar
masterDetail.ToolbarItems.Add(tiPubChat); masterDetail.ToolbarItems.Add(tiPubChat);
this.MainPage = masterDetail; this.MainPage = masterDetail;
NavigationService = new NavigationService(masterDetail.Detail.Navigation); NavigationService = new NavigationService(Navigation);
} }
public static Task<string> DisplayActionSheet(string title, string cancel, string destruction, string [] buttons) public static Task<string> DisplayActionSheet(string title, string cancel, string destruction, string [] buttons)
{ {
var currentPage = CurrentApp.masterDetail.Detail.Navigation.NavigationStack.Last(); var currentPage = Navigation.NavigationStack.Last();
return currentPage.DisplayActionSheet(title, cancel, destruction, buttons); return currentPage.DisplayActionSheet(title, cancel, destruction, buttons);
} }
@ -293,7 +331,7 @@ namespace BookAStar
if (isConnected) if (isConnected)
{ {
// TODO Start all cloud related stuff // TODO Start all cloud related stuff
CurrentApp.StartHubConnection(); StartConnexion();
} }
} }
@ -308,18 +346,20 @@ namespace BookAStar
} }
} }
// Start the Hub connection // Start the Hub connection
public async void StartHubConnection () public static async void StartConnexion ()
{ {
try if (CrossConnectivity.Current.IsConnected)
try
{ {
if (chatHubConnection.State == ConnectionState.Disconnected)
await chatHubConnection.Start(); await chatHubConnection.Start();
} }
catch (WebException webex ) catch (WebException )
{ {
// TODO use webex, set this cx down status somewhere, // TODO use webex, set this cx down status somewhere,
// & display it & maybe try again later. // & display it & maybe try again later.
} }
catch (Exception ex) catch (Exception )
{ {
// TODO use ex // TODO use ex
} }
@ -327,36 +367,53 @@ namespace BookAStar
public void SetupHubConnection() public void SetupHubConnection()
{ {
if (chatHubConnection != null)
chatHubConnection.Dispose();
chatHubConnection = new HubConnection(Constants.SignalRHubsUrl); chatHubConnection = new HubConnection(Constants.SignalRHubsUrl);
chatHubConnection.Error += ChatHubConnection_Error; chatHubConnection.Error += ChatHubConnection_Error;
chatHubProxy = chatHubConnection.CreateHubProxy("ChatHub"); chatHubProxy = chatHubConnection.CreateHubProxy("ChatHub");
chatHubProxy.On<string, string>("addPV", (n, m) => { chatHubProxy.On<string, string>("addPV", (n, m) => {
DataManager.Current.PrivateMessages.Add( var msg = new ChatMessage
new ChatMessage {
{ Message = m,
Message = m, SenderId = n,
SenderId = n, Date = DateTime.Now
Date = DateTime.Now };
} DataManager.Instance.PrivateMessages.Add(
msg
); );
DataManager.Instance.ChatUsers.OnPrivateMessage(msg);
}); });
} }
public static void StopConnection()
{
try
{
if (chatHubConnection.State != ConnectionState.Disconnected)
chatHubConnection.Stop();
}
catch (WebException)
{
// TODO use webex, set this cx down status somewhere,
// & display it & maybe try again later.
}
catch (Exception)
{
// TODO use ex
}
}
private void MainSettings_UserChanged(object sender, EventArgs e) private void MainSettings_UserChanged(object sender, EventArgs e)
{ {
if (MainSettings.CurrentUser == null) StopConnection();
{ if (MainSettings.CurrentUser != null)
chatHubConnection.Dispose();
chatHubConnection = null;
chatHubProxy = null;
}
else
{ {
var token = MainSettings.CurrentUser.YavscTokens.AccessToken; var token = MainSettings.CurrentUser.YavscTokens.AccessToken;
SetupHubConnection(); if (chatHubConnection.Headers.ContainsKey("Authorization"))
chatHubConnection.Headers.Remove("Authorization");
chatHubConnection.Headers.Add("Authorization", $"Bearer {token}"); chatHubConnection.Headers.Add("Authorization", $"Bearer {token}");
} }
StartConnexion();
} }
private void ChatHubConnection_Error(Exception obj) private void ChatHubConnection_Error(Exception obj)
@ -379,18 +436,18 @@ namespace BookAStar
} }
} }
public void PostDeviceInfo() public static void PostDeviceInfo()
{ {
var info = PlatformSpecificInstance.GetDeviceInfo(); var info = PlatformSpecificInstance.GetDeviceInfo();
if (!string.IsNullOrWhiteSpace(info.GCMRegistrationId)) if (!string.IsNullOrWhiteSpace(info.GCMRegistrationId))
PlatformSpecificInstance.InvokeApi("gcm/register", info); PlatformSpecificInstance.InvokeApi("gcm/register", info);
} }
public static void ShowBookQuery (BookQueryData query) public static void ShowBookQuery (BookQuery query)
{ {
var page = ViewFactory.CreatePage<BookQueryViewModel var page = new BookQueryPage
, BookQueryPage>((b, p) => p.BindingContext = new BookQueryViewModel(query)); { BindingContext = new BookQueryViewModel(query) };
App.Current.MainPage.Navigation.PushAsync(page as Page); ShowPage(page);
} }
} }
} }

View File

@ -1,63 +0,0 @@
using Xamarin.Forms;
namespace BookAStar.Behaviors
{
public class EditorMaxLengthValidator : Behavior<Editor>
{
public static readonly BindableProperty MaxLengthProperty = BindableProperty.Create("MaxLength", typeof(int), typeof(MaxLengthValidator), 0);
public int MaxLength
{
get { return (int)GetValue(MaxLengthProperty); }
set { SetValue(MaxLengthProperty, value); }
}
protected override void OnAttachedTo(Editor bindable)
{
bindable.TextChanged += bindable_TextChanged;
}
public bool IsValid { get; set; }
private void bindable_TextChanged(object sender, TextChangedEventArgs e)
{
IsValid = e.NewTextValue == null? false : ( e.NewTextValue.Length > 0 && e.NewTextValue.Length <= MaxLength ) ;
if (!IsValid) if (e.NewTextValue!=null) if (e.NewTextValue.Length > MaxLength)
((Editor)sender).Text = e.NewTextValue.Substring(0, MaxLength);
}
protected override void OnDetachingFrom(Editor bindable)
{
bindable.TextChanged -= bindable_TextChanged;
}
}
public class MaxLengthValidator : Behavior<Entry>
{
public static readonly BindableProperty MaxLengthProperty = BindableProperty.Create("MaxLength", typeof(int), typeof(MaxLengthValidator), 0);
public int MaxLength
{
get { return (int)GetValue(MaxLengthProperty); }
set { SetValue(MaxLengthProperty, value); }
}
protected override void OnAttachedTo(Entry bindable)
{
bindable.TextChanged += bindable_TextChanged;
}
private void bindable_TextChanged(object sender, TextChangedEventArgs e)
{
//if (MaxLength != null && MaxLength.HasValue)
if (e.NewTextValue.Length > MaxLength)
((Entry)sender).Text = e.NewTextValue.Substring(0, MaxLength);
}
protected override void OnDetachingFrom(Entry bindable)
{
bindable.TextChanged -= bindable_TextChanged;
}
}
}

View File

@ -42,23 +42,43 @@
</Compile> </Compile>
<Compile Include="Attributes\CurrencyAttribute.cs" /> <Compile Include="Attributes\CurrencyAttribute.cs" />
<Compile Include="Attributes\DisplayAttribute.cs" /> <Compile Include="Attributes\DisplayAttribute.cs" />
<Compile Include="Behaviors\EmailValidatorBehavior.cs" /> <Compile Include="Converters\Behaviors\EmailValidatorBehavior.cs" />
<Compile Include="Behaviors\IntegerEntryBehavior.cs" /> <Compile Include="Converters\Behaviors\IntegerEntryBehavior.cs" />
<Compile Include="Behaviors\MaxLengthValidator.cs" /> <Compile Include="Converters\Behaviors\EditorMaxLengthValidator.cs" />
<Compile Include="Behaviors\DecimalValidatorBehavior.cs" /> <Compile Include="Converters\Behaviors\DecimalValidatorBehavior.cs" />
<Compile Include="Behaviors\PickerBehavior.cs" /> <Compile Include="Converters\Behaviors\MarkdownViewLengthValidator.cs" />
<Compile Include="Behaviors\StarBehavior.cs" /> <Compile Include="Converters\Behaviors\PickerBehavior.cs" />
<Compile Include="Converters\Behaviors\RegexValidatorBehavior.cs" />
<Compile Include="Converters\Behaviors\StarBehavior.cs" />
<Compile Include="Constants.cs" /> <Compile Include="Constants.cs" />
<Compile Include="Converters\NotValueConverter.cs" /> <Compile Include="Converters\NotValueConverter.cs" />
<Compile Include="Converters\SignalRConnectionStateToObject.cs" /> <Compile Include="Converters\SignalRConnectionStateToObject.cs" />
<Compile Include="Data\LocalState.cs" /> <Compile Include="Data\ApiCallFailedException.cs" />
<Compile Include="Data\EstimateEntity.cs" />
<Compile Include="Data\NonCrUD\RemoteFiles.cs" /> <Compile Include="Data\NonCrUD\RemoteFiles.cs" />
<Compile Include="Model\Access\BlackListed.cs" />
<Compile Include="Model\Booking\MusicalPreference.cs" />
<Compile Include="Model\Booking\MusicalTendency.cs" />
<Compile Include="Model\FileSystem\UserDirectoryInfo.cs" /> <Compile Include="Model\FileSystem\UserDirectoryInfo.cs" />
<Compile Include="Model\FileSystem\UserFileInfo.cs" /> <Compile Include="Model\FileSystem\UserFileInfo.cs" />
<Compile Include="Model\Settings\SignatureSettings.cs" /> <Compile Include="Model\Settings\SignatureSettings.cs" />
<Compile Include="Model\Social\Messaging\ChatStatus.cs" /> <Compile Include="Model\Social\Chat\ChatStatus.cs" />
<Compile Include="Model\Social\Messaging\PrivateMessage.cs" /> <Compile Include="Model\Social\Chat\ChatMessage.cs" />
<Compile Include="Model\UI\PageState.cs" /> <Compile Include="Model\Social\LocationType.cs" />
<Compile Include="Pages\ClientPages\SearchPage.xaml.cs">
<DependentUpon>SearchPage.xaml</DependentUpon>
</Compile>
<Compile Include="ViewModels\HomeViewModel.cs" />
<Compile Include="ViewModels\Messaging\ChatUserCollection.cs" />
<Compile Include="ViewModels\Messaging\ChatUserInfo.cs" />
<Compile Include="Model\Social\Chat\Connection.cs" />
<Compile Include="ViewModels\Searching\SearchingAnArtistViewModel.cs" />
<Compile Include="ViewModels\Validation\Error.cs" />
<Compile Include="ViewModels\Validation\ModelState.cs" />
<Compile Include="ViewModels\PageState.cs" />
<Compile Include="Pages\EstimatePages\EstimateSigningPage.xaml.cs">
<DependentUpon>EstimateSigningPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\EstimatePages\EstimatesClientPage.xaml.cs"> <Compile Include="Pages\EstimatePages\EstimatesClientPage.xaml.cs">
<DependentUpon>EstimatesClientPage.xaml</DependentUpon> <DependentUpon>EstimatesClientPage.xaml</DependentUpon>
</Compile> </Compile>
@ -85,14 +105,15 @@
<DesignTime>True</DesignTime> <DesignTime>True</DesignTime>
<DependentUpon>Strings.en.resx</DependentUpon> <DependentUpon>Strings.en.resx</DependentUpon>
</Compile> </Compile>
<Compile Include="ViewModels\EditingViewModel.cs" /> <Compile Include="ViewModels\Validation\EditingViewModel.cs" />
<Compile Include="Pages\DocSigning.xaml.cs"> <Compile Include="Pages\DocSigning.xaml.cs">
<DependentUpon>DocSigning.xaml</DependentUpon> <DependentUpon>DocSigning.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="ViewModels\Signing\DocSigningViewModel.cs" /> <Compile Include="ViewModels\Signing\EstimateSigningViewModel.cs" />
<Compile Include="ViewModels\Signing\SignaturePadConfigViewModel.cs" /> <Compile Include="ViewModels\Signing\SignaturePadConfigViewModel.cs" />
<Compile Include="ViewModels\UserProfile\UserProfileViewModel.cs" /> <Compile Include="ViewModels\UserProfile\UserProfileViewModel.cs" />
<Compile Include="ViewModels\UserProfile\DirectoryInfoViewModel.cs" /> <Compile Include="ViewModels\UserProfile\DirectoryInfoViewModel.cs" />
<Compile Include="ViewModels\Validation\ErrorSeverity.cs" />
<Compile Include="Views\EnumPicker.cs" /> <Compile Include="Views\EnumPicker.cs" />
<Compile Include="Converters\BooleanToObjectConverter.cs" /> <Compile Include="Converters\BooleanToObjectConverter.cs" />
<Compile Include="Converters\EnumConverter.cs" /> <Compile Include="Converters\EnumConverter.cs" />
@ -126,7 +147,7 @@
<Compile Include="Pages\EstimatePages\BookQueryPage.xaml.cs"> <Compile Include="Pages\EstimatePages\BookQueryPage.xaml.cs">
<DependentUpon>BookQueryPage.xaml</DependentUpon> <DependentUpon>BookQueryPage.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Pages\ChatPage.xaml.cs"> <Compile Include="Pages\Chat\ChatPage.xaml.cs">
<DependentUpon>ChatPage.xaml</DependentUpon> <DependentUpon>ChatPage.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Pages\EstimatePages\EditBillingLinePage.xaml.cs"> <Compile Include="Pages\EstimatePages\EditBillingLinePage.xaml.cs">
@ -143,7 +164,7 @@
<Compile Include="Interfaces\IPlatform.cs" /> <Compile Include="Interfaces\IPlatform.cs" />
<Compile Include="Model\Blog\Blog.cs" /> <Compile Include="Model\Blog\Blog.cs" />
<Compile Include="Model\Blog\BlogTag.cs" /> <Compile Include="Model\Blog\BlogTag.cs" />
<Compile Include="Model\BookQueryData.cs" /> <Compile Include="Model\Booking\BookQuery.cs" />
<Compile Include="Model\Market\BaseProduct.cs" /> <Compile Include="Model\Market\BaseProduct.cs" />
<Compile Include="Model\Workflow\BillingLine.cs" /> <Compile Include="Model\Workflow\BillingLine.cs" />
<Compile Include="Model\Manager.cs" /> <Compile Include="Model\Manager.cs" />
@ -183,7 +204,7 @@
<Compile Include="Pages\EstimatePages\EditEstimatePage.xaml.cs"> <Compile Include="Pages\EstimatePages\EditEstimatePage.xaml.cs">
<DependentUpon>EditEstimatePage.xaml</DependentUpon> <DependentUpon>EditEstimatePage.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Pages\Oooops\HomePage.xaml.cs"> <Compile Include="Pages\HomePage.xaml.cs">
<DependentUpon>HomePage.xaml</DependentUpon> <DependentUpon>HomePage.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Pages\Oooops\PinPage.cs" /> <Compile Include="Pages\Oooops\PinPage.cs" />
@ -198,9 +219,6 @@
<Compile Include="Views\ImageButton.cs" /> <Compile Include="Views\ImageButton.cs" />
<Compile Include="Views\MarkdownView.cs" /> <Compile Include="Views\MarkdownView.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Pages\Oooops\SearchPage.xaml.cs">
<DependentUpon>SearchPage.xaml</DependentUpon>
</Compile>
<Compile Include="Pages\UserProfile\AccountChooserPage.xaml.cs"> <Compile Include="Pages\UserProfile\AccountChooserPage.xaml.cs">
<DependentUpon>AccountChooserPage.xaml</DependentUpon> <DependentUpon>AccountChooserPage.xaml</DependentUpon>
</Compile> </Compile>
@ -233,17 +251,13 @@
<Generator>MSBuild:UpdateDesignTimeXaml</Generator> <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Pages\Oooops\SearchPage.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Pages\UserProfile\AccountChooserPage.xaml"> <EmbeddedResource Include="Pages\UserProfile\AccountChooserPage.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator> <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Model\Billing\" /> <Folder Include="Model\UI\" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="ExifLib, Version=1.0.1.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="ExifLib, Version=1.0.1.0, Culture=neutral, processorArchitecture=MSIL">
@ -272,6 +286,12 @@
<Reference Include="Plugin.Connectivity.Abstractions"> <Reference Include="Plugin.Connectivity.Abstractions">
<HintPath>..\..\packages\Xam.Plugin.Connectivity.2.2.12\lib\MonoAndroid10\Plugin.Connectivity.Abstractions.dll</HintPath> <HintPath>..\..\packages\Xam.Plugin.Connectivity.2.2.12\lib\MonoAndroid10\Plugin.Connectivity.Abstractions.dll</HintPath>
</Reference> </Reference>
<Reference Include="Plugin.Media">
<HintPath>..\..\packages\Xam.Plugin.Media.2.3.0\lib\portable-net45+wp8+wpa81+win8+MonoAndroid10+MonoTouch10+Xamarin.iOS10\Plugin.Media.dll</HintPath>
</Reference>
<Reference Include="Plugin.Media.Abstractions">
<HintPath>..\..\packages\Xam.Plugin.Media.2.3.0\lib\portable-net45+wp8+wpa81+win8+MonoAndroid10+MonoTouch10+Xamarin.iOS10\Plugin.Media.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Plugin.Settings, Version=2.5.1.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="Plugin.Settings, Version=2.5.1.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xam.Plugins.Settings.2.5.1.0\lib\portable-net45+wp80+win8+wpa81\Plugin.Settings.dll</HintPath> <HintPath>..\..\packages\Xam.Plugins.Settings.2.5.1.0\lib\portable-net45+wp80+win8+wpa81\Plugin.Settings.dll</HintPath>
<Private>True</Private> <Private>True</Private>
@ -296,6 +316,9 @@
<HintPath>..\..\packages\Microsoft.Net.Http.2.2.29\lib\portable-net45+win8+wpa81\System.Net.Http.Primitives.dll</HintPath> <HintPath>..\..\packages\Microsoft.Net.Http.2.2.29\lib\portable-net45+win8+wpa81\System.Net.Http.Primitives.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="System.Web.Services">
<HintPath>..\..\..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\MonoAndroid\v1.0\System.Web.Services.dll</HintPath>
</Reference>
<Reference Include="Xamarin.Forms.Core, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="Xamarin.Forms.Core, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Xamarin.Forms.2.3.2.127\lib\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+MonoTouch10+Xamarin.iOS10\Xamarin.Forms.Core.dll</HintPath> <HintPath>..\..\packages\Xamarin.Forms.2.3.2.127\lib\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+MonoTouch10+Xamarin.iOS10\Xamarin.Forms.Core.dll</HintPath>
<Private>True</Private> <Private>True</Private>
@ -351,12 +374,6 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Yavsc.Client\Yavsc.Client.csproj">
<Project>{67f9d3a8-f71e-4428-913f-c37ae82cdb24}</Project>
<Name>Yavsc.Client</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Pages\EstimatePages\EditEstimatePage.xaml"> <EmbeddedResource Include="Pages\EstimatePages\EditEstimatePage.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator> <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
@ -390,7 +407,7 @@
<EmbeddedResource Include="Images\Users\icon_user_settings.png" /> <EmbeddedResource Include="Images\Users\icon_user_settings.png" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Pages\Oooops\HomePage.xaml"> <EmbeddedResource Include="Pages\HomePage.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator> <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
</EmbeddedResource> </EmbeddedResource>
@ -416,7 +433,7 @@
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Pages\ChatPage.xaml"> <EmbeddedResource Include="Pages\Chat\ChatPage.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator> <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
</EmbeddedResource> </EmbeddedResource>
@ -478,6 +495,27 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Pages\EstimatePages\EstimateSigningPage.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\YavscLib\YavscLib.csproj">
<Project>{67f9d3a8-f71e-4428-913f-c37ae82cdb24}</Project>
<Name>YavscLib</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Images\Chat\talk.png" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Pages\ClientPages\SearchPage.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" /> <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')" /> <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"> <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">

View File

@ -11,11 +11,13 @@ namespace BookAStar
{ {
public const string ApplicationName = "Booking Star"; public const string ApplicationName = "Booking Star";
#region Uri
#if DEV #if DEV
public const string YavscHomeUrl = "http://dev.pschneider.fr"; public const string YavscHomeUrl = "http://dev.pschneider.fr";
#else #else
#if WDEV
public const string YavscHomeUrl = "http://192.168.0.29:5000";
#else
#if YAVSC #if YAVSC
public const string YavscHomeUrl = "https://yavsc.pschneider.fr"; public const string YavscHomeUrl = "https://yavsc.pschneider.fr";
#else #else
@ -26,17 +28,19 @@ namespace BookAStar
#endif #endif
#endif #endif
#endif #endif
#endif
public static readonly string AuthorizeUrl = YavscHomeUrl + "/authorize";
public static readonly string RedirectUrl = YavscHomeUrl + "/oauth/success";
public static readonly string AccessTokenUrl = YavscHomeUrl + "/token";
public static readonly string YavscApiUrl = YavscHomeUrl + "/api"; public static readonly string YavscApiUrl = YavscHomeUrl + "/api";
public static readonly string MobileRegistrationUrl = YavscApiUrl + "/gcm/register"; public static readonly string MobileRegistrationUrl = YavscApiUrl + "/gcm/register";
public static readonly string UserInfoUrl = YavscApiUrl + "/me"; public static readonly string UserInfoUrl = YavscApiUrl + "/me";
public static readonly string BlogUrl = YavscApiUrl + "/blogs"; public static readonly string BlogUrl = YavscApiUrl + "/blogs";
public static readonly string FsUrl = YavscApiUrl + "/fs"; public static readonly string FsUrl = YavscApiUrl + "/fs";
public static readonly string SignalRHubsUrl = YavscHomeUrl + "/api/signalr/hubs"; public static readonly string SignalRHubsUrl = YavscHomeUrl + "/api/signalr/hubs";
#endregion
#region Permissions ids
public static int AllowBeATarget = 1; public static int AllowBeATarget = 1;
#endregion
public static int CloudTimeout = 400; public static int CloudTimeout = 400;
} }

View File

@ -0,0 +1,80 @@
using System;
using Xamarin.Forms;
namespace BookAStar.Behaviors
{
public class EditorMaxLengthValidator : Behavior<Editor>
{
public static readonly BindableProperty MaxLengthProperty =
BindableProperty.Create("MaxLength", typeof(int), typeof(EditorMaxLengthValidator), int.MaxValue);
public static readonly BindableProperty MinLengthProperty =
BindableProperty.Create("MinLength", typeof(int), typeof(EditorMaxLengthValidator), 0);
public static readonly BindableProperty IsValidProperty =
BindableProperty.Create("IsValid", typeof(bool), typeof(EditorMaxLengthValidator), false);
public static readonly BindableProperty ErrorProperty =
BindableProperty.Create("Error", typeof(string), typeof(EditorMaxLengthValidator), null);
public int MaxLength
{
get { return (int) GetValue(MaxLengthProperty); }
set { SetValue(MaxLengthProperty, value); }
}
public int MinLength
{
get { return (int) GetValue(MinLengthProperty); }
set { SetValue(MinLengthProperty, value); }
}
protected override void OnAttachedTo(Editor bindable)
{
bindable.TextChanged += bindable_TextChanged;
}
public bool IsValid {
get { return (bool) GetValue(IsValidProperty); }
set { SetValue(IsValidProperty, value);
}
}
public string Error
{
get
{
return (string) GetValue(ErrorProperty);
}
set
{
SetValue(ErrorProperty, value);
}
}
private void bindable_TextChanged(object sender, TextChangedEventArgs e)
{
if (e.NewTextValue != null && e.NewTextValue.Length >= MinLength)
{
if (e.NewTextValue.Length > MaxLength)
{
((Editor)sender).Text = e.NewTextValue.Substring(0, MaxLength);
Error = Strings.YourTextWasTooLong;
}
else Error = "";
IsValid = true;
}
else
{
Error = string.Format(Strings.MinMaxStringValidationError,
MinLength, MaxLength);
IsValid = false;
}
}
protected override void OnDetachingFrom(Editor bindable)
{
bindable.TextChanged -= bindable_TextChanged;
}
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace BookAStar.Behaviors
{
public class EmailValidatorBehavior : RegexValidatorBehavior
{
const string emailRegex = @"^(?("")("".+?(?<!\\)""@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))" +
@"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-\w]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$";
public EmailValidatorBehavior()
{
Regexp = emailRegex;
}
}
}

View File

@ -6,7 +6,10 @@ namespace BookAStar.Behaviors
{ {
public static readonly BindableProperty MinProperty = BindableProperty.Create("Min", typeof(int), typeof(IntegerEntryBehavior), 0); 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 static readonly BindableProperty MaxProperty = BindableProperty.Create("Max", typeof(int), typeof(IntegerEntryBehavior), 0);
public static readonly BindableProperty IsValidProperty =
BindableProperty.Create("IsValid", typeof(bool), typeof(IntegerEntryBehavior), false);
public static readonly BindableProperty ErrorProperty =
BindableProperty.Create("Error", typeof(string), typeof(IntegerEntryBehavior), null);
public int Min public int Min
{ {
get { return (int)GetValue(MinProperty); } get { return (int)GetValue(MinProperty); }
@ -30,8 +33,13 @@ namespace BookAStar.Behaviors
if (int.TryParse(e.NewTextValue, out val)) if (int.TryParse(e.NewTextValue, out val))
{ {
IsValid = (Min > Max) || (Max >= val && val >= Min); IsValid = (Min > Max) || (Max >= val && val >= Min);
Error = string.Format(Strings.MinMaxIntError, Min, Max);
}
else
{
IsValid = false;
Error = "";
} }
else IsValid = false;
} }
protected override void OnDetachingFrom(Entry bindable) protected override void OnDetachingFrom(Entry bindable)
@ -39,6 +47,26 @@ namespace BookAStar.Behaviors
bindable.TextChanged -= bindable_TextChanged; bindable.TextChanged -= bindable_TextChanged;
} }
public bool IsValid { get; private set; } public bool IsValid
{
get {
return (bool) GetValue(IsValidProperty);
}
set
{
SetValue(IsValidProperty, value);
}
}
public string Error
{
get
{
return (string) GetValue(ErrorProperty);
}
set
{
SetValue(ErrorProperty, value);
}
}
} }
} }

View File

@ -0,0 +1,39 @@
using BookAStar.Views;
using Xamarin.Forms;
namespace BookAStar.Behaviors
{
public class MarkdownViewLengthValidator : Behavior<MarkdownView>
{
public static readonly BindableProperty MaxLengthProperty = BindableProperty.Create("MaxLength", typeof(int), typeof(MarkdownViewLengthValidator), 0);
public static readonly BindableProperty MinLengthProperty = BindableProperty.Create("MinLength", typeof(int), typeof(MarkdownViewLengthValidator), 0);
public int MaxLength
{
get { return (int)GetValue(MaxLengthProperty); }
set { SetValue(MaxLengthProperty, value); }
}
public int MinLength
{
get { return (int)GetValue(MinLengthProperty); }
set { SetValue(MinLengthProperty, value); }
}
protected override void OnAttachedTo(MarkdownView bindable)
{
bindable.Modified += bindable_TextChanged;
}
public bool IsValid { get; set; }
private void bindable_TextChanged(object sender, TextChangedEventArgs e)
{
IsValid = e.NewTextValue == null ? false : (e.NewTextValue.Length >= MinLength && e.NewTextValue.Length <= MaxLength);
if (!IsValid) if (e.NewTextValue != null) if (e.NewTextValue.Length > MaxLength)
((Editor)sender).Text = e.NewTextValue.Substring(0, MaxLength);
}
protected override void OnDetachingFrom(MarkdownView bindable)
{
bindable.Modified -= bindable_TextChanged;
}
}
}

View File

@ -1,21 +1,15 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Xamarin.Forms; using Xamarin.Forms;
namespace BookAStar.Behaviors namespace BookAStar.Behaviors
{ {
public class EmailValidatorBehavior : Behavior<Entry> public class RegexValidatorBehavior : Behavior<Entry>
{ {
const string emailRegex = @"^(?("")("".+?(?<!\\)""@)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])@))" + private string regexp = @"^([\d\w]+)$";
@"(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-\w]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$";
// Creating BindableProperties with Limited write access: http://iosapi.xamarin.com/index.aspx?link=M%3AXamarin.Forms.BindableObject.SetValue(Xamarin.Forms.BindablePropertyKey%2CSystem.Object) // Creating BindableProperties with Limited write access: http://iosapi.xamarin.com/index.aspx?link=M%3AXamarin.Forms.BindableObject.SetValue(Xamarin.Forms.BindablePropertyKey%2CSystem.Object)
static readonly BindablePropertyKey IsValidPropertyKey = BindableProperty.CreateReadOnly("IsValid", typeof(bool), typeof(EmailValidatorBehavior), false); static readonly BindablePropertyKey IsValidPropertyKey = BindableProperty.CreateReadOnly("IsValid", typeof(bool), typeof(RegexValidatorBehavior), false);
public static readonly BindableProperty IsValidProperty = IsValidPropertyKey.BindableProperty; public static readonly BindableProperty IsValidProperty = IsValidPropertyKey.BindableProperty;
@ -25,6 +19,19 @@ namespace BookAStar.Behaviors
private set { base.SetValue(IsValidPropertyKey, value); } private set { base.SetValue(IsValidPropertyKey, value); }
} }
protected string Regexp
{
get
{
return regexp;
}
set
{
regexp = value;
}
}
protected override void OnAttachedTo(Entry bindable) protected override void OnAttachedTo(Entry bindable)
{ {
bindable.TextChanged += HandleTextChanged; bindable.TextChanged += HandleTextChanged;
@ -33,7 +40,7 @@ namespace BookAStar.Behaviors
void HandleTextChanged(object sender, TextChangedEventArgs e) void HandleTextChanged(object sender, TextChangedEventArgs e)
{ {
IsValid = (Regex.IsMatch(e.NewTextValue, emailRegex, RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250))); IsValid = (Regex.IsMatch(e.NewTextValue, regexp, RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250)));
((Entry)sender).TextColor = IsValid ? Color.Default : Color.Red; ((Entry)sender).TextColor = IsValid ? Color.Default : Color.Red;
} }

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BookAStar.Data
{
class ApiCallFailedException : Exception
{
public ApiCallFailedException(string message) : base(message)
{
}
public ApiCallFailedException(string message, Exception inner) : base(message, inner)
{
}
}
}

View File

@ -3,45 +3,49 @@
using Model; using Model;
using Model.Blog; using Model.Blog;
using Model.Workflow; using Model.Workflow;
using Model.UI;
using Model.Social.Messaging; using Model.Social.Messaging;
using Model.FileSystem;
using ViewModels.EstimateAndBilling; using ViewModels.EstimateAndBilling;
using NonCrUD; using NonCrUD;
using ViewModels;
using Model.Access;
using ViewModels.Messaging;
using Model.Social;
public class DataManager public class DataManager
{ {
// TODO estimatetemplate rating service product tag // TODO estimatetemplate rating service product tag
public RemoteEntityRO<BookQueryData, long> BookQueries { get; set; } public RemoteEntityRO<BookQuery, long> BookQueries { get; set; }
public RemoteEntity<Estimate, long> Estimates { get; set; } public ChatUserCollection ChatUsers { get; set; }
public EstimateEntity Estimates { get; set; }
public RemoteEntity<Blog, long> Blogspot { get; set; } public RemoteEntity<Blog, long> Blogspot { get; set; }
internal RemoteFilesEntity RemoteFiles { get; set; } internal RemoteFilesEntity RemoteFiles { get; set; }
public LocalEntity<ClientProviderInfo,string> Contacts { get; set; } public LocalEntity<ClientProviderInfo, string> Contacts { get; set; }
internal LocalEntity<PageState, int> AppState { get; set; } internal RemoteEntity<BlackListed, long> BlackList { get; set; }
/// <summary> /// <summary>
/// They have no remote exisence ... /// They've got no remote existence ...
/// </summary> /// </summary>
internal LocalEntity<EditEstimateViewModel, long> EstimationCache { get; set; } internal LocalEntity<EditEstimateViewModel, long> EstimationCache { get; set; }
internal LocalEntity<BillingLine, string> EstimateLinesTemplates { get; set; } internal LocalEntity<BillingLine, string> EstimateLinesTemplates { get; set; }
internal LocalEntity<ChatMessage, int> PrivateMessages { get; set; } internal LocalEntity<ChatMessage, int> PrivateMessages { get; set; }
protected static DataManager current ; internal LocalEntity<PageState, int> AppState { get; set; }
internal LocalEntity<string,string> ClientSignatures { get; set; }
internal LocalEntity<string, string> ProviderSignatures { get; set; }
public static DataManager Current protected static DataManager instance = new DataManager();
public static DataManager Instance
{ {
get get
{ {
if (current == null) return instance;
current = new DataManager();
return current;
} }
} }
public DataManager() public DataManager()
{ {
BookQueries = new RemoteEntityRO<BookQueryData, long>("bookquery", q => q.Id); BookQueries = new RemoteEntityRO<BookQuery, long>("bookquery", q => q.Id);
Estimates = new RemoteEntity<Estimate, long>("estimate", x => x.Id); Estimates = new EstimateEntity();
Blogspot = new RemoteEntity<Blog, long>("blog", x=>x.Id); Blogspot = new RemoteEntity<Blog, long>("blog", x=>x.Id);
Contacts = new LocalEntity<ClientProviderInfo, string>(c => c.UserId); Contacts = new LocalEntity<ClientProviderInfo, string>(c => c.UserId);
@ -50,7 +54,8 @@
EstimateLinesTemplates = new LocalEntity<BillingLine, string>(l => l.Description); EstimateLinesTemplates = new LocalEntity<BillingLine, string>(l => l.Description);
PrivateMessages = new LocalEntity<ChatMessage, int>(m=> m.GetHashCode()); PrivateMessages = new LocalEntity<ChatMessage, int>(m=> m.GetHashCode());
RemoteFiles = new RemoteFilesEntity (); RemoteFiles = new RemoteFilesEntity ();
BlackList = new RemoteEntity<BlackListed, long>("blacklist",u => u.Id);
ChatUsers = new ChatUserCollection();
PrivateMessages.Load(); PrivateMessages.Load();
BookQueries.Load(); BookQueries.Load();
Estimates.Load(); Estimates.Load();
@ -60,6 +65,9 @@
EstimationCache.Load(); EstimationCache.Load();
EstimateLinesTemplates.Load(); EstimateLinesTemplates.Load();
RemoteFiles.Load(); RemoteFiles.Load();
BlackList.Load();
ChatUsers.Load();
BlackList.Load();
} }
} }
} }

View File

@ -0,0 +1,62 @@

namespace BookAStar.Data
{
using Helpers;
using Model.Workflow;
using Newtonsoft.Json;
using System;
using System.Diagnostics;
using System.IO;
using System.Net.Http;
public class EstimateEntity : RemoteEntity<Estimate, long>
{
public EstimateEntity() : base("estimate", e => e.Id)
{
}
public async void SignAsProvider(Estimate estimate, Stream pngStream)
{
if (estimate.Id == 0)
{
var ok = await this.Create(estimate);
if (!ok)
{
await App.DisplayAlert("Erreur d'accès au serveur", "Echec de l'envoi de l'estimation");
return;
}
this.Add(estimate);
}
using (HttpClient client = UserHelpers.CreateJsonClient())
{
try
{
var requestContent = new MultipartFormDataContent();
var content = new StreamContent(pngStream);
var filename = $"prosign-{estimate.Id}.png";
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
content.Headers.Add("Content-Disposition", $"form-data; name=\"file\"; filename=\"{filename}\"");
requestContent.Add(content, "file", filename);
using (var response = await client.PostAsync(
Constants.YavscApiUrl + $"/pdfestimate/prosign/{estimate.Id}", requestContent))
{
if (!response.IsSuccessStatusCode)
{
var errContent = await response.Content.ReadAsStringAsync();
throw new ApiCallFailedException($"SignAsProvider: {response.StatusCode} / {errContent}");
}
var json = await response.Content.ReadAsStringAsync();
JsonConvert.PopulateObject(json, estimate);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
}
}
this.SaveEntity();
}
}
}

View File

@ -6,13 +6,6 @@ namespace BookAStar.Data.NonCrUD
{ {
using Helpers; using Helpers;
using Model.FileSystem; using Model.FileSystem;
using System.Linq;
/*
public class DirectoryEntryChangingEvent : EventArgs
{
public UserDirectoryInfo OldItem { get; set; }
public UserDirectoryInfo NewItem { get; set; }
}*/
public class RemoteFilesEntity : RemoteEntity<UserDirectoryInfo, FileAddress> public class RemoteFilesEntity : RemoteEntity<UserDirectoryInfo, FileAddress>
{ {
@ -24,7 +17,7 @@ namespace BookAStar.Data.NonCrUD
public override async void Execute(object parameter) public override async void Execute(object parameter)
{ {
BeforeExecute(); BeforeExecute();
using (var client = UserHelpers.CreateClient()) using (var client = UserHelpers.CreateJsonClient())
{ {
// Get the whole data // Get the whole data
try try

View File

@ -11,6 +11,7 @@ namespace BookAStar.Data
using Helpers; using Helpers;
using System.Diagnostics; using System.Diagnostics;
using System.Text; using System.Text;
using System.Web;
public class RemoteEntity<V, K> : LocalEntity<V, K>, ICommand where K : IEquatable<K> public class RemoteEntity<V, K> : LocalEntity<V, K>, ICommand where K : IEquatable<K>
{ {
@ -22,7 +23,7 @@ namespace BookAStar.Data
public bool CanExecute(object parameter) public bool CanExecute(object parameter)
{ {
return !IsExecuting && (MainSettings.CurrentUser != null); return !IsExecuting;
} }
public RemoteEntity(string controllerName, Func<V, K> getKey) : base(getKey) public RemoteEntity(string controllerName, Func<V, K> getKey) : base(getKey)
@ -36,7 +37,7 @@ namespace BookAStar.Data
protected void BeforeExecute() protected void BeforeExecute()
{ {
if (IsExecuting) if (IsExecuting)
throw new InvalidOperationException("Already executing"); throw new InvalidOperationException(Strings.AlreadyExecuting);
IsExecuting = true; IsExecuting = true;
if (CanExecuteChanged != null) if (CanExecuteChanged != null)
CanExecuteChanged.Invoke(this, new EventArgs()); CanExecuteChanged.Invoke(this, new EventArgs());
@ -49,7 +50,7 @@ namespace BookAStar.Data
public virtual async void Execute(object parameter) public virtual async void Execute(object parameter)
{ {
BeforeExecute(); BeforeExecute();
using (HttpClient client = UserHelpers.CreateClient()) using (HttpClient client = UserHelpers.CreateJsonClient())
{ {
// Get the whole data // Get the whole data
try try
@ -71,7 +72,7 @@ namespace BookAStar.Data
} }
catch (WebException webex) catch (WebException webex)
{ {
throw new ServiceNotAvailable("No remote entity", webex); throw new ServiceNotAvailable(Strings.ENoRemoteEntity, webex);
} }
} }
@ -95,7 +96,7 @@ namespace BookAStar.Data
protected Uri GetUri(K key) protected Uri GetUri(K key)
{ {
return new Uri(ControllerUri.AbsoluteUri + "/" + key.ToString()); return new Uri(ControllerUri.AbsoluteUri + "/" + HttpUtility.UrlEncode(key.ToString()));
} }
public virtual async Task<V> RemoteGet(K key) public virtual async Task<V> RemoteGet(K key)
@ -105,7 +106,7 @@ namespace BookAStar.Data
// Get the whole data // Get the whole data
var uri = GetUri(key); var uri = GetUri(key);
using (HttpClient client = UserHelpers.CreateClient()) using (HttpClient client = UserHelpers.CreateJsonClient())
{ {
using (var response = await client.GetAsync(uri)) using (var response = await client.GetAsync(uri))
{ {
@ -123,23 +124,27 @@ namespace BookAStar.Data
return item; return item;
} }
public virtual async void Create(V item) public virtual async Task<bool> Create(V item)
{ {
bool created = false;
BeforeExecute(); BeforeExecute();
using (HttpClient client = UserHelpers.CreateClient()) using (HttpClient client = UserHelpers.CreateJsonClient())
{ {
var stringContent = JsonConvert.SerializeObject(item); var stringContent = JsonConvert.SerializeObject(item);
HttpContent content = new StringContent( HttpContent content = new StringContent(
stringContent, Encoding.UTF8, "application/json" stringContent, Encoding.UTF8, "application/json"
); );
using (var response = await client.PostAsync(ControllerUri, content)) using (var response = await client.PostAsync(ControllerUri, content))
{ {
if (!response.IsSuccessStatusCode) created = response.IsSuccessStatusCode;
if (!created)
{ {
// TODO throw custom exception, and catch to inform user // TODO throw custom exception, and catch to inform user
var errcontent = await response.Content.ReadAsStringAsync(); var errcontent = await response.Content.ReadAsStringAsync();
Debug.WriteLine($"Create failed posting {stringContent} @ {ControllerUri.AbsoluteUri}: {errcontent}"); Debug.WriteLine(string.Format(Strings.CreationFailed));
Debug.WriteLine(errcontent);
} }
else else
{ {
@ -148,31 +153,35 @@ namespace BookAStar.Data
JsonConvert.PopulateObject(recontent, item); JsonConvert.PopulateObject(recontent, item);
} }
} }
} }
CurrentItem = item; CurrentItem = item;
AfterExecuting(); AfterExecuting();
return created;
} }
public virtual async void Update(V item) public virtual async Task<bool> Update(V item)
{ {
var updated = false;
BeforeExecute(); BeforeExecute();
var uri = GetUri(GetKey(item)); var uri = GetUri(GetKey(item));
using (HttpClient client = UserHelpers.CreateClient()) using (HttpClient client = UserHelpers.CreateJsonClient())
{ {
HttpContent content = new StringContent( HttpContent content = new StringContent(
JsonConvert.SerializeObject(item), Encoding.UTF8, "application/json" JsonConvert.SerializeObject(item), Encoding.UTF8, "application/json"
); );
using (var response = await client.PutAsync(uri, content)) using (var response = await client.PutAsync(uri, content))
{ {
if (!response.IsSuccessStatusCode) updated = response.IsSuccessStatusCode;
if (!updated)
{// TODO throw custom exception, and catch to inform user {// TODO throw custom exception, and catch to inform user
if (response.StatusCode == HttpStatusCode.BadRequest) if (response.StatusCode == HttpStatusCode.BadRequest)
{ {
var recontent = await response.Content.ReadAsStringAsync(); var errorcontent = await response.Content.ReadAsStringAsync();
} Debug.WriteLine(string.Format(Strings.UpdateFailed));
Debug.WriteLine(errorcontent);
}
else Debug.WriteLine($"Update failed ({item} @ {uri.AbsolutePath} )"); else Debug.WriteLine($"Update failed ({item} @ {uri.AbsolutePath} )");
} }
@ -188,13 +197,14 @@ namespace BookAStar.Data
CurrentItem = item; CurrentItem = item;
AfterExecuting(); AfterExecuting();
return updated;
} }
public virtual async void Delete(K key) public virtual async void Delete(K key)
{ {
BeforeExecute(); BeforeExecute();
var uri = GetUri(key); var uri = GetUri(key);
using (HttpClient client = UserHelpers.CreateClient()) using (HttpClient client = UserHelpers.CreateJsonClient())
{ {
using (var response = await client.DeleteAsync(uri)) using (var response = await client.DeleteAsync(uri))
{ {

View File

@ -17,18 +17,35 @@ namespace BookAStar.Helpers
{ {
var result = avatarPath == null ? var result = avatarPath == null ?
ImageSource.FromResource( "BookAStar.Images.Users.icon_user.png") : ImageSource.FromResource( "BookAStar.Images.Users.icon_user.png") :
avatarPath.StartsWith("res://") ? ImageSource.FromUri(new Uri(Constants.YavscHomeUrl+"/Avatars/"+avatarPath)) ;
ImageSource.FromResource(avatarPath.Substring(6)) :
ImageSource.FromUri(new Uri(avatarPath));
return result; return result;
} }
public static HttpClient CreateClient() public static ImageSource SmallAvatar(string avatarPath, string username)
{
return avatarPath == null ?
ImageSource.FromResource("BookAStar.Images.Users.icon_user.png") :
ImageSource.FromUri(new Uri($"{Constants.YavscHomeUrl}/Avatars/{username}.s.png"));
}
public static ImageSource ExtraSmallAvatar(string avatarPath, string username)
{
return avatarPath == null ?
ImageSource.FromResource("BookAStar.Images.Users.icon_user.png") :
ImageSource.FromUri(new Uri($"{Constants.YavscHomeUrl}/Avatars/{username}.xs.png"));
}
public static HttpClient CreateJsonClient()
{
return CreateJsonClient(MainSettings.CurrentUser.YavscTokens.AccessToken);
}
public static HttpClient CreateJsonClient(string accessToken)
{ {
var client = new HttpClient(); var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue( new System.Net.Http.Headers.AuthenticationHeaderValue(
"Bearer", MainSettings.CurrentUser.YavscTokens.AccessToken); "Bearer", accessToken);
client.DefaultRequestHeaders.Add("Accept", "application/json"); client.DefaultRequestHeaders.Add("Accept", "application/json");
return client; return client;
} }
@ -42,7 +59,7 @@ namespace BookAStar.Helpers
/// <returns></returns> /// <returns></returns>
public static async Task<bool> Upload(Stream inputStream, string fileName) public static async Task<bool> Upload(Stream inputStream, string fileName)
{ {
using (var client = CreateClient()) using (var client = CreateJsonClient())
{ {
using (var content = using (var content =
new MultipartFormDataContent("Upload----" + DateTime.Now.ToString(CultureInfo.InvariantCulture))) new MultipartFormDataContent("Upload----" + DateTime.Now.ToString(CultureInfo.InvariantCulture)))

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,28 @@
using BookAStar.Model.Access;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Yavsc.Models;
namespace BookAStar.Model.Access
{
public class BlackListed : IBlackListed
{
public long Id
{
get; set;
}
public string OwnerId
{
get; set;
}
public string UserId
{
get; set;
}
}
}

View File

@ -1,12 +1,7 @@
using BookAStar.Helpers; using BookAStar.Helpers;
using Newtonsoft.Json; using Newtonsoft.Json;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel; using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms; using Xamarin.Forms;
namespace BookAStar.Model.Auth.Account namespace BookAStar.Model.Auth.Account
@ -81,6 +76,20 @@ namespace BookAStar.Model.Auth.Account
OnPropertyChanged("Avatar"); OnPropertyChanged("Avatar");
} }
} }
private string address;
public string Address
{
get
{
return address;
}
set
{
address = value;
OnPropertyChanged("Address");
}
}
[JsonIgnore] [JsonIgnore]
public ImageSource AvatarSource public ImageSource AvatarSource
{ {

View File

@ -1,15 +1,16 @@
using BookAStar.Interfaces; 
using BookAStar.Model.Social;
using System; using System;
namespace BookAStar.Model namespace BookAStar.Model.Social
{ {
public class BookQueryData : IBookQueryData public class BookQuery
{ {
public ClientProviderInfo Client { get; set; } public ClientProviderInfo Client { get; set; }
public Location Location { get; set; } public Location Location { get; set; }
public long Id { get; set; } public long Id { get; set; }
public DateTime EventDate { get; set; } public DateTime EventDate { get; set; }
public decimal? Previsionnal { get; set; } public decimal? Previsionnal { get; set; }
public string Reason { get; set; }
public bool Read { get; set; }
} }
} }

View File

@ -0,0 +1,12 @@
namespace BookAStar.Model.Social
{
public class MusicalPreference : MusicalTendency {
public long OwnerId { get; set; }
public int Rate { get; set; }
}
}

View File

@ -0,0 +1,13 @@
namespace BookAStar.Model.Social
{
public class MusicalTendency {
public long Id {get; set; }
public string Name { get ; set; }
}
}

View File

@ -12,22 +12,14 @@ namespace BookAStar.Model
public string Avatar { get; set; } public string Avatar { get; set; }
public string UserId { get; set; } public string UserId { get; set; }
// TODO Get User Professional status existence as a boolean
// And hack the avatar with
public int Rate { get; set; } public int Rate { get; set; }
public string EMail { get; set; } public string EMail { get; set; }
public string Phone { get; set; } public string Phone { get; set; }
public Location BillingAddress { get; set; } public Location BillingAddress { get; set; }
// TODO Get User Professional status existence as a boolean
// And hack the avatar with
[JsonIgnore]
public ImageSource AvatarOrNot
{
get
{
return UserHelpers.Avatar(Avatar);
}
}
public string ChatHubConnectionId { get; set; }
public override string ToString() public override string ToString()
{ {

View File

@ -1,8 +1,7 @@
using System; using BookAStar.Data;
using System.Collections.Generic; using Newtonsoft.Json;
using System;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BookAStar.Model.Social.Messaging namespace BookAStar.Model.Social.Messaging
{ {
@ -11,5 +10,6 @@ namespace BookAStar.Model.Social.Messaging
public DateTime Date { get; set; } public DateTime Date { get; set; }
public string SenderId { get; set; } public string SenderId { get; set; }
public string Message { get; set; } public string Message { get; set; }
public bool Read { get; set; }
} }
} }

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YavscLib;
namespace BookAStar.Model.Social.Chat
{
public class Connection : IConnection
{
public bool Connected
{
get; set;
}
public string ConnectionId
{
get; set;
}
public string UserAgent
{
get; set;
}
}
}

View File

@ -0,0 +1,10 @@
namespace BookAStar.Model.Social
{
public class LocationType
{
public long Id { get; set; }
public string Name { get; set; }
}
}

View File

@ -7,7 +7,6 @@ namespace BookAStar.Model.Workflow.Messaging
/// <summary> /// <summary>
/// Query, for a date, with a given perfomer, at this given place. /// Query, for a date, with a given perfomer, at this given place.
/// </summary> /// </summary>
public class BookQuery { public class BookQuery {
/// <summary> /// <summary>
/// The command identifier /// The command identifier

View File

@ -1,5 +1,5 @@
namespace Yavsc.Models namespace Yavsc.Model
{ {
public partial class Tag public partial class Tag
{ {

View File

@ -1,6 +1,4 @@
using BookAStar.Data; 
using BookAStar.Helpers;
using BookAStar.Model.Interfaces;
using Newtonsoft.Json; using Newtonsoft.Json;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -8,6 +6,9 @@ using System.Linq;
namespace BookAStar.Model.Workflow namespace BookAStar.Model.Workflow
{ {
using Data;
using Interfaces;
using Social;
public partial class Estimate : IEstimate public partial class Estimate : IEstimate
{ {
public long Id { get; set; } public long Id { get; set; }
@ -26,6 +27,7 @@ namespace BookAStar.Model.Workflow
/// <returns></returns> /// <returns></returns>
public List<string> AttachedGraphics { get; set; } public List<string> AttachedGraphics { get; set; }
[JsonIgnore] [JsonIgnore]
// form in db
public string AttachedGraphicsString public string AttachedGraphicsString
{ {
get { return AttachedGraphics==null?null:string.Join(":", AttachedGraphics); } get { return AttachedGraphics==null?null:string.Join(":", AttachedGraphics); }
@ -39,6 +41,7 @@ namespace BookAStar.Model.Workflow
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public List<string> AttachedFiles { get; set; } public List<string> AttachedFiles { get; set; }
// form in db
[JsonIgnore] [JsonIgnore]
public string AttachedFilesString public string AttachedFilesString
{ {
@ -47,16 +50,28 @@ namespace BookAStar.Model.Workflow
} }
public string OwnerId { get; set; } public string OwnerId { get; set; }
[JsonIgnore]
public ClientProviderInfo Owner
{
get
{
if (!string.IsNullOrWhiteSpace(OwnerId))
{
var dm = DataManager.Instance;
return dm.Contacts.LocalGet(OwnerId);
}
return null;
}
}
public string ClientId { get; set; } public string ClientId { get; set; }
[JsonIgnore] [JsonIgnore]
public BookQueryData Query public BookQuery Query
{ {
get get
{ {
if (CommandId.HasValue) if (CommandId.HasValue)
{ {
var dm = DataManager.Current; var dm = DataManager.Instance;
return dm.BookQueries.LocalGet(CommandId.Value); return dm.BookQueries.LocalGet(CommandId.Value);
} }
return null; return null;
@ -67,7 +82,7 @@ namespace BookAStar.Model.Workflow
{ {
get get
{ {
return DataManager.Current.Contacts.LocalGet(ClientId); return DataManager.Instance.Contacts.LocalGet(ClientId);
} }
} }
@ -77,9 +92,14 @@ namespace BookAStar.Model.Workflow
return Bill?.Aggregate((decimal)0, (t, l) => t + l.Count * l.UnitaryCost) ?? (decimal)0; return Bill?.Aggregate((decimal)0, (t, l) => t + l.Count * l.UnitaryCost) ?? (decimal)0;
} }
} }
/// <summary>
public DateTime LatestValidationDate { get; set; } /// This validation comes first from the provider.
/// </summary>
public DateTime ProviderValidationDate { get; set; }
/// <summary>
/// Date for the agreement from the client
/// </summary>
public DateTime ClientApprouvalDate { get; set; } public DateTime ClientApprouvalDate { get; set; }
} }
} }

View File

@ -1,15 +1,14 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms" <TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="BookAStar.Pages.ChatPage" x:Class="BookAStar.Pages.Chat.ChatPage"
xmlns:views="clr-namespace:BookAStar.Views;assembly=BookAStar" xmlns:views="clr-namespace:BookAStar.Views;assembly=BookAStar"
xmlns:controls="clr-namespace:XLabs.Forms.Controls;assembly=XLabs.Forms" xmlns:controls="clr-namespace:XLabs.Forms.Controls;assembly=XLabs.Forms"
xmlns:toolkit="clr-namespace:XLabs.Forms.Mvvm;assembly=XLabs.Forms" xmlns:toolkit="clr-namespace:XLabs.Forms.Mvvm;assembly=XLabs.Forms"
xmlns:converters="clr-namespace:BookAStar.Converters;assembly=BookAStar" xmlns:converters="clr-namespace:BookAStar.Converters;assembly=BookAStar"
xmlns:local="clr-namespace:BookAStar;Assembly:BookAStar" xmlns:local="clr-namespace:BookAStar;Assembly:BookAStar"
xmlns:extensions="clr-namespace:BookAStar.Extensions;assembly=BookAStar" xmlns:extensions="clr-namespace:BookAStar.Extensions;assembly=BookAStar"
> Style="{StaticResource PageStyle}" >
<TabbedPage.Resources> <TabbedPage.Resources>
<ResourceDictionary> <ResourceDictionary>
<converters:SignalRConnectionStateToObject x:Key="cxToStyleImage" x:TypeArguments="Style"> <converters:SignalRConnectionStateToObject x:Key="cxToStyleImage" x:TypeArguments="Style">
@ -100,19 +99,14 @@
<ContentPage Title="Contacts" Icon="peer_to_peer.png" > <ContentPage Title="Contacts" Icon="peer_to_peer.png" >
<StackLayout Padding="5, 5, 5, 5"> <StackLayout Padding="5, 5, 5, 5">
<StackLayout Spacing = "12" <StackLayout Spacing = "12"
Orientation = "Horizontal" Orientation = "Horizontal">
Padding="5, 0, 5, 0">
<Entry x:Name="pvEntry" Placeholder = "enter your private message"
VerticalOptions = "Center"
HorizontalOptions = "FillAndExpand"></Entry>
<!--
<controls:ExtendedPicker x:Name="contactPicker" Display="{Binding UserName}"></controls:ExtendedPicker>
-->
<controls:ExtendedPicker x:Name="contactPicker" ItemsSource="{Binding Contacts}" />
<Button x:Name="sendPVButton" Text = "Send" HorizontalOptions = "End"
VerticalOptions = "Center"></Button>
</StackLayout> </StackLayout>
<ListView x:Name="PVList" HasUnevenRows="true" ItemsSource="{Binding PVs}">
<views:UserListView x:Name="chatUserList" />
<StackLayout BindingContext="{x:Reference Name=chatUserList}" IsVisible="{Binding HasASelection}">
<ListView x:Name="pvList" HasUnevenRows="true" ItemsSource="{Binding SelectedUser.PrivateMessages}" BindingContext="{x:Reference Name=chatUserList}">
<ListView.ItemTemplate HeightRequest="80" <ListView.ItemTemplate HeightRequest="80"
VerticalOptions="StartAndExpand"> VerticalOptions="StartAndExpand">
<DataTemplate> <DataTemplate>
@ -129,7 +123,13 @@
</DataTemplate> </DataTemplate>
</ListView.ItemTemplate> </ListView.ItemTemplate>
</ListView> </ListView>
<!-- <views:UserListView ItemsSource="" /> -->
<Entry x:Name="pvEntry" Placeholder = "enter your private message"
VerticalOptions = "Center" HorizontalOptions = "FillAndExpand"></Entry>
<Button x:Name="sendPVButton" Text = "Send" HorizontalOptions = "End"
VerticalOptions = "Center"></Button>
</StackLayout>
</StackLayout> </StackLayout>
</ContentPage> </ContentPage>

View File

@ -3,7 +3,7 @@ using System.Diagnostics;
using Microsoft.AspNet.SignalR.Client; using Microsoft.AspNet.SignalR.Client;
using Xamarin.Forms; using Xamarin.Forms;
namespace BookAStar.Pages namespace BookAStar.Pages.Chat
{ {
using Data; using Data;
using System.Linq; using System.Linq;
@ -12,14 +12,28 @@ namespace BookAStar.Pages
public partial class ChatPage : TabbedPage public partial class ChatPage : TabbedPage
{ {
public string ChatUser { get; set; } public string ChatUser { get; set; }
public ChatPage(ChatViewModel model)
{
Init();
BindingContext = model;
}
public ChatPage() public ChatPage()
{
Init();
}
private void Init()
{ {
InitializeComponent(); InitializeComponent();
Title = "Chat"; Title = "Chat";
BindingContext = new ChatViewModel(); /*
ToolbarItems.Add(new ToolbarItem(
name: "...",
icon: null,
activated: () => { })); */
App.ChatHubConnection.StateChanged += ChatHubConnection_StateChanged;
sendButton.Clicked += async (sender, args) => sendButton.Clicked += async (sender, args) =>
{ {
IsBusy = true; IsBusy = true;
@ -38,26 +52,30 @@ namespace BookAStar.Pages
IsBusy = false; IsBusy = false;
}; };
chatUserList.BindingContext = DataManager.Instance.ChatUsers;
sendPVButton.Clicked += async (sender, args) =>
sendPVButton.Clicked += (sender, args) =>
{ {
string userName = contactPicker.SelectedItem as string; var dest = chatUserList.SelectedUser;
if (string.IsNullOrEmpty(userName)) return; if (dest!=null)
var user = DataManager.Current.Contacts.Single(
c => c.UserName == userName);
if (string.IsNullOrEmpty(user.ChatHubConnectionId)) return;
IsBusy = true;
try
{ {
await App.ChatHubProxy.Invoke<string>("SendPV", user.ChatHubConnectionId, pvEntry.Text); IsBusy = true;
pvEntry.Text = null; try
{
foreach (var cx in dest.ObservableConnections)
{
if (cx.Connected)
App.ChatHubProxy.Invoke<string>("SendPV", cx.ConnectionId, pvEntry.Text);
}
pvEntry.Text = null;
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
IsBusy = false;
} }
catch (Exception ex)
{
Debug.WriteLine(ex);
}
IsBusy = false;
}; };
} }

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="BookAStar.Pages.ClientPages.SearchPage"
xmlns:views="clr-namespace:BookAStar.Views;assembly=BookAStar"
xmlns:controls="clr-namespace:XLabs.Forms.Controls;assembly=XLabs.Forms"
xmlns:toolkit="clr-namespace:XLabs.Forms.Mvvm;assembly=XLabs.Forms"
xmlns:converters="clr-namespace:BookAStar.Converters;assembly=BookAStar"
xmlns:local="clr-namespace:BookAStar;Assembly:BookAStar"
xmlns:extensions="clr-namespace:BookAStar.Extensions;assembly=BookAStar"
Style="{StaticResource PageStyle}" >
<TabbedPage.Children>
<ContentPage Title="Une star" Icon="">
<StackLayout Orientation="Horizontal">
<Editor x:Name="search_phrase" HorizontalOptions="FillAndExpand"/>
<Button x:Name="btn_update" HorizontalOptions="End" />
</StackLayout>
<DatePicker x:Name="search_date" />
</ContentPage>
</TabbedPage.Children>
<TabbedPage.Children>
<ContentPage Title="Un DJ" Icon="">
<StackLayout Orientation="Horizontal">
<Editor x:Name="search_phrase" HorizontalOptions="FillAndExpand"/>
<Button x:Name="btn_update" HorizontalOptions="End" />
</StackLayout>
<DatePicker x:Name="search_date" />
</ContentPage>
</TabbedPage.Children>
<TabbedPage.Children>
<ContentPage Title="Un chanteur" Icon="">
<StackLayout Orientation="Horizontal">
<Editor x:Name="search_phrase" HorizontalOptions="FillAndExpand"/>
<Button x:Name="btn_update" HorizontalOptions="End" />
</StackLayout>
<DatePicker x:Name="search_date" />
</ContentPage>
</TabbedPage.Children>
<TabbedPage.Children>
<ContentPage Title="Une formation musicale" Icon="">
<StackLayout Orientation="Horizontal">
<Editor x:Name="search_phrase" HorizontalOptions="FillAndExpand"/>
<Button x:Name="btn_update" HorizontalOptions="End" />
</StackLayout>
<DatePicker x:Name="search_date" />
</ContentPage>
</TabbedPage.Children>
</TabbedPage>

View File

@ -6,11 +6,11 @@ using System.Threading.Tasks;
using Xamarin.Forms; using Xamarin.Forms;
namespace BookAStar.Pages namespace BookAStar.Pages.ClientPages
{ {
public partial class HomePage : ContentPage public partial class SearchPage : TabbedPage
{ {
public HomePage() public SearchPage()
{ {
InitializeComponent(); InitializeComponent();
} }

View File

@ -3,7 +3,8 @@
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:signature="clr-namespace:SignaturePad.Forms;assembly=SignaturePad.Forms" xmlns:signature="clr-namespace:SignaturePad.Forms;assembly=SignaturePad.Forms"
xmlns:views="clr-namespace:BookAStar.Views;assembly=BookAStar" xmlns:views="clr-namespace:BookAStar.Views;assembly=BookAStar"
x:Class="BookAStar.ViewModels.Signing.DocSigning"> x:Class="BookAStar.ViewModels.Signing.Signing"
Style="{StaticResource PageStyle}">
<StackLayout> <StackLayout>
<views:MarkdownView x:Name="mdView" <views:MarkdownView x:Name="mdView"
Editable="False" Editable="False"

View File

@ -9,9 +9,9 @@ using Xamarin.Forms;
namespace BookAStar.ViewModels.Signing namespace BookAStar.ViewModels.Signing
{ {
public partial class DocSigning : ContentPage public partial class Signing : ContentPage
{ {
public DocSigning() public Signing()
{ {
InitializeComponent(); InitializeComponent();
} }

View File

@ -30,29 +30,34 @@
<ScrollView> <ScrollView>
<StackLayout Padding="10,10,10,10" x:Name="mainLayout"> <StackLayout Padding="10,10,10,10" x:Name="mainLayout">
<ListView RefreshCommand="{Binding RefreshQueries}" IsPullToRefreshEnabled="True" <ListView RefreshCommand="{Binding RefreshQueries}" IsPullToRefreshEnabled="True"
ItemsSource="{Binding Queries}" x:Name="list" ItemTapped="OnViewDetail" HasUnevenRows="true" RowHeight="80"> ItemsSource="{Binding Queries}" x:Name="list" ItemTapped="OnViewDetail" HasUnevenRows="true"
<ListView.ItemTemplate HeightRequest="80" VerticalOptions="StartAndExpand"> SeparatorVisibility="Default" SeparatorColor="Black">
<ListView.ItemTemplate VerticalOptions="StartAndExpand">
<DataTemplate> <DataTemplate>
<ViewCell> <ViewCell>
<ViewCell.View> <ViewCell.View>
<StackLayout Orientation="Horizontal" Padding="10,10,10,10" VerticalOptions="StartAndExpand"> <Grid>
<StackLayout Orientation="Vertical" <Grid.RowDefinitions>
HeightRequest="80" VerticalOptions="StartAndExpand"> <RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Row="0" Grid.Column="0" Grid.RowSpan="3" Source="{Binding Avatar}" />
<Label Grid.Row="3" Grid.Column="0" Grid.RowSpan="2" Text="{Binding Client.UserName}" Style="{StaticResource labelStyle}"></Label>
<Label Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2" Grid.RowSpan="2" Text="{Binding Data.Reason}"></Label>
<Label Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" LineBreakMode="WordWrap" Text="{Binding EventDate, StringFormat='{0:dddd d MMMM à HH:mm}'}" FontFamily="Italic"/>
<Label Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2" LineBreakMode="WordWrap" Text="{Binding Location.Address}"/>
<Label Grid.Row="4" Grid.Column="1" Text="{Binding Previsionnal}" />
<Label Grid.Row="4" Grid.Column="2" Text="{Binding Id}" />
</Grid>
<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.View>
</ViewCell> </ViewCell>
</DataTemplate> </DataTemplate>

View File

@ -14,20 +14,34 @@ namespace BookAStar.Pages
public BookQueriesPage() public BookQueriesPage()
{ {
InitializeComponent(); InitializeComponent();
var model = new BookQueriesViewModel(); BindingContext = new BookQueriesViewModel();
model.RefreshQueries = }
new Command( () => {
DataManager.Current.BookQueries.Execute(null);
this.list.EndRefresh();
});
public BookQueriesPage(BookQueriesViewModel model)
{
InitializeComponent();
BindingContext = model; BindingContext = model;
} }
protected override void OnBindingContextChanged()
{
BookQueriesViewModel model = (BookQueriesViewModel) BindingContext;
if (model!=null)
{
model.RefreshQueries =
new Command(() =>
{
DataManager.Instance.BookQueries.Execute(null);
this.list.EndRefresh();
});
}
base.OnBindingContextChanged();
}
private void OnViewDetail(object sender, ItemTappedEventArgs e) private void OnViewDetail(object sender, ItemTappedEventArgs e)
{ {
BookQueryData data = e.Item as BookQueryData; var item = e.Item as BookQueryViewModel;
App.NavigationService.NavigateTo<BookQueryPage>(true,data); App.NavigationService.NavigateTo<BookQueryPage>(true,item);
} }
} }
} }

View File

@ -19,20 +19,24 @@
<StackLayout x:Name="bookQueryLayout"> <StackLayout x:Name="bookQueryLayout">
<StackLayout Orientation="Vertical"> <StackLayout Orientation="Vertical">
<Image Source="{Binding Client.AvatarOrNot}" Aspect="AspectFit" VisualElement.HeightRequest="{StaticResource BigUserAvatarSize}"/> <Image Source="{Binding Avatar}" Aspect="AspectFit" VisualElement.HeightRequest="{StaticResource BigUserAvatarSize}"/>
<Label Text="{Binding Client.UserName}" /> <Label Text="{Binding Client.UserName}" />
<Label Text="{Binding EventDate, StringFormat='le {0:dddd d MMMM yyyy à hh:mm}'}" /> <Label Text="{Binding EventDate, StringFormat='le {0:dddd d MMMM yyyy à hh:mm}'}" />
<Label Text="{Binding Data.Reason}" />
<Label Text="{Binding Location.Address}" /> <Label Text="{Binding Location.Address}" />
<Label Text="{Binding Previsional}" /> <Label Text="{Binding Previsional}" />
</StackLayout> </StackLayout>
<StackLayout Orientation="Vertical" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" > <StackLayout Orientation="Vertical" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" >
<maps:Map x:Name="map" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"></maps:Map> <maps:Map x:Name="map" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"></maps:Map>
<StackLayout Orientation="Horizontal"> <StackLayout Orientation="Horizontal">
<Button Text="{Binding EditEstimateButtonText}" Clicked="OnEditEstimate" />
<Button Text="{x:Static local:Strings.DeclineQuery}" Clicked="OnDropQuery" />
<Button Text="{x:Static local:Strings.BlockThisUser}" Clicked="OnBlockThisUser" />
<Button Text="{x:Static local:Strings.ViewEstimate}" <Button Text="{x:Static local:Strings.ViewEstimate}"
BorderRadius="50" BorderWidth="2" BorderColor="Aqua" x:Name ="btn" BorderRadius="50" BorderWidth="2" BorderColor="Aqua" x:Name ="btn"
IsEnabled="{Binding EstimationDone}" VisualElement.IsVisible="{Binding EstimationDone}"
Clicked="OnViewEstimate" Image="exclam.png" /> Clicked="OnViewEstimate" Image="exclam.png" />
<Button Text="{Binding EditEstimateButtonText}" Clicked="OnEditEstimate" />
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>

View File

@ -7,14 +7,14 @@ namespace BookAStar.Pages
{ {
using Data; using Data;
using EstimatePages; using EstimatePages;
using Model; using Model.Social;
using Model.Workflow; using Model.Workflow;
using ViewModels.EstimateAndBilling; using ViewModels.EstimateAndBilling;
public partial class BookQueryPage : ContentPage public partial class BookQueryPage : ContentPage
{ {
public BookQueryData BookQuery public BookQuery BookQuery
{ {
get get
{ {
@ -32,14 +32,14 @@ namespace BookAStar.Pages
var pin = new Pin var pin = new Pin
{ {
Type = PinType.SavedPin, Type = PinType.SavedPin,
Position = new Position( Position = new Xamarin.Forms.Maps.Position
lat, lon), (lat, lon),
Label = BookQuery.Client.UserName, Label = BookQuery.Client.UserName,
Address = BookQuery.Location.Address Address = BookQuery.Location.Address
}; };
map.Pins.Add(pin); map.Pins.Add(pin);
map.MoveToRegion(MapSpan.FromCenterAndRadius( map.MoveToRegion(MapSpan.FromCenterAndRadius(
new Position(lat, lon), Distance.FromMeters(100))); new Xamarin.Forms.Maps.Position(lat, lon), Distance.FromMeters(100)));
} }
} }
@ -47,14 +47,14 @@ namespace BookAStar.Pages
{ {
InitializeComponent(); InitializeComponent();
} }
public BookQueryPage(BookQueryData bookQuery=null) public BookQueryPage(BookQueryViewModel bookQuery =null)
{ {
InitializeComponent(); InitializeComponent();
// when TODO update?
// Task.Run( async () => { bookQuery = await App.CurrentApp.DataManager.BookQueries.Get(bookQueryId); }); // Task.Run( async () => { bookQuery = await App.CurrentApp.DataManager.BookQueries.Get(bookQueryId); });
BindingContext = new BookQueryViewModel(bookQuery); BindingContext = bookQuery;
} }
private void OnEditEstimate(object sender, EventArgs ev) private void OnEditEstimate(object sender, EventArgs ev)
@ -65,31 +65,25 @@ namespace BookAStar.Pages
if (editEstimateViewModel == null) if (editEstimateViewModel == null)
{ {
// First search for an existing estimate // First search for an existing estimate
var estimateToEdit = DataManager.Current.Estimates.FirstOrDefault( editEstimateViewModel = DataManager.Instance.EstimationCache.FirstOrDefault(
estimate=> estimate.CommandId == bookQueryViewModel.Id estimate=> estimate.Query.Id == bookQueryViewModel.Id
); );
if (estimateToEdit == null) if (editEstimateViewModel == null)
{ {
DataManager.Current.Contacts.Merge(BookQuery.Client); DataManager.Instance.Contacts.Merge(BookQuery.Client);
DataManager.Current.Contacts.SaveEntity(); DataManager.Instance.Contacts.SaveEntity();
estimateToEdit = new Estimate editEstimateViewModel = new EditEstimateViewModel( new Estimate
{ {
ClientId = BookQuery.Client.UserId, ClientId = BookQuery.Client.UserId,
CommandId = BookQuery.Id, CommandId = BookQuery.Id,
OwnerId = MainSettings.CurrentUser.Id, OwnerId = MainSettings.CurrentUser.Id,
Id = 0 Id = 0
}; });
editEstimateViewModel = new EditEstimateViewModel(estimateToEdit); DataManager.Instance.EstimationCache.Add(editEstimateViewModel);
} }
else
editEstimateViewModel = new EditEstimateViewModel(estimateToEdit);
DataManager.Current.EstimationCache.Add(editEstimateViewModel);
} }
App.NavigationService.NavigateTo<EditEstimatePage>(true, App.NavigationService.NavigateTo<EditEstimatePage>(true,
editEstimateViewModel); editEstimateViewModel);
} }
private async void OnViewEstimate(object sender, EventArgs ev) private async void OnViewEstimate(object sender, EventArgs ev)
@ -118,5 +112,13 @@ namespace BookAStar.Pages
} }
base.OnSizeAllocated(width, height); base.OnSizeAllocated(width, height);
} }
private void OnBlockThisUser(object sender, EventArgs ev)
{
throw new NotImplementedException();
}
private void OnDropQuery(object sender, EventArgs ev)
{
throw new NotImplementedException();
}
} }
} }

View File

@ -44,12 +44,16 @@
<StackLayout x:Name="mainStackLayout"> <StackLayout x:Name="mainStackLayout">
<Label Text="Description de la ligne de facture" <Label Text="Description de la ligne de facture"
Style="{StaticResource InputLabelStyle}"></Label> Style="{StaticResource InputLabelStyle}"></Label>
<StackLayout Orientation="Horizontal" VerticalOptions="FillAndExpand"> <Editor HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Text="{Binding Description, Mode=TwoWay}">
<Editor HorizontalOptions="FillAndExpand" Text="{Binding Description, Mode=TwoWay}">
<Editor.Behaviors> <Editor.Behaviors>
<behaviors:EditorMaxLengthValidator x:Name="descriptionValidator" MaxLength="512" /> <behaviors:EditorMaxLengthValidator x:Name="descriptionLenValidator" MaxLength="12" MinLength="3" />
</Editor.Behaviors> </Editor.Behaviors>
</Editor> </Editor>
<StackLayout Orientation="Horizontal">
<Image x:Name="descriptionSuccessErrorImage"
Style="{Binding Source={x:Reference descriptionLenValidator}, Path=IsValid, Converter={StaticResource boolToStyleImage}}" />
<Label Text="{Binding Source={x:Reference descriptionLenValidator}, Path=Error}"
Style="{StaticResource ErrorLabelStyle}"></Label>
</StackLayout> </StackLayout>
<Label Text="Durée de la prestation" <Label Text="Durée de la prestation"
@ -74,18 +78,21 @@
</StackLayout> </StackLayout>
<Label Text="Quantité facturée" Style="{StaticResource InputLabelStyle}"></Label> <Label Text="Quantité facturée" Style="{StaticResource InputLabelStyle}"></Label>
<StackLayout Orientation="Horizontal">
<Entry Text="{Binding Count, Mode=TwoWay}" Placeholder="Quantité" Keyboard="Numeric" <Entry Text="{Binding Count, Mode=TwoWay}" Placeholder="Quantité" Keyboard="Numeric"
Style="{StaticResource BigEntry}"> Style="{StaticResource BigEntry}">
<Entry.Behaviors> <Entry.Behaviors>
<behaviors:IntegerEntryBehavior x:Name="countValidator" Min="0" Max="10" /> <behaviors:IntegerEntryBehavior x:Name="countValidator" Min="0" Max="10" />
</Entry.Behaviors> </Entry.Behaviors>
</Entry> </Entry>
<Image x:Name="countSuccessErrorImage" <StackLayout Orientation="Horizontal">
<Image x:Name="countSuccessErrorImage"
Style="{Binding Source={x:Reference countValidator}, Style="{Binding Source={x:Reference countValidator},
Path=IsValid, Path=IsValid,
Converter={StaticResource boolToStyleImage}}" /> Converter={StaticResource boolToStyleImage}}" />
</StackLayout> <Label Text="{Binding Source={x:Reference countValidator}, Path=Error}"
Style="{StaticResource ErrorLabelStyle}"></Label>
</StackLayout>
<Label Text="Prix unitaire" Style="{StaticResource InputLabelStyle}"></Label> <Label Text="Prix unitaire" Style="{StaticResource InputLabelStyle}"></Label>
<StackLayout Orientation="Horizontal"> <StackLayout Orientation="Horizontal">
@ -95,7 +102,7 @@
<behaviors:DecimalValidatorBehavior x:Name="unitCostValidator" /> <behaviors:DecimalValidatorBehavior x:Name="unitCostValidator" />
</Entry.Behaviors> </Entry.Behaviors>
</Entry> </Entry>
<Label Text="€" Style="{StaticResource BigLabel}" /> <Label Text="€" Style="{StaticResource BigLabelStyle}" />
<Image x:Name="unitaryCostSuccessErrorImage" <Image x:Name="unitaryCostSuccessErrorImage"
Style="{Binding Source={x:Reference unitCostValidator}, Style="{Binding Source={x:Reference unitCostValidator},
Path=IsValid, Path=IsValid,
@ -106,7 +113,7 @@
Command="{Binding ValidateCommand}" Command="{Binding ValidateCommand}"
Clicked="OnValidateClicked"></Button> Clicked="OnValidateClicked"></Button>
<Button Text="Supprimer" <Button Text="Supprimer"
Command="{Binding DeleteCommand}" Command="{Binding RemoveCommand}"
Clicked="OnDeleteClicked"></Button> Clicked="OnDeleteClicked"></Button>
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>

View File

@ -17,14 +17,17 @@ namespace BookAStar.Pages
picker.Items.Add(du); picker.Items.Add(du);
BindingContext = model; BindingContext = model;
} }
public void OnDeleteClicked(object sender, EventArgs e) public void OnDeleteClicked(object sender, EventArgs e)
{ {
throw new NotImplementedException(); this.Navigation.PopAsync();
} }
public void OnValidateClicked (object sender, EventArgs e) public void OnValidateClicked (object sender, EventArgs e)
{ {
this.Navigation.PopAsync(); this.Navigation.PopAsync();
} }
protected override bool OnBackButtonPressed() protected override bool OnBackButtonPressed()
{ {
var bvm = (BillingLineViewModel)BindingContext; var bvm = (BillingLineViewModel)BindingContext;

View File

@ -2,8 +2,11 @@
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="BookAStar.Pages.EditEstimatePage" x:Class="BookAStar.Pages.EditEstimatePage"
xmlns:converters="clr-namespace:BookAStar.Converters;assembly=BookAStar"
xmlns:views="clr-namespace:BookAStar.Views;assembly=BookAStar" xmlns:views="clr-namespace:BookAStar.Views;assembly=BookAStar"
xmlns:local="clr-namespace:BookAStar;assembly=BookAStar" xmlns:local="clr-namespace:BookAStar;assembly=BookAStar"
xmlns:behaviors="clr-namespace:BookAStar.Behaviors;assembly=BookAStar"
xmlns:extensions="clr-namespace:BookAStar.Extensions;assembly=BookAStar"
Style="{StaticResource PageStyle}"> Style="{StaticResource PageStyle}">
<ContentPage.Resources> <ContentPage.Resources>
<ResourceDictionary> <ResourceDictionary>
@ -13,14 +16,32 @@
<Style TargetType="Button"> <Style TargetType="Button">
<Setter Property="Style" Value="{StaticResource ButtonStyle}" /> <Setter Property="Style" Value="{StaticResource ButtonStyle}" />
</Style> </Style>
<converters:BooleanToObjectConverter x:Key="boolToStyleImage" x:TypeArguments="Style">
<converters:BooleanToObjectConverter.FalseObject>
<Style TargetType="Image">
<Setter Property="HeightRequest" Value="20" />
<Setter Property="Source"
Value="{extensions:ImageResource BookAStar.Images.Validation.error.png}" />
</Style>
</converters:BooleanToObjectConverter.FalseObject>
<converters:BooleanToObjectConverter.TrueObject>
<Style TargetType="Image">
<Setter Property="HeightRequest" Value="20" />
<Setter Property="Source"
Value="{extensions:ImageResource BookAStar.Images.Validation.success.png}" />
</Style>
</converters:BooleanToObjectConverter.TrueObject>
</converters:BooleanToObjectConverter>
</ResourceDictionary> </ResourceDictionary>
</ContentPage.Resources> </ContentPage.Resources>
<ContentPage.Content> <ContentPage.Content>
<ScrollView>
<StackLayout Padding="10,10,10,10" x:Name="mainLayout"> <StackLayout Padding="10,10,10,10" x:Name="mainLayout">
<Grid MinimumHeightRequest="12"> <Grid HeightRequest="120">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="*" /> <RowDefinition Height="2*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
@ -34,18 +55,20 @@
<Entry Placeholder="Saisissez un titre pour ce devis" Text="{Binding Title, Mode=TwoWay}" /> <Entry Placeholder="Saisissez un titre pour ce devis" Text="{Binding Title, Mode=TwoWay}" />
<views:MarkdownView x:Name="mdview" <views:MarkdownView x:Name="mdview"
Editable="True" Editable="True"
HorizontalOptions="FillAndExpand" Markdown="{Binding Description, Mode=TwoWay}">
Markdown="{Binding Description, Mode=TwoWay}" <views:MarkdownView.Behaviors>
VerticalOptions="Start" /> <behaviors:MarkdownViewLengthValidator x:Name="descriptionLenValidator" MaxLength="512" MinLength="3" />
</views:MarkdownView.Behaviors>
</views:MarkdownView>
<StackLayout x:Name="biAnVaLayout"> <StackLayout x:Name="biAnVaLayout">
<ListView x:Name="BillListView" ItemsSource="{Binding Bill}" <ListView x:Name="BillListView" ItemsSource="{Binding Bill}"
MinimumHeightRequest="40" HasUnevenRows="true" VerticalOptions="FillAndExpand" HasUnevenRows="true" ItemTapped="OnEditLine">
HeightRequest="40" ItemTapped="OnEditLine">
<ListView.ItemTemplate> <ListView.ItemTemplate>
<DataTemplate > <DataTemplate >
<ViewCell> <ViewCell>
<ViewCell.View> <ViewCell.View Padding="5,5,5,5" >
<Grid MinimumHeightRequest="12"> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="*" /> <RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
@ -54,6 +77,7 @@
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="30" /> <ColumnDefinition Width="30" />
<ColumnDefinition Width="90" /> <ColumnDefinition Width="90" />
<ColumnDefinition Width="16" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Label Text="{Binding Description}" <Label Text="{Binding Description}"
Grid.Row="0" Grid.Column="0" ></Label> Grid.Row="0" Grid.Column="0" ></Label>
@ -64,6 +88,9 @@
<Label Text="{Binding UnitaryCost}" <Label Text="{Binding UnitaryCost}"
Grid.Row="0" Grid.Column="3" Grid.Row="0" Grid.Column="3"
FontFamily="Monospace"></Label> FontFamily="Monospace"></Label>
<Image Grid.Row="0" Grid.Column="4" Style="{Binding
Path=ViewModelState.IsValid,
Converter={StaticResource boolToStyleImage}}" ></Image>
</Grid> </Grid>
</ViewCell.View> </ViewCell.View>
</ViewCell> </ViewCell>
@ -73,10 +100,9 @@
<StackLayout Orientation="Vertical"> <StackLayout Orientation="Vertical">
<Button Text="Ajouter une ligne de facture" Clicked="OnNewCommanLine"></Button> <Button Text="Ajouter une ligne de facture" Clicked="OnNewCommanLine"></Button>
<Label FormattedText="{Binding FormattedTotal}"/> <Label FormattedText="{Binding FormattedTotal}"/>
<Button Text="Valider ce devis" Clicked="OnEstimateValidated" ></Button> <Button x:Name="btnValidate" Text="Valider ce devis" Clicked="OnEstimateValidated" IsEnabled="{Binding ViewModelState.IsValid}" ></Button>
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
</ScrollView>
</ContentPage.Content> </ContentPage.Content>
</ContentPage> </ContentPage>

View File

@ -4,16 +4,36 @@ using Xamarin.Forms;
namespace BookAStar.Pages namespace BookAStar.Pages
{ {
using Data; using Data;
using EstimatePages;
using Model.Workflow; using Model.Workflow;
using ViewModels.EstimateAndBilling; using ViewModels.EstimateAndBilling;
using ViewModels.Signing;
public partial class EditEstimatePage : ContentPage public partial class EditEstimatePage : ContentPage
{ {
public EditEstimateViewModel Model
{
get
{
return (EditEstimateViewModel)BindingContext;
}
}
public EditEstimatePage(EditEstimateViewModel model) public EditEstimatePage(EditEstimateViewModel model)
{ {
InitializeComponent();
BindingContext = model; BindingContext = model;
Model.CheckCommand = new Action<Estimate, ViewModels.Validation.ModelState>(
(e, m) =>
{
foreach (var line in model.Bill)
{
line.Check();
if (!line.ViewModelState.IsValid)
model.ViewModelState.AddError("Bill", "invalid line");
}
});
InitializeComponent();
Model.Check();
} }
protected override void OnBindingContextChanged() protected override void OnBindingContextChanged()
@ -24,7 +44,7 @@ namespace BookAStar.Pages
private void EditEstimatePage_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) private void EditEstimatePage_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{ {
DataManager.Current.EstimationCache.SaveEntity(); DataManager.Instance.EstimationCache.SaveEntity();
} }
protected override void OnSizeAllocated(double width, double height) protected override void OnSizeAllocated(double width, double height)
@ -46,54 +66,62 @@ namespace BookAStar.Pages
var bill = ((EditEstimateViewModel)BindingContext).Bill; var bill = ((EditEstimateViewModel)BindingContext).Bill;
var lineView = new BillingLineViewModel(com) var lineView = new BillingLineViewModel(com)
{ ValidateCommand = new Command(() => { { ValidateCommand = new Command(() => {
bill.Add(com); bill.Add(new BillingLineViewModel(com));
DataManager.Current.EstimationCache.SaveEntity(); DataManager.Instance.EstimationCache.SaveEntity();
})}; })};
lineView.PropertyChanged += LineView_PropertyChanged;
App.NavigationService.NavigateTo<EditBillingLinePage>( App.NavigationService.NavigateTo<EditBillingLinePage>(
true, lineView ); true, lineView );
} }
protected void OnEditLine(object sender, ItemTappedEventArgs e) protected void OnEditLine(object sender, ItemTappedEventArgs e)
{ {
var line = (BillingLine)e.Item;
var bill = ((EditEstimateViewModel)BindingContext).Bill;
var lineView = new BillingLineViewModel(line)
{
ValidateCommand = new Command(() => {
DataManager.Current.EstimationCache.SaveEntity();
})
};
lineView.PropertyChanged += LineView_PropertyChanged;
lineView.PropertyChanged += LineView_PropertyChanged; var line = (BillingLineViewModel)e.Item;
var evm = ((EditEstimateViewModel)BindingContext);
// update the validation command, that
// was creating a new line in the bill at creation time,
// now one only wants to update the line
line.ValidateCommand = new Command(() =>
{
evm.Check();
DataManager.Instance.EstimationCache.SaveEntity();
});
// and setup a removal command, that was not expected at creation time
line.RemoveCommand = new Command(() =>
{
evm.Bill.Remove(line);
evm.Check();
DataManager.Instance.EstimationCache.SaveEntity();
});
App.NavigationService.NavigateTo<EditBillingLinePage>( App.NavigationService.NavigateTo<EditBillingLinePage>(
true, lineView ); true, line );
BillListView.SelectedItem = null;
} }
protected async void OnEstimateValidated(object sender, EventArgs e)
private void LineView_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{ {
DataManager.Current.EstimationCache.SaveEntity();
}
protected void OnEstimateValidated(object sender, EventArgs e) var thisPage = this;
{ var evm = (EditEstimateViewModel) BindingContext;
var evm = (EditEstimateViewModel)BindingContext; var cmd = new Command<bool>( async (validated) =>
if (evm.Data.Id == 0)
{ {
DataManager.Current.Estimates.Create(evm.Data); if (validated) {
// we have to manually add this item in our local collection, DataManager.Instance.EstimationCache.Remove(evm);
// since we could prefer to update the whole collection DataManager.Instance.EstimationCache.SaveEntity();
// from server, or whatever other scenario }
DataManager.Current.Estimates.Add(evm.Data); await thisPage.Navigation.PopAsync();
} else });
var response = await App.DisplayActionSheet(
Strings.SignOrNot, Strings.DonotsignEstimate,
Strings.CancelValidation, new string[] { Strings.Sign });
if (response == Strings.Sign)
{ {
DataManager.Current.Estimates.Update(evm.Data); App.NavigationService.NavigateTo<EstimateSigningPage>(true,
new EstimateSigningViewModel(evm.Data) { ValidationCommand = cmd });
} }
DataManager.Current.Estimates.SaveEntity(); else if (response == Strings.CancelValidation)
DataManager.Current.EstimationCache.Remove(evm); return;
DataManager.Current.EstimationCache.SaveEntity(); else cmd.Execute(true);
Navigation.PopAsync();
} }
} }
} }

View File

@ -0,0 +1,93 @@
<?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.Pages.EstimatePages.EstimateSigningPage">
<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>
<StackLayout Padding="10,10,10,10" x:Name="mainLayout">
<ScrollView>
<StackLayout>
<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}" Style="{StaticResource BigLabelStyle}" />
<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 Orientation="Horizontal">
<Image Source="{Binding ProSignImage}" HorizontalOptions="Start"></Image>
<Image Source="{Binding CliSignImage}" HorizontalOptions="End"></Image>
</StackLayout>
</StackLayout>
</ScrollView>
<signature:SignaturePadView x:Name="padView"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
BackgroundColor="White"
CaptionText="{Binding Data.Owner.UserName}" CaptionTextColor="Black"
ClearText="Effacer!" ClearTextColor="Red"
PromptText="Prompt Here" PromptTextColor="Red"
SignatureLineColor="Aqua" StrokeColor="Black" StrokeWidth="2" />
<Button Clicked="OnValidate" Text="Valider cette signature" x:Name="btnValidate" />
</StackLayout>
</ContentPage.Content>
</ContentPage>

View File

@ -0,0 +1,74 @@
using SignaturePad.Forms;
using System;
using System.IO;
using System.Linq;
using Xamarin.Forms;
namespace BookAStar.Pages.EstimatePages
{
using Data;
using ViewModels.EstimateAndBilling;
using ViewModels.Signing;
public partial class EstimateSigningPage : ContentPage
{
public EstimateSigningPage(EstimateSigningViewModel model)
{
InitializeComponent();
BindingContext = model;
}
private async void OnValidate (object sender, EventArgs ev)
{
btnValidate.IsEnabled = false;
if (DataManager.Instance.Estimates.IsExecuting)
{
await App.DisplayAlert(Strings.OperationPending, Strings.oups);
return;
}
var evm = (EditEstimateViewModel)BindingContext;
var estimate = evm.Data;
var pngStream = await padView.GetImageStreamAsync(SignatureImageFormat.Png);
pngStream.Seek(0, SeekOrigin.Begin);
DataManager.Instance.Estimates.SignAsProvider(estimate, pngStream);
DataManager.Instance.Estimates.SaveEntity();
await Navigation.PopAsync();
var ParentValidationCommand = ((EstimateSigningViewModel)BindingContext).ValidationCommand;
if (ParentValidationCommand != null)
ParentValidationCommand.Execute(true);
}
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

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

View File

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="BookAStar.Pages.EstimatesProviderPage"> x:Class="BookAStar.Pages.EstimatesProviderPage"
Style="{StaticResource PageStyle}">
<ScrollView> <ScrollView>
<StackLayout Padding="10,10,10,10" x:Name="mainLayout"> <StackLayout Padding="10,10,10,10" x:Name="mainLayout">

View File

@ -33,9 +33,6 @@
</Grid> </Grid>
<Label Text="{Binding Title}" /> <Label Text="{Binding Title}" />
<Label Text="{Binding Description}" />
<views:MarkdownView x:Name="mdview" <views:MarkdownView x:Name="mdview"
HorizontalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"
Markdown="{Binding Description}" Markdown="{Binding Description}"

View File

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:BookAStar;assembly=BookAStar"
x:Class="BookAStar.Pages.HomePage"
Style="{StaticResource PageStyle}"
Title="Accueil">
<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>
<TabbedPage.Children>
<!-- La recherche d'un pro -->
<TabbedPage Title="{x:Static local:Strings.SearchForAPro}" >
</TabbedPage>
<!--
les derniers sons/videos/articles postés
-->
<ContentPage Title="Blogspot">
<StackLayout Orientation="Horizontal">
<Editor x:Name="search_blog_phrase" HorizontalOptions="FillAndExpand"/>
<Button x:Name="btn_blog_update" HorizontalOptions="End" />
</StackLayout>
</ContentPage>
<!--
Les demandes devis en attente de réponse (pro)
-->
<ContentPage Title="Demandes de devis"
IsVisible="{Binding UserProfile.IsAPerformer}">
<StackLayout BindingContext="{Binding BookQueries}">
<ListView x:Name="querylist"
RefreshCommand="{Binding RefreshQueries}"
IsPullToRefreshEnabled="True"
ItemsSource="{Binding Queries}"
ItemTapped="OnViewBookQueryDetail"
HasUnevenRows="true"
SeparatorVisibility="Default"
SeparatorColor="Black">
<ListView.ItemTemplate VerticalOptions="StartAndExpand" >
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Row="0" Grid.Column="0" Grid.RowSpan="4" Source="{Binding Avatar}" />
<Label Grid.Row="0" Grid.Column="1" Text="{Binding Client.UserName}" Style="{StaticResource LabelStyle}"></Label>
<Label Grid.Row="1" Grid.Column="1" Text="{Binding Data.Reason}"></Label>
<Label Grid.Row="2" Grid.Column="1" LineBreakMode="WordWrap" Text="{Binding EventDate, StringFormat='{0:dddd d MMMM à HH:mm}'}" FontFamily="Italic"/>
<Label Grid.Row="3" Grid.Column="1" LineBreakMode="WordWrap" Text="{Binding Location.Address}" />
</Grid>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
<!-- Les signatures de contrat en souffreances (pro) -->
<ContentPage Title="Contrats fournisseur" IsVisible="{Binding UserProfile.IsAPerformer}">
</ContentPage>
<!-- Les signatures de contrat en souffreances (client) -->
<ContentPage Title="Contrats client" IsVisible="{Binding UserProfile.IsAPerformer}">
</ContentPage>
<!-- Les annonces pro (pro) -->
<ContentPage Title="Annonces pro" IsVisible="{Binding UserProfile.IsAPerformer}">
<StackLayout Orientation="Horizontal" >
<Editor x:Name="search_pub_pro_phrase" HorizontalOptions="FillAndExpand" VerticalOptions="Start"/>
<Button x:Name="btn_pro_pub" HorizontalOptions="End" VerticalOptions="Start" Text="Chercher"/>
</StackLayout>
</ContentPage>
<!-- les petites annonces des clients (pro) -->
<ContentPage Title="Annonces client" Icon="" IsVisible="{Binding UserProfile.IsAPerformer}">
<StackLayout Orientation="Horizontal" >
<Editor x:Name="search_pub_cli_phrase" HorizontalOptions="FillAndExpand" VerticalOptions="Start"/>
<Button x:Name="btn_cli_pub" HorizontalOptions="End" VerticalOptions="Start" Text="Chercher"/>
</StackLayout>
</ContentPage>
</TabbedPage.Children>
</TabbedPage>

View File

@ -0,0 +1,55 @@
using Xamarin.Forms;
namespace BookAStar.Pages
{
using Data;
using ViewModels;
using ViewModels.EstimateAndBilling;
public partial class HomePage
{
public HomePage()
{
InitializeComponent();
}
public HomePage(HomeViewModel model)
{
BindingContext = model;
}
public HomeViewModel Model {
get {
return (HomeViewModel) BindingContext;
}
set
{
BindingContext = value;
}
}
protected override void OnBindingContextChanged()
{
// this technique make this view model
// non-sharable between view or pages
if (Model != null)
{
// set the refresh command before using it
Model.BookQueries.RefreshQueries =
new Command(() =>
{
DataManager.Instance.BookQueries.Execute(null);
this.querylist.EndRefresh();
});
}
// Use the new refresh command
base.OnBindingContextChanged();
}
private void OnViewBookQueryDetail(object sender, ItemTappedEventArgs e)
{
var item = e.Item as BookQueryViewModel;
App.NavigationService.NavigateTo<BookQueryPage>(true, item);
}
}
}

View File

@ -1,17 +0,0 @@
<?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.HomePage"
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>
<Label Text="Blah" VerticalOptions="Center" HorizontalOptions="Center" />
</ContentPage>

View File

@ -1,55 +0,0 @@
<?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:local="clr-namespace:BookAStar;Assembly:BookAStar"
x:Class="BookAStar.SearchPage" Title="Page de recherche"
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>
<StackLayout>
<StackLayout Orientation="Horizontal">
<Editor x:Name="search_phrase" HorizontalOptions="FillAndExpand"/>
<Button x:Name="btn_update" HorizontalOptions="End" />
</StackLayout>
<DatePicker x:Name="search_date" />
<ListView x:Name="list" ItemsSource="{x:Static local:Manager.Events}"
HasUnevenRows="true">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout Orientation="Horizontal">
<Image Source="{Binding ImgLocator}" HeightRequest="80" />
<StackLayout Orientation="Vertical">
<Label Text="{Binding Title}"/>
<StackLayout Orientation="Horizontal">
<StackLayout><Label Text="Heure:" FontAttributes="Italic" FontSize="9" />
<Label Text="{Binding StartDate, StringFormat='{0:H:mm}'}" VerticalOptions="End"/>
</StackLayout>
<StackLayout><Label Text="Lieu:" FontAttributes="Italic" FontSize="9" VerticalOptions="End"/>
<Label Text="{Binding Location.Name}"/></StackLayout>
<Label Text="{Binding Promotion}" FontSize="20" TextColor="Yellow" BackgroundColor="#102030"/>
</StackLayout>
</StackLayout>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>

View File

@ -1,31 +0,0 @@
using System;
using Xamarin.Forms;
using System.Collections.ObjectModel;
using BookAStar.Model.Social;
using BookAStar.Model.Workflow.Messaging;
using BookAStar.Pages;
namespace BookAStar
{
public partial class SearchPage : ContentPage
{
public ObservableCollection<LocalizedEvent> Events { get; private set; }
public SearchPage ()
{
InitializeComponent ();
BindingContext = this;
Events = Manager.Events;
list.ItemTapped += async (object sender, ItemTappedEventArgs e) => {
await Navigation.PushAsync(new EventDetail( (YaEvent) e.Item) { Title = "Détail de la soirée" } );
};
search_date.Date = DateTime.Now;
search_date.MinimumDate = DateTime.Now;
search_phrase.Text = "Suresnes";
btn_update.Clicked += (object sender, EventArgs e) => {
//
};
}
}
}

View File

@ -3,7 +3,8 @@
xmlns:local="clr-namespace:BookAStar;assembly=BookAStar" xmlns:local="clr-namespace:BookAStar;assembly=BookAStar"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="BookAStar.Pages.UserProfile.AccountChooserPage" x:Class="BookAStar.Pages.UserProfile.AccountChooserPage"
Title="Paramètres Booking star" Style="{StaticResource PageStyle}" Title="{x:Static local:Strings.UserAccounts}"
Style="{StaticResource PageStyle}"
xmlns:lc="clr-namespace:XLabs.Forms.Controls;assembly=XLabs.Forms" xmlns:lc="clr-namespace:XLabs.Forms.Controls;assembly=XLabs.Forms"
xmlns:lb="clr-namespace:XLabs.Forms.Behaviors;assembly=XLabs.Forms"> xmlns:lb="clr-namespace:XLabs.Forms.Behaviors;assembly=XLabs.Forms">
@ -13,6 +14,7 @@
<ListView x:Name="AccountListView" <ListView x:Name="AccountListView"
SeparatorVisibility="Default" SeparatorVisibility="Default"
VerticalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
ItemsSource="{x:Static local:MainSettings.AccountList}"
> >
<ListView.Header> <ListView.Header>
<StackLayout Orientation="Horizontal"> <StackLayout Orientation="Horizontal">
@ -20,17 +22,18 @@
</StackLayout> </StackLayout>
</ListView.Header> </ListView.Header>
<ListView.ItemTemplate HeightRequest="60" VerticalOptions="StartAndExpand"> <ListView.ItemTemplate>
<DataTemplate> <DataTemplate>
<ViewCell> <ViewCell>
<Grid Padding="5" <Grid Padding="5"
ColumnSpacing="10" ColumnSpacing="10"
RowSpacing="2" > RowSpacing="2" >
<Image Source="{Binding AvatarSource}" HeightRequest="80" <Image Grid.Column="0" Source="{Binding AvatarSource}" HeightRequest="80" />
x:Name="avatarImage"/> <Label Grid.Column="1" Text="{Binding UserName}" >
<Label Grid.Column="0" Text="{Binding UserName}" >
</Label> </Label>
<Label Grid.Column="2" Text="{Binding EMails.ToString()}" />
<Label Grid.Column="3" Text="{Binding Roles.ToString()}" />
<Label Grid.Column="4" Text="{Binding Address}" />
</Grid> </Grid>
</ViewCell> </ViewCell>
</DataTemplate> </DataTemplate>

View File

@ -14,37 +14,24 @@ namespace BookAStar.Pages.UserProfile
public partial class AccountChooserPage : ContentPage public partial class AccountChooserPage : ContentPage
{ {
public ICommand RemoteSettingsRefreshCommand { get; private set; }
public AccountChooserPage () public AccountChooserPage ()
{ {
InitializeComponent (); InitializeComponent ();
AccountListView.ItemsSource = MainSettings.AccountList;
this.Musical = MainSettings.Musical;
this.Environ = MainSettings.Environ;
this.BindingContext = this;
AddAccountBtn.Clicked += AddAccountBtn_Clicked; AddAccountBtn.Clicked += AddAccountBtn_Clicked;
// avatarImage.
//RemoveAccountBouton.Clicked += RemoveAccountBouton_Clicked; //RemoveAccountBouton.Clicked += RemoveAccountBouton_Clicked;
AccountListView.ItemSelected += Accounts_ItemSelected; AccountListView.ItemSelected += Accounts_ItemSelected;
DumpParam = new RelayGesture((g, x) => // MainSettings.UserChanged += MainSettings_UserChanged;
{ }
if (g.GestureType == GestureType.Swipe && g.Direction == Directionality.Left) // Should be useless
{ private void MainSettings_UserChanged(object sender, EventArgs e)
RemoveAccount(); {
} AccountListView.SelectedItem = MainSettings.CurrentUser;
}); throw new NotImplementedException();
} }
public RelayGesture DumpParam { get; set; }
public ObservableCollection<User> Accounts { get; private set; }
public Dictionary<string, double> Musical { get; private set; }
public Dictionary<string, double> Environ { get; private set; }
private void Accounts_ItemSelected(object sender, SelectedItemChangedEventArgs e) private void Accounts_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{ {
@ -68,7 +55,6 @@ namespace BookAStar.Pages.UserProfile
protected override void OnAppearing() protected override void OnAppearing()
{ {
base.OnAppearing(); base.OnAppearing();
AccountListView.SelectedItem = MainSettings.CurrentUser;
} }
private void AddAccountBtn_Clicked(object sender, EventArgs e) private void AddAccountBtn_Clicked(object sender, EventArgs e)

View File

@ -7,7 +7,7 @@
x:Class="BookAStar.Pages.UserProfile.DashboardPage" x:Class="BookAStar.Pages.UserProfile.DashboardPage"
xmlns:lc="clr-namespace:XLabs.Forms.Controls;assembly=XLabs.Forms" xmlns:lc="clr-namespace:XLabs.Forms.Controls;assembly=XLabs.Forms"
xmlns:lb="clr-namespace:XLabs.Forms.Behaviors;assembly=XLabs.Forms" xmlns:lb="clr-namespace:XLabs.Forms.Behaviors;assembly=XLabs.Forms"
Style="{StaticResource PageStyle}"> Style="{StaticResource DashboardPageStyle}">
<ContentPage.Resources> <ContentPage.Resources>
<ResourceDictionary> <ResourceDictionary>
<Style TargetType="Label"> <Style TargetType="Label">
@ -21,34 +21,43 @@
<ContentPage.Content> <ContentPage.Content>
<ScrollView> <ScrollView>
<StackLayout BoxView.Color="{StaticResource ContentBackgroundColor}"> <StackLayout BoxView.Color="{StaticResource ContentBackgroundColor}">
<Label Text="{Binding UserName}" Style="{StaticResource LabelPageHeadingStyle}" <lc:GesturesContentView>
HorizontalTextAlignment="Center" <Image Source="{Binding Avatar}" HeightRequest="{StaticResource BigUserAvatarSize}" >
LineBreakMode="WordWrap" XAlign="Center" <lb:Gestures.Interests>
></Label> <lb:GestureCollection>
<lb:GestureInterest GestureType="SingleTap" GestureCommand="{Binding AvatarCommand}" GestureParameter="{Binding Ready}"/>
<lb:GestureInterest GestureType="LongPress" GestureCommand="{Binding AvatarCommand}" GestureParameter="{Binding Ready}"/>
<lb:GestureInterest GestureType="Swipe" Direction="Left" GestureCommand="{Binding AvatarCommand}" GestureParameter="{Binding Ready}"/>
<lb:GestureInterest GestureType="Swipe" Direction="Right" GestureCommand="{Binding AvatarCommand}" GestureParameter="{Binding Ready}"/>
</lb:GestureCollection>
</lb:Gestures.Interests>
</Image>
</lc:GesturesContentView>
<Button Text="{Binding UserName}" Clicked="OnRefreshQuery" />
<Button Text="{Binding UserFilesText}" Clicked="OnManageFiles" />
<StackLayout VisualElement.IsVisible="{Binding HaveAnUser}">
<Button Text="{Binding PerformerStatus}" Clicked="OnViewPerformerStatus" />
<Button Text="{Binding UserQueries}" Clicked="OnViewUserQueries"
VisualElement.IsVisible="{Binding IsAPerformer}"/>
<Image Source="{Binding Avatar}" <StackLayout Orientation="Horizontal">
VisualElement.HeightRequest="{StaticResource BigUserAvatarSize}" />
<views:RatingView Rating="{Binding Rating, Mode=TwoWay}" x:Name="ratingView" />
<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" /> <Label Text="Recevoir les notifications push" StyleClass="Header" />
<Switch IsToggled="{Binding ReceivePushNotifications, Mode=TwoWay}" <Switch IsToggled="{Binding ReceivePushNotifications, Mode=TwoWay}"
HorizontalOptions="End" /> HorizontalOptions="End" />
</StackLayout>
</StackLayout> </StackLayout>
<StackLayout Orientation="Horizontal"> <StackLayout Orientation="Horizontal">
<Label Text="Utiliser ma position" StyleClass="Header" /> <Label Text="Utiliser ma position" StyleClass="Header" />
<Switch HorizontalOptions="End" IsToggled="{Binding AllowUseMyPosition, Mode=TwoWay}"/> <Switch HorizontalOptions="End" IsToggled="{Binding AllowUseMyPosition, Mode=TwoWay}"/>
</StackLayout> </StackLayout>
<StackLayout Orientation="Horizontal" VerticalOptions="Start" <StackLayout VisualElement.IsVisible="{Binding IsAPerformer}" >
VisualElement.IsVisible="{Binding UserIsPro}" > <Label Text="{x:Static local:Strings.Profprof}" Style="{StaticResource LabelStyle}"/>
<Label Text="Ne recevoir de demande de devis que de la part de professionnels uniquement" /> <views:RatingView Rating="{Binding Rating, Mode=TwoWay}" x:Name="ratingView" />
<Switch HorizontalOptions="End" IsToggled="{Binding AllowProBookingOnly, Mode=TwoWay}"/> <StackLayout Orientation="Horizontal" VerticalOptions="Start">
<Label Text="{x:Static local:Strings.ClientProRequest}" />
<Switch HorizontalOptions="End" IsToggled="{Binding AllowProBookingOnly, Mode=TwoWay}" />
</StackLayout>
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
</ScrollView> </ScrollView>

View File

@ -5,7 +5,12 @@ using XLabs.Forms.Behaviors;
namespace BookAStar.Pages.UserProfile namespace BookAStar.Pages.UserProfile
{ {
using Data; using Data;
using Helpers;
using Newtonsoft.Json.Linq;
using System.Linq;
using System.Net.Http;
using ViewModels.UserProfile; using ViewModels.UserProfile;
using XLabs.Forms.Controls;
public partial class DashboardPage : ContentPage public partial class DashboardPage : ContentPage
{ {
@ -18,11 +23,43 @@ namespace BookAStar.Pages.UserProfile
protected override void OnBindingContextChanged() protected override void OnBindingContextChanged()
{ {
base.OnBindingContextChanged(); base.OnBindingContextChanged();
// Assert ((DashboardViewModel)BindingContext!=null) }
((DashboardViewModel)BindingContext).UserNameGesture
= new RelayGesture( (gesture,arg) => { public async void OnRefreshQuery(object sender, EventArgs e)
ShowPage<AccountChooserPage>(null, true); {
}); // TODO disable the button when current user is not registered
if (MainSettings.CurrentUser==null)
ShowPage<AccountChooserPage>(null, true);
else
{
IsBusy = true;
using (var client = UserHelpers.CreateJsonClient())
{
using (var request = new HttpRequestMessage(HttpMethod.Get, Constants.UserInfoUrl))
{
using (var response = await client.SendAsync(request))
{
response.EnsureSuccessStatusCode();
string userJson = await response.Content.ReadAsStringAsync();
JObject jactiveUser = JObject.Parse(userJson);
var username = jactiveUser["UserName"].Value<string>();
var roles = jactiveUser["Roles"].Values<string>().ToList();
var emails = jactiveUser["EMails"].Values<string>().ToList();
var avatar = jactiveUser["Avatar"].Value<string>();
var address = jactiveUser["Avatar"].Value<string>();
var me = MainSettings.CurrentUser;
me.Address = address;
me.Avatar = avatar;
me.EMails = emails;
me.UserName = username;
me.Roles = roles;
MainSettings.SaveUser(me);
}
}
}
IsBusy = false;
}
} }
public void OnManageFiles(object sender, EventArgs e) public void OnManageFiles(object sender, EventArgs e)

View File

@ -12,7 +12,7 @@ namespace BookAStar.Pages.UserProfile
public UserFiles() public UserFiles()
{ {
InitializeComponent(); InitializeComponent();
var current = DataManager.Current.RemoteFiles.CurrentItem; var current = DataManager.Instance.RemoteFiles.CurrentItem;
if (current != null) if (current != null)
BindingContext = new DirectoryInfoViewModel(current); BindingContext = new DirectoryInfoViewModel(current);
else BindingContext = new DirectoryInfoViewModel else BindingContext = new DirectoryInfoViewModel
@ -33,8 +33,8 @@ namespace BookAStar.Pages.UserProfile
if (model != null) if (model != null)
model.RefreshCommand = new Command(() => model.RefreshCommand = new Command(() =>
{ {
DataManager.Current.RemoteFiles.Execute(null); DataManager.Instance.RemoteFiles.Execute(null);
var item = DataManager.Current.RemoteFiles.CurrentItem; var item = DataManager.Instance.RemoteFiles.CurrentItem;
if (item != null) if (item != null)
model.InnerModel = item; model.InnerModel = item;
// this.dirlist.EndRefresh(); // this.dirlist.EndRefresh();

View File

@ -43,7 +43,7 @@
<StackLayout VisualElement.IsVisible="{Binding IsAPerformer}"> <StackLayout VisualElement.IsVisible="{Binding IsAPerformer}">
<StackLayout Orientation="Horizontal" VerticalOptions="Start" <StackLayout Orientation="Horizontal" VerticalOptions="Start"
VisualElement.IsVisible="{Binding UserIsPro}" > VisualElement.IsVisible="{Binding IsAPerformer}" >
<Label Text="Ne recevoir de demande de devis que de la part de professionnels uniquement" /> <Label Text="Ne recevoir de demande de devis que de la part de professionnels uniquement" />
<Switch HorizontalOptions="End" IsToggled="{Binding AllowProBookingOnly, Mode=TwoWay}"/> <Switch HorizontalOptions="End" IsToggled="{Binding AllowProBookingOnly, Mode=TwoWay}"/>
</StackLayout> </StackLayout>

View File

@ -1,5 +1,7 @@
 
using BookAStar.ViewModels.UserProfile; using BookAStar.ViewModels.UserProfile;
using Plugin.Media;
using Plugin.Media.Abstractions;
using System; using System;
using Xamarin.Forms; using Xamarin.Forms;
@ -13,10 +15,35 @@ namespace BookAStar.Pages.UserProfile
InitializeComponent(); InitializeComponent();
AvatarButton.Clicked += AvatarButton_Clicked; AvatarButton.Clicked += AvatarButton_Clicked;
} }
public UserProfilePage(UserProfileViewModel model)
private void AvatarButton_Clicked (object sender, EventArgs e)
{ {
throw new NotImplementedException(); InitializeComponent();
AvatarButton.Clicked += AvatarButton_Clicked;
BindingContext = model;
}
private async void AvatarButton_Clicked (object sender, EventArgs e)
{
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
await DisplayAlert("No Camera", ":( No camera avaialble.", "OK");
return;
}
var file = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions
{
Directory = "Avatars",
Name = "me.jpg"
});
if (file == null)
return;
// ImageSource.FromFile(file.Path);
/* ImageSource.FromStream(() =>
{
var stream = file.GetStream();
file.Dispose();
return stream;
}); */
} }
public void OnManageFiles(object sender, EventArgs e) public void OnManageFiles(object sender, EventArgs e)

View File

@ -1,6 +1,4 @@
// Helpers/Settings.cs // Helpers/Settings.cs
using BookAStar.Model;
using BookAStar.Model.Auth.Account;
using Newtonsoft.Json; using Newtonsoft.Json;
using Plugin.Settings; using Plugin.Settings;
using Plugin.Settings.Abstractions; using Plugin.Settings.Abstractions;
@ -8,11 +6,12 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace BookAStar namespace BookAStar
{ {
using Model.Social;
using Model.Auth.Account;
using Data;
/// <summary> /// <summary>
/// This is the Settings static class that can be used in your Core solution or in any /// This is the Settings static class that can be used in your Core solution or in any
@ -32,7 +31,7 @@ namespace BookAStar
#region Setting Constants #region Setting Constants
public static readonly string SettingsDefault = string.Empty; public static readonly string SettingsDefault = string.Empty;
public static readonly string EntityDataSettingsPrefix = "Ed"; public static readonly string EntityDataSettingsPrefix = Constants.YavscApiUrl;
private const string userNameKey = "user_id"; private const string userNameKey = "user_id";
private const string PushNotificationsKey = "pushNotifs"; private const string PushNotificationsKey = "pushNotifs";
private const string AllowGPSUsageKey = "allowGPSUsage"; private const string AllowGPSUsageKey = "allowGPSUsage";
@ -75,23 +74,13 @@ namespace BookAStar
} }
} }
public const string bookQueryNotificationsKey = "BookQueryNotifications"; public const string bookQueryNotificationsKey = "BookQueryNotifications";
public static BookQueryData[] GetBookQueryNotifications() public static BookQuery[] GetBookQueryNotifications()
{ {
// Do not return any null List // Do not return any null List
var json = AppSettings.GetValueOrDefault<string>(bookQueryNotificationsKey); var json = AppSettings.GetValueOrDefault<string>(bookQueryNotificationsKey);
if (!string.IsNullOrWhiteSpace(json)) if (!string.IsNullOrWhiteSpace(json))
return JsonConvert.DeserializeObject<BookQueryData[]>(json); return JsonConvert.DeserializeObject<BookQuery[]>(json);
return new BookQueryData[] { }; return new BookQuery[] { };
}
public static BookQueryData[] AddBookQueryNotification(BookQueryData query)
{
var existing = new List<BookQueryData>(GetBookQueryNotifications());
existing.Add(query);
var result = existing.ToArray();
AppSettings.AddOrUpdateValue(bookQueryNotificationsKey,
JsonConvert.SerializeObject(result));
return result;
} }
public static string GoogleRegId public static string GoogleRegId
@ -104,7 +93,7 @@ namespace BookAStar
// Inform the server of it. // Inform the server of it.
if (oldregid != value) if (oldregid != value)
{ {
App.CurrentApp.PostDeviceInfo(); App.PostDeviceInfo();
} }
} }
get { return AppSettings.GetValueOrDefault<string>(GoogleRegIdKey); } get { return AppSettings.GetValueOrDefault<string>(GoogleRegIdKey); }
@ -155,15 +144,15 @@ namespace BookAStar
{ {
if (olduserid != value.Id) if (olduserid != value.Id)
{ {
App.CurrentApp.PostDeviceInfo(); App.PostDeviceInfo();
if (UserChanged!=null) if (UserChanged!=null)
UserChanged.Invoke(App.CurrentApp, new EventArgs()); UserChanged.Invoke(App.Current, new EventArgs());
} }
} }
else if (olduserid != null) else if (olduserid != null)
{ {
if (UserChanged != null) if (UserChanged != null)
UserChanged.Invoke(App.CurrentApp, new EventArgs()); UserChanged.Invoke(App.Current, new EventArgs());
// TODO else Unregister this device // TODO else Unregister this device
} }
} }
@ -171,21 +160,34 @@ namespace BookAStar
public static event EventHandler<EventArgs> UserChanged; public static event EventHandler<EventArgs> UserChanged;
/// <summary>
/// Saves the given user account in the account list.
/// An existent presenting the same Id will be dropped.
/// </summary>
/// <param name="user"></param>
public static void SaveUser(User user) public static void SaveUser(User user)
{ {
var existent = AccountList.FirstOrDefault(u => u.UserName == user.UserName); var existent = AccountList.FirstOrDefault(u => u.Id == user.Id);
if (existent != null) if (existent != null)
AccountList.Remove(existent); AccountList.Remove(existent);
AccountList.Add(user); AccountList.Add(user);
var json = JsonConvert.SerializeObject(AccountList.ToArray()); var json = JsonConvert.SerializeObject(AccountList.ToArray());
AppSettings.AddOrUpdateValue(UserListsKey, json); AppSettings.AddOrUpdateValue(UserListsKey, json);
} }
/// <summary>
/// Gets an account connection info, given its name
/// </summary>
/// <param name="username"></param>
/// <returns></returns>
public static User GetUser(string username) public static User GetUser(string username)
{ {
return AccountList.FirstOrDefault(a => a.UserName == username); return AccountList.FirstOrDefault(a => a.UserName == username);
} }
// FIXME real time usage
/// <summary>
/// Enables/disables push notifications
/// </summary>
public static bool PushNotifications public static bool PushNotifications
{ {
get get
@ -203,6 +205,10 @@ namespace BookAStar
} }
} }
// FIXME real time usage
/// <summary>
/// Enables/disables GPS usage
/// </summary>
public static bool AllowGPSUsage public static bool AllowGPSUsage
{ {
get get
@ -218,7 +224,11 @@ namespace BookAStar
value); value);
} }
} }
// TODO make it a server side user's parameter // TODO make it a server side user's parameter
/// <summary>
/// Only allow professionals to ask for user's services
/// </summary>
public static bool AllowProBookingOnly public static bool AllowProBookingOnly
{ {
get get

View File

@ -61,6 +61,60 @@ namespace BookAStar {
} }
} }
/// <summary>
/// Recherche une chaîne localisée semblable à Une execution est déjà en cours.
/// </summary>
public static string AlreadyExecuting {
get {
return ResourceManager.GetString("AlreadyExecuting", resourceCulture);
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à Bloquer cet utilisateur.
/// </summary>
public static string BlockThisUser {
get {
return ResourceManager.GetString("BlockThisUser", resourceCulture);
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à Annuler la validation.
/// </summary>
public static string CancelValidation {
get {
return ResourceManager.GetString("CancelValidation", resourceCulture);
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à Restreindre la demande aux clients professionnels.
/// </summary>
public static string ClientProRequest {
get {
return ResourceManager.GetString("ClientProRequest", resourceCulture);
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à La création a échoué, contenu envoyé: {stringContent} @ Uri: {ControllerUri.AbsoluteUri}: Erreur : {errcontent}.
/// </summary>
public static string CreationFailed {
get {
return ResourceManager.GetString("CreationFailed", resourceCulture);
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à Décliner cette proposition (envoyer un refus, et archiver la demande).
/// </summary>
public static string DeclineQuery {
get {
return ResourceManager.GetString("DeclineQuery", resourceCulture);
}
}
/// <summary> /// <summary>
/// Recherche une chaîne localisée semblable à Faire un devis. /// Recherche une chaîne localisée semblable à Faire un devis.
/// </summary> /// </summary>
@ -70,6 +124,15 @@ namespace BookAStar {
} }
} }
/// <summary>
/// Recherche une chaîne localisée semblable à Valider le devis sans signer.
/// </summary>
public static string DonotsignEstimate {
get {
return ResourceManager.GetString("DonotsignEstimate", resourceCulture);
}
}
/// <summary> /// <summary>
/// Recherche une chaîne localisée semblable à Editer le devis. /// Recherche une chaîne localisée semblable à Editer le devis.
/// </summary> /// </summary>
@ -79,6 +142,15 @@ namespace BookAStar {
} }
} }
/// <summary>
/// Recherche une chaîne localisée semblable à Erreur d&apos;accès aux données distantes.
/// </summary>
public static string ENoRemoteEntity {
get {
return ResourceManager.GetString("ENoRemoteEntity", resourceCulture);
}
}
/// <summary> /// <summary>
/// Recherche une chaîne localisée semblable à Star. /// Recherche une chaîne localisée semblable à Star.
/// </summary> /// </summary>
@ -97,6 +169,51 @@ namespace BookAStar {
} }
} }
/// <summary>
/// Recherche une chaîne localisée semblable à Géographiquement proche.
/// </summary>
public static string GeographicalyNear {
get {
return ResourceManager.GetString("GeographicalyNear", resourceCulture);
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à Valeur invalide.
/// </summary>
public static string InvalidValue {
get {
return ResourceManager.GetString("InvalidValue", resourceCulture);
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à Veuillez saisir une valeur entre {0} et {1}..
/// </summary>
public static string MinMaxIntError {
get {
return ResourceManager.GetString("MinMaxIntError", resourceCulture);
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à Veuillez saisir une description (entre {0} et {1} caractères)..
/// </summary>
public static string MinMaxStringValidationError {
get {
return ResourceManager.GetString("MinMaxStringValidationError", resourceCulture);
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à Pas de description.
/// </summary>
public static string NoDescription {
get {
return ResourceManager.GetString("NoDescription", resourceCulture);
}
}
/// <summary> /// <summary>
/// Recherche une chaîne localisée semblable à Un artiste. /// Recherche une chaîne localisée semblable à Un artiste.
/// </summary> /// </summary>
@ -115,6 +232,60 @@ namespace BookAStar {
} }
} }
/// <summary>
/// Recherche une chaîne localisée semblable à Opération en cours.
/// </summary>
public static string OperationPending {
get {
return ResourceManager.GetString("OperationPending", resourceCulture);
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à oups..
/// </summary>
public static string oups {
get {
return ResourceManager.GetString("oups", resourceCulture);
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à Profile professionnel.
/// </summary>
public static string Profprof {
get {
return ResourceManager.GetString("Profprof", resourceCulture);
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à Rechercher un artiste.
/// </summary>
public static string SearchForAPro {
get {
return ResourceManager.GetString("SearchForAPro", resourceCulture);
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à Signer.
/// </summary>
public static string Sign {
get {
return ResourceManager.GetString("Sign", resourceCulture);
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à Signer le devis?.
/// </summary>
public static string SignOrNot {
get {
return ResourceManager.GetString("SignOrNot", resourceCulture);
}
}
/// <summary> /// <summary>
/// Recherche une chaîne localisée semblable à Incontournable. /// Recherche une chaîne localisée semblable à Incontournable.
/// </summary> /// </summary>
@ -134,12 +305,48 @@ namespace BookAStar {
} }
/// <summary> /// <summary>
/// Recherche une chaîne localisée semblable à Voir le devis. /// Recherche une chaîne localisée semblable à La mise à jour a échoué..
/// </summary>
public static string UpdateFailed {
get {
return ResourceManager.GetString("UpdateFailed", resourceCulture);
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à Comptes utilisateur.
/// </summary>
public static string UserAccounts {
get {
return ResourceManager.GetString("UserAccounts", resourceCulture);
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à Voir les devis validés.
/// </summary> /// </summary>
public static string ViewEstimate { public static string ViewEstimate {
get { get {
return ResourceManager.GetString("ViewEstimate", resourceCulture); return ResourceManager.GetString("ViewEstimate", resourceCulture);
} }
} }
/// <summary>
/// Recherche une chaîne localisée semblable à Vos fichiers .
/// </summary>
public static string YourFiles {
get {
return ResourceManager.GetString("YourFiles", resourceCulture);
}
}
/// <summary>
/// Recherche une chaîne localisée semblable à Votre texte a été taillé car il était trop long..
/// </summary>
public static string YourTextWasTooLong {
get {
return ResourceManager.GetString("YourTextWasTooLong", resourceCulture);
}
}
} }
} }

View File

@ -117,12 +117,27 @@
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="AlreadyExecuting" xml:space="preserve">
<value>Une execution est déjà en cours</value>
</data>
<data name="CancelValidation" xml:space="preserve">
<value>Annuler la validation</value>
</data>
<data name="CreationFailed" xml:space="preserve">
<value>La création a échoué, contenu envoyé: {stringContent} @ Uri: {ControllerUri.AbsoluteUri}: Erreur : {errcontent}</value>
</data>
<data name="DoEstimate" xml:space="preserve"> <data name="DoEstimate" xml:space="preserve">
<value>Faire un devis</value> <value>Faire un devis</value>
</data> </data>
<data name="DonotsignEstimate" xml:space="preserve">
<value>Valider le devis sans signer</value>
</data>
<data name="EditEstimate" xml:space="preserve"> <data name="EditEstimate" xml:space="preserve">
<value>Editer le devis</value> <value>Editer le devis</value>
</data> </data>
<data name="ENoRemoteEntity" xml:space="preserve">
<value>Erreur d'accès aux données distantes</value>
</data>
<data name="FiveStars" xml:space="preserve"> <data name="FiveStars" xml:space="preserve">
<value>Star</value> <value>Star</value>
<comment>Da star, da one</comment> <comment>Da star, da one</comment>
@ -137,6 +152,18 @@
<data name="OneStar" xml:space="preserve"> <data name="OneStar" xml:space="preserve">
<value>Étoile montante</value> <value>Étoile montante</value>
</data> </data>
<data name="OperationPending" xml:space="preserve">
<value>Opération en cours</value>
</data>
<data name="oups" xml:space="preserve">
<value>oups.</value>
</data>
<data name="Sign" xml:space="preserve">
<value>Signer</value>
</data>
<data name="SignOrNot" xml:space="preserve">
<value>Signer le devis?</value>
</data>
<data name="ThreeStars" xml:space="preserve"> <data name="ThreeStars" xml:space="preserve">
<value>Incontournable</value> <value>Incontournable</value>
</data> </data>
@ -144,6 +171,48 @@
<value>À ne manquer sous aucun prétexte</value> <value>À ne manquer sous aucun prétexte</value>
</data> </data>
<data name="ViewEstimate" xml:space="preserve"> <data name="ViewEstimate" xml:space="preserve">
<value>Voir le devis</value> <value>Voir les devis validés</value>
</data>
<data name="UpdateFailed" xml:space="preserve">
<value>La mise à jour a échoué.</value>
</data>
<data name="MinMaxStringValidationError" xml:space="preserve">
<value>Veuillez saisir une description (entre {0} et {1} caractères).</value>
</data>
<data name="YourTextWasTooLong" xml:space="preserve">
<value>Votre texte a été taillé car il était trop long.</value>
</data>
<data name="NoDescription" xml:space="preserve">
<value>Pas de description</value>
</data>
<data name="InvalidValue" xml:space="preserve">
<value>Valeur invalide</value>
</data>
<data name="MinMaxIntError" xml:space="preserve">
<value>Veuillez saisir une valeur entre {0} et {1}.</value>
</data>
<data name="YourFiles" xml:space="preserve">
<value>Vos fichiers </value>
</data>
<data name="Profprof" xml:space="preserve">
<value>Profile professionnel</value>
</data>
<data name="ClientProRequest" xml:space="preserve">
<value>Restreindre la demande aux clients professionnels</value>
</data>
<data name="UserAccounts" xml:space="preserve">
<value>Comptes utilisateur</value>
</data>
<data name="BlockThisUser" xml:space="preserve">
<value>Bloquer cet utilisateur</value>
</data>
<data name="DeclineQuery" xml:space="preserve">
<value>Décliner cette proposition (envoyer un refus, et archiver la demande)</value>
</data>
<data name="GeographicalyNear" xml:space="preserve">
<value>Géographiquement proche</value>
</data>
<data name="SearchForAPro" xml:space="preserve">
<value>Rechercher un artiste</value>
</data> </data>
</root> </root>

View File

@ -1,29 +0,0 @@
using BookAStar.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using XLabs.Forms.Mvvm;
namespace BookAStar.ViewModels
{
/// <summary>
/// Used to make the DataManager know how
/// to sync local and remote data
/// </summary>
public class EditingViewModel: ViewModel
{
private LocalState state;
public LocalState State {
get
{
return state;
}
set
{
base.SetProperty<LocalState>(ref state, value);
}
}
}
}

View File

@ -1,25 +1,54 @@
using BookAStar.Attributes; using BookAStar.Attributes;
using BookAStar.Interfaces; using BookAStar.Interfaces;
using BookAStar.Model.Workflow; using BookAStar.Model.Workflow;
using BookAStar.ViewModels.Validation;
using System; using System;
using System.Globalization; using System.Globalization;
using System.Windows.Input; using System.Windows.Input;
using System.ComponentModel;
namespace BookAStar.ViewModels.EstimateAndBilling namespace BookAStar.ViewModels.EstimateAndBilling
{ {
public class BillingLineViewModel : EditingViewModel, IBillingLine public class BillingLineViewModel : EditingViewModel<BillingLine>, IBillingLine
{ {
BillingLine data; public ICommand RemoveCommand { get; set; }
public ICommand ValidateCommand { set; get; }
public BillingLineViewModel(BillingLine data) public BillingLineViewModel(BillingLine data): base(data)
{ {
this.data = data ?? new BillingLine(); CheckCommand = new Action<BillingLine, ModelState>(
// sets durationValue & durationUnit (l,s) => {
count = data.Count; if (string.IsNullOrWhiteSpace(l.Description))
description = data.Description; {
s.AddError("Description",Strings.NoDescription);
}
if (l.UnitaryCost < 0) { s.AddError("UnitaryCost", Strings.InvalidValue); }
if (l.Count < 0) { s.AddError("Count", Strings.InvalidValue); }
});
SyncData();
}
Duration = data.Duration; private void SyncData()
unitaryCostText = data.UnitaryCost.ToString("G", CultureInfo.InvariantCulture); {
if (Data != null)
{
// set durationValue, durationUnit
Duration = Data.Duration;
// other redondant representation
count = Data.Count;
description = Data.Description;
unitaryCostText = Data.UnitaryCost.ToString("G", CultureInfo.InvariantCulture);
}
CheckCommand(Data, ViewModelState);
}
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.PropertyName=="Data")
{
SyncData();
}
} }
private int count; private int count;
@ -33,7 +62,7 @@ namespace BookAStar.ViewModels.EstimateAndBilling
set set
{ {
SetProperty<int>(ref count, value); SetProperty<int>(ref count, value);
data.Count = count; Data.Count = count;
} }
} }
private string description; private string description;
@ -47,9 +76,10 @@ namespace BookAStar.ViewModels.EstimateAndBilling
set set
{ {
SetProperty<string>(ref description, value); SetProperty<string>(ref description, value);
data.Description = description; Data.Description = description;
} }
} }
decimal unitaryCost; decimal unitaryCost;
public decimal UnitaryCost public decimal UnitaryCost
{ {
@ -61,7 +91,7 @@ namespace BookAStar.ViewModels.EstimateAndBilling
set set
{ {
SetProperty<decimal>(ref unitaryCost, value); SetProperty<decimal>(ref unitaryCost, value);
data.UnitaryCost = unitaryCost; Data.UnitaryCost = unitaryCost;
} }
} }
@ -76,7 +106,7 @@ namespace BookAStar.ViewModels.EstimateAndBilling
set set
{ {
SetProperty<int>(ref durationValue, value, "DurationValue"); SetProperty<int>(ref durationValue, value, "DurationValue");
data.Duration = this.Duration; Data.Duration = this.Duration;
} }
} }
@ -99,7 +129,7 @@ pour décrire la quantité de travail associée à ce type de service")]
set set
{ {
SetProperty<DurationUnits>(ref durationUnit, value, "DurationUnit"); SetProperty<DurationUnits>(ref durationUnit, value, "DurationUnit");
data.Duration = this.Duration; Data.Duration = this.Duration;
} }
} }
@ -125,7 +155,6 @@ pour décrire la quantité de travail associée à ce type de service")]
} }
} }
public ICommand ValidateCommand { set; get; }
public TimeSpan Duration public TimeSpan Duration
{ {

View File

@ -5,18 +5,24 @@ namespace BookAStar.ViewModels.EstimateAndBilling
{ {
using Data; using Data;
using Model; using Model;
using System.Linq;
public class BookQueriesViewModel : XLabs.Forms.Mvvm.ViewModel public class BookQueriesViewModel : XLabs.Forms.Mvvm.ViewModel
{ {
public BookQueriesViewModel() public BookQueriesViewModel()
{ {
queries = new ObservableCollection<BookQueryViewModel>
(DataManager.Instance.BookQueries.Select(
q =>
new BookQueryViewModel(q)));
} }
private ObservableCollection<BookQueryViewModel> queries;
public ObservableCollection<BookQueryData> Queries public ObservableCollection<BookQueryViewModel> Queries
{ {
get get
{ {
return DataManager.Current.BookQueries; return queries;
} }
} }

View File

@ -6,20 +6,22 @@ using XLabs.Forms.Mvvm;
namespace BookAStar.ViewModels.EstimateAndBilling namespace BookAStar.ViewModels.EstimateAndBilling
{ {
using Data; using Data;
using Helpers;
using Interfaces; using Interfaces;
using Model; using Model;
using Model.Social; using Model.Social;
using Model.Workflow; using Model.Workflow;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using Xamarin.Forms;
class BookQueryViewModel : ViewModel, IBookQueryData public class BookQueryViewModel : ViewModel, IBookQueryData
{ {
public BookQueryViewModel() public BookQueryViewModel()
{ {
} }
public BookQueryViewModel(BookQueryData data) public BookQueryViewModel(BookQuery data)
{ {
Debug.Assert(data != null); Debug.Assert(data != null);
Client=data.Client; Client=data.Client;
@ -28,19 +30,33 @@ namespace BookAStar.ViewModels.EstimateAndBilling
Previsionnal = data.Previsionnal; Previsionnal = data.Previsionnal;
Id = data.Id; Id = data.Id;
estimates = new ObservableCollection<Estimate>( estimates = new ObservableCollection<Estimate>(
DataManager.Current.Estimates.Where( DataManager.Instance.Estimates.Where(
e => e.Query.Id == Id e => e.Query.Id == Id
)); ));
this.data = data; this.data = data;
} }
private BookQueryData data; private BookQuery data;
public BookQueryData Data { public BookQuery Data {
get get
{ {
return data; return data;
} }
} }
public ClientProviderInfo Client { get; set; } public ClientProviderInfo Client { get; set; }
public ImageSource Avatar
{
get
{
return UserHelpers.Avatar(Client.Avatar);
}
}
public ImageSource SmallAvatar
{
get
{
return UserHelpers.SmallAvatar(Client.Avatar, Client.UserName);
}
}
public Location Location { get; set; } public Location Location { get; set; }
public long Id { get; set; } public long Id { get; set; }
public DateTime EventDate { get; set; } public DateTime EventDate { get; set; }
@ -49,7 +65,7 @@ namespace BookAStar.ViewModels.EstimateAndBilling
{ {
get get
{ {
return DataManager.Current.EstimationCache.LocalGet(this.Id); return DataManager.Instance.EstimationCache.LocalGet(this.Id);
} }
} }
private ObservableCollection<Estimate> estimates; private ObservableCollection<Estimate> estimates;

View File

@ -1,55 +1,78 @@
using System.Collections.Generic; using System.Collections.Generic;
using BookAStar.Model.Workflow;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using BookAStar.Model;
using Xamarin.Forms; using Xamarin.Forms;
using BookAStar.Data;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Linq;
using System.ComponentModel;
namespace BookAStar.ViewModels.EstimateAndBilling namespace BookAStar.ViewModels.EstimateAndBilling
{ {
public class EditEstimateViewModel : EditingViewModel using Model;
using Model.Workflow;
using Model.Social;
using Validation;
public class EditEstimateViewModel : EditingViewModel<Estimate>
{ {
/// <summary>
/// For deserialization
/// </summary>
public EditEstimateViewModel()
{
}
/// <summary> /// <summary>
/// Builds a new view model on estimate, /// Builds a new view model on estimate,
/// sets <c>Data</c> with given value parameter /// sets <c>Data</c> with given value parameter
/// </summary> /// </summary>
/// <param name="data"></param> /// <param name="data"></param>
/// <param name="localState"></param> /// <param name="localState"></param>
public EditEstimateViewModel(Estimate data) public EditEstimateViewModel(Estimate data) : base(data)
{ {
Data = data; SyncData();
}
public override void OnViewAppearing()
{
base.OnViewAppearing();
SyncData();
}
/// <summary>
/// Called to synchronyze this view on target model,
/// at accepting a new representation for this model
/// </summary>
private void SyncData()
{
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>();
AttachedFiles = new ObservableCollection<string>(Data.AttachedFiles);
AttachedGraphicList = new ObservableCollection<string>(Data.AttachedGraphics);
Bill = new ObservableCollection<BillingLineViewModel>(Data.Bill.Select(
l => new BillingLineViewModel(l)
));
Bill.CollectionChanged += Bill_CollectionChanged;
Title = Data.Title;
Description = Data.Description;
NotifyPropertyChanged("FormattedTotal");
NotifyPropertyChanged("Query");
NotifyPropertyChanged("CLient");
NotifyPropertyChanged("ModelState");
} }
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.PropertyName.StartsWith("Data"))
{
SyncData();
}
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Bill_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) private void Bill_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{ {
Data.Bill = new List<BillingLine>( Bill ); Data.Bill = Bill.Select(l => l.Data).ToList();
NotifyPropertyChanged("FormattedTotal"); NotifyPropertyChanged("FormattedTotal");
NotifyPropertyChanged("Bill"); NotifyPropertyChanged("Bill");
NotifyPropertyChanged("ViewModelState");
} }
private Estimate data;
public Estimate Data { get { return data; } set {
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>();
AttachedFiles = new ObservableCollection<string>(data.AttachedFiles);
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] [JsonIgnore]
public ObservableCollection<string> AttachedFiles public ObservableCollection<string> AttachedFiles
@ -64,7 +87,7 @@ namespace BookAStar.ViewModels.EstimateAndBilling
} }
[JsonIgnore] [JsonIgnore]
public ObservableCollection<BillingLine> Bill public ObservableCollection<BillingLineViewModel> Bill
{ {
get; protected set; get; protected set;
} }
@ -107,25 +130,27 @@ namespace BookAStar.ViewModels.EstimateAndBilling
public ClientProviderInfo Client { get { return Data.Client; } } public ClientProviderInfo Client { get { return Data.Client; } }
[JsonIgnore] [JsonIgnore]
public BookQueryData Query { get { return Data.Query; } } public BookQuery Query { get { return Data.Query; } }
[JsonIgnore] [JsonIgnore]
public FormattedString FormattedTotal public FormattedString FormattedTotal
{ {
get get
{ {
OnPlatform<Font> lfs = (OnPlatform<Font>)App.Current.Resources["LargeFontSize"]; /*
OnPlatform<Color> etc = (OnPlatform<Color>)App.Current.Resources["EmphasisTextColor"]; OnPlatform<Font> lfs = (OnPlatform<Font>)App.Current.Resources["MediumFontSize"];
*/
OnPlatform<double> mfs = (OnPlatform < double > ) App.Current.Resources["MediumFontSize"];
Color etc = (Color) App.Current.Resources["EmphasisTextColor"];
return new FormattedString return new FormattedString
{ {
Spans = { Spans = {
new Span { Text = "Total TTC: " }, new Span { Text = "Total TTC: " },
new Span { Text = Data.Total.ToString(), new Span { Text = Data.Total.ToString(),
ForegroundColor = etc.Android , ForegroundColor = etc,
FontSize = (double) lfs.Android.FontSize }, FontSize = mfs },
new Span { Text = "€", FontSize = (double) lfs.Android.FontSize } new Span { Text = "€", FontSize = mfs }
} }
}; };
} }

View File

@ -0,0 +1,14 @@
using XLabs.Forms.Mvvm;
namespace BookAStar.ViewModels
{
using EstimateAndBilling;
using UserProfile;
public class HomeViewModel : ViewModel
{
public BookQueriesViewModel BookQueries { get; set; }
public UserProfileViewModel UserProfile { get; set; }
}
}

View File

@ -0,0 +1,46 @@
using BookAStar.Data;
using BookAStar.Model.Social.Chat;
using BookAStar.Model.Social.Messaging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Forms;
namespace BookAStar.ViewModels.Messaging
{
public class ChatUserCollection : RemoteEntityRO<ChatUserInfo, string>
{
public ChatUserCollection() : base ("chat/users", u=>u.UserId)
{
}
public void OnPrivateMessage(ChatMessage msg)
{
var sender = this.FirstOrDefault(user => user.UserName == msg.SenderId);
if (sender != null)
{
sender.PrivateMessages.Add(msg);
} else
{
// TODO alert? or else get chat user info
// or else just display this message ...
}
}
public override void Merge(ChatUserInfo item)
{
var key = GetKey(item);
var existent = this.FirstOrDefault(u => u.UserId == key);
if (existent != null) {
existent.UserName = item.UserName;
existent.Roles = item.Roles;
existent.Avatar = item.Avatar;
existent.Connections = item.Connections;
}
else Add(item);
}
}
}

View File

@ -0,0 +1,196 @@

using BookAStar.Helpers;
using System.Collections.ObjectModel;
using System.Linq;
using Xamarin.Forms;
using XLabs.Forms.Mvvm;
using YavscLib;
using System;
using Newtonsoft.Json;
using BookAStar.Model.Social.Messaging;
namespace BookAStar.Model.Social.Chat
{
public class ChatUserInfo : ViewModel, IChatUserInfo
{
public ChatUserInfo()
{
PrivateMessages.CollectionChanged += PrivateMessages_CollectionChanged;
}
private void PrivateMessages_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
NotifyPropertyChanged("Unread");
}
public string avatar;
public string Avatar
{
get
{
return avatar;
}
set
{
var newSource = UserHelpers.Avatar(value);
SetProperty<string>(ref avatar, value);
SetProperty<ImageSource>(ref avatarSource, newSource, "AvatarSource");
}
}
ImageSource avatarSource;
[JsonIgnore]
public ImageSource AvatarSource
{
get
{
return avatarSource;
}
}
public Connection [] Connections
{
get
{
return ObservableConnections?.ToArray();
}
set
{
ObservableConnections = new ObservableCollection<Connection>(value);
}
}
ObservableCollection<Connection> connections;
[JsonIgnore]
public ObservableCollection<Connection> ObservableConnections
{
get
{
return connections;
}
set
{
SetProperty<ObservableCollection<Connection>>(ref connections, value);
}
}
string[] roles;
public string[] Roles
{
get
{
return roles;
}
set
{
SetProperty<string[]>(ref roles, value);
NotifyPropertyChanged("RolesAsAString");
}
}
[JsonIgnore]
public string RolesAsAString
{
get
{
return Roles == null? "": string.Join(", ", Roles);
}
}
string userId;
public string UserId
{
get
{
return userId;
}
set
{
SetProperty<string>(ref userId, value);
}
}
string userName;
public string UserName
{
get
{
return userName;
}
set
{
SetProperty<string>(ref userName, value);
}
}
public bool IsConnected { get
{
return Connections.Length > 0;
} }
[JsonIgnore]
IConnection[] IChatUserInfo.Connections
{
get
{
return Connections;
}
set
{
throw new NotImplementedException();
}
}
ObservableCollection<ChatMessage> privateMessages = new ObservableCollection<ChatMessage>();
[JsonIgnore]
public ObservableCollection<ChatMessage> PrivateMessages
{
get
{
return privateMessages;
}
}
[JsonIgnore]
public bool Unread
{
get
{
return PrivateMessages==null?false: PrivateMessages.Any(
m => !m.Read);
}
}
[JsonIgnore]
public ImageSource MessagesBadge
{
get
{
return Unread ? ImageSource.FromResource("BookAStar.Images.Chat.talk.png") :null;
}
}
public void OnConnected(string cxId)
{
// We do assume this cxId dosn't already exist in this list.
var cx = new Connection { ConnectionId = cxId, Connected = true };
if (ObservableConnections == null)
ObservableConnections = new ObservableCollection<Connection>();
ObservableConnections.Add(cx);
if (this.ObservableConnections.Count == 1)
NotifyPropertyChanged("IsConnected");
}
public void OnDisconnected(string cxId)
{
var existentcx = Connections.FirstOrDefault(cx => cx.ConnectionId == cxId);
if (existentcx != null)
{
this.ObservableConnections.Remove(existentcx);
if (this.ObservableConnections.Count == 0)
NotifyPropertyChanged("IsConnected");
}
}
}
}

View File

@ -7,27 +7,15 @@ using XLabs.Forms.Mvvm;
namespace BookAStar.ViewModels.Messaging namespace BookAStar.ViewModels.Messaging
{ {
using Data; using Data;
using Model; using Model.Social.Chat;
using Model.Social.Messaging; using Model.Social.Messaging;
class ChatViewModel: ViewModel public class ChatViewModel: ViewModel
{ {
public ObservableCollection<ChatMessage> Messages { get; set; } public ObservableCollection<ChatMessage> Messages { get; set; }
public ObservableCollection<ChatMessage> Notifs { get; set; } public ObservableCollection<ChatMessage> Notifs { get; set; }
public ObservableCollection<ChatMessage> PVs { get; set; } public ChatUserCollection ChatUsers { get; set; }
public ObservableCollection<ClientProviderInfo> Contacts { get; set; }
private string chatUser;
public string ChatUser
{
get
{
return chatUser;
}
set
{
SetProperty<string>(ref chatUser, value);
}
}
private ConnectionState state; private ConnectionState state;
public ConnectionState State public ConnectionState State
{ {
@ -40,8 +28,7 @@ namespace BookAStar.ViewModels.Messaging
MainSettings.UserChanged += MainSettings_UserChanged; MainSettings.UserChanged += MainSettings_UserChanged;
Messages = new ObservableCollection<ChatMessage>(); Messages = new ObservableCollection<ChatMessage>();
Notifs = new ObservableCollection<ChatMessage>(); Notifs = new ObservableCollection<ChatMessage>();
PVs = DataManager.Current.PrivateMessages; ChatUsers = DataManager.Instance.ChatUsers;
Contacts = DataManager.Current.Contacts;
App.ChatHubProxy.On<string, string>("addMessage", (n, m) => App.ChatHubProxy.On<string, string>("addMessage", (n, m) =>
{ {
Messages.Add(new ChatMessage Messages.Add(new ChatMessage
@ -54,45 +41,53 @@ namespace BookAStar.ViewModels.Messaging
App.ChatHubProxy.On<string, string, string>("notify", (eventId, cxId, userName) => App.ChatHubProxy.On<string, string, string>("notify", (eventId, cxId, userName) =>
{ {
var msg = new ChatMessage
{
Message = eventId,
SenderId = userName,
Date = DateTime.Now
};
// TODO make admin possible // TODO make admin possible
// by assigning a server side username to anonymous. // by assigning a server side username to anonymous.
// From now, don't log anonymous if (string.IsNullOrEmpty(userName))
if (!string.IsNullOrEmpty(userName))
{ {
Notifs.Add(new ChatMessage msg.SenderId = $"({cxId})";
{
Message = eventId,
SenderId = userName,
Date = DateTime.Now
});
if (eventId == "connected")
OnUserConnected(cxId, userName);
else if (eventId == "disconnected")
OnUserDisconnected(userName);
} }
Notifs.Add(msg);
if (eventId == "connected")
OnUserConnected(cxId, userName);
else if (eventId == "disconnected")
OnUserDisconnected(cxId, userName);
}); });
ChatUser = MainSettings.UserName;
} }
private void OnUserConnected(string cxId, string userName) private void OnUserConnected(string cxId, string userName)
{ {
var user = Contacts.SingleOrDefault( var user = ChatUsers.SingleOrDefault(
c => c.UserName == userName); c => c.UserName == userName);
if (user != null) if (user == null)
user.ChatHubConnectionId = cxId; {
user = new ChatUserInfo {
UserName = userName
};
ChatUsers.Add(user);
}
user.OnConnected(cxId);
} }
private void OnUserDisconnected (string userName) private void OnUserDisconnected (string cxId, string userName)
{ {
var user = Contacts.SingleOrDefault( var user = ChatUsers.SingleOrDefault(
c => c.UserName == userName); c => c.UserName == userName);
if (user != null) if (user == null)
user.ChatHubConnectionId = null; {
return;
}
user.OnDisconnected(cxId);
} }
private void MainSettings_UserChanged(object sender, EventArgs e) private void MainSettings_UserChanged(object sender, EventArgs e)
{ {
ChatUser = MainSettings.UserName;
} }
private void ChatHubConnection_StateChanged(StateChange obj) private void ChatHubConnection_StateChanged(StateChange obj)

View File

@ -1,6 +1,6 @@
using XLabs.Forms.Mvvm; using XLabs.Forms.Mvvm;
namespace BookAStar.Model.UI namespace BookAStar.ViewModels
{ {
internal class PageState internal class PageState
{ {

View File

@ -0,0 +1,10 @@

using XLabs.Forms.Mvvm;
namespace BookAStar.ViewModels.Searching
{
class SearchingAnArtistViewModel: ViewModel
{
}
}

Some files were not shown because too many files have changed in this diff Show More