Pretty much finished the app

This commit is contained in:
Trevi Awater
2022-01-14 01:53:46 +01:00
parent 02a5af9eec
commit 2a0f83a397
57 changed files with 1267 additions and 386 deletions

View File

@@ -0,0 +1,23 @@
//
// QueryEditorRenderer.cs
//
// Trevi Awater
// 13-01-2022
//
// © Xamarin.Neo4j.iOS
//
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using Xamarin.Neo4j.Controls;
using Xamarin.Neo4j.iOS.CustomRenderers;
[assembly: ExportRenderer(typeof(QueryEditor), typeof(QueryEditorRenderer))]
namespace Xamarin.Neo4j.iOS.CustomRenderers
{
public class QueryEditorRenderer : EditorRenderer
{
}
}

View File

@@ -2,43 +2,43 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>UIDeviceFamily</key> <key>UIDeviceFamily</key>
<array> <array>
<integer>1</integer> <integer>1</integer>
<integer>2</integer> <integer>2</integer>
</array> </array>
<key>UISupportedInterfaceOrientations</key> <key>UISupportedInterfaceOrientations</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>UISupportedInterfaceOrientations~ipad</key> <key>UISupportedInterfaceOrientations~ipad</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string> <string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>
<string>14.2</string> <string>14.2</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
<string>Pocket Graph</string> <string>PocketGraph</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>nl.resoftware.xamarin-neo4j</string> <string>nl.resoftware.pocketgraph</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.0</string> <string>1.0.1</string>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
<string>LaunchScreen</string> <string>LaunchScreen</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>Pocket Graph</string> <string>PocketGraph</string>
<key>XSAppIconAssets</key> <key>XSAppIconAssets</key>
<string>Assets.xcassets/AppIcon.appiconset</string> <string>Assets.xcassets/AppIcon.appiconset</string>
<key>UIAppFonts</key> <key>UIAppFonts</key>
<array> <array>
<string>iconize-fontawesome-regular.ttf</string> <string>iconize-fontawesome-regular.ttf</string>
<string>iconize-fontawesome-solid.ttf</string> <string>iconize-fontawesome-solid.ttf</string>
<string>iconize-fontawesome-brands.ttf</string> <string>iconize-fontawesome-brands.ttf</string>
</array> </array>
</dict> </dict>
</plist> </plist>

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" image="logo.png" translatesAutoresizingMaskIntoConstraints="NO" id="198">
<rect key="frame" x="87" y="384" width="240" height="128"/>
<constraints>
<constraint firstAttribute="width" constant="240" id="623"/>
<constraint firstAttribute="height" constant="128" id="830"/>
</constraints>
</imageView>
</subviews>
<viewLayoutGuide key="safeArea" id="1038"/>
<color key="backgroundColor" red="0.19136750699999999" green="0.2006954849" blue="0.2327556908" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="198" firstAttribute="centerX" secondItem="1038" secondAttribute="centerX" id="201"/>
<constraint firstItem="1038" firstAttribute="centerY" secondItem="198" secondAttribute="centerY" id="203"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="logo.png" width="512" height="512"/>
</resources>
</document>

View File

@@ -1,39 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6245" systemVersion="13F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="X5k-f2-b5h">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6238"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="gAE-YM-kbH">
<objects>
<viewController id="X5k-f2-b5h" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Y8P-hJ-Z43"/>
<viewControllerLayoutGuide type="bottom" id="9ZL-r4-8FZ"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="yd7-JS-zBw">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" misplaced="YES" image="Icon-60.png" translatesAutoresizingMaskIntoConstraints="NO" id="23">
<rect key="frame" x="270" y="270" width="60" height="60"/>
<rect key="contentStretch" x="0.0" y="0.0" width="0.0" height="0.0"/>
</imageView>
</subviews>
<color key="backgroundColor" red="0.20392156862745098" green="0.59607843137254901" blue="0.85882352941176465" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstItem="23" firstAttribute="centerY" secondItem="yd7-JS-zBw" secondAttribute="centerY" priority="1" id="39"/>
<constraint firstItem="23" firstAttribute="centerX" secondItem="yd7-JS-zBw" secondAttribute="centerX" priority="1" id="41"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="XAI-xm-WK6" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="349" y="339"/>
</scene>
</scenes>
<resources>
<image name="Icon-60.png" width="180" height="180"/>
</resources>
</document>

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

View File

@@ -0,0 +1,30 @@
//
// ScreenSizeService.cs
//
// Trevi Awater
// 13-01-2022
//
// © Xamarin.Neo4j.iOS
//
using UIKit;
using Xamarin.Forms;
using Xamarin.Neo4j.iOS.Services;
using Xamarin.Neo4j.Services;
[assembly: Dependency(typeof(ScreenSizeService))]
namespace Xamarin.Neo4j.iOS.Services
{
public class ScreenSizeService : IScreenSizeService
{
public int GetScreenHeight()
{
return (int)UIScreen.MainScreen.Bounds.Height;
}
public int GetScreenWidth()
{
return (int)UIScreen.MainScreen.Bounds.Width;
}
}
}

View File

@@ -94,8 +94,10 @@
<MtouchLink>SdkOnly</MtouchLink> <MtouchLink>SdkOnly</MtouchLink>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="CustomRenderers\QueryEditorRenderer.cs" />
<Compile Include="Main.cs" /> <Compile Include="Main.cs" />
<Compile Include="AppDelegate.cs" /> <Compile Include="AppDelegate.cs" />
<Compile Include="Services\ScreenSizeService.cs" />
<None Include="Entitlements.plist" /> <None Include="Entitlements.plist" />
<None Include="Info.plist" /> <None Include="Info.plist" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
@@ -119,7 +121,7 @@
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Icon-76x76@2x.png" /> <ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Icon-76x76@2x.png" />
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Icon-83.5@2x.png" /> <ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Icon-83.5@2x.png" />
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Icon-marketing-1024x1024.png" /> <ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Icon-marketing-1024x1024.png" />
<InterfaceDefinition Include="Resources\LaunchScreen.storyboard" /> <InterfaceDefinition Include="LaunchScreen.storyboard" />
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Contents.json"> <ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Contents.json">
<Visible>false</Visible> <Visible>false</Visible>
</ImageAsset> </ImageAsset>
@@ -143,6 +145,8 @@
<BundleResource Include="Resources\iconize-fontawesome-brands.ttf" /> <BundleResource Include="Resources\iconize-fontawesome-brands.ttf" />
<BundleResource Include="Resources\iconize-fontawesome-regular.ttf" /> <BundleResource Include="Resources\iconize-fontawesome-regular.ttf" />
<BundleResource Include="Resources\iconize-fontawesome-solid.ttf" /> <BundleResource Include="Resources\iconize-fontawesome-solid.ttf" />
<BundleResource Include="Resources\logo.png" />
<BundleResource Include="Resources\resoftware.png" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" /> <Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
</Project> </Project>

View File

@@ -2,36 +2,4 @@
<Application xmlns="http://xamarin.com/schemas/2014/forms" <Application 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="Xamarin.Neo4j.App"> x:Class="Xamarin.Neo4j.App">
<Application.Resources>
<Color x:Key="Glitter">#e4ecf5</Color>
<Color x:Key="EbonyClay">#31333b</Color>
<OnPlatform x:TypeArguments="x:String" x:Key="FontAwesomeBrands">
<On Platform="Android" Value="FontAwesome5Brands.ttf#Regular" />
<On Platform="iOS" Value="FontAwesome5Brands-Regular" />
</OnPlatform>
<OnPlatform x:TypeArguments="x:String" x:Key="FontAwesomeSolid">
<On Platform="Android" Value="FontAwesome5Solid.ttf#Regular" />
<On Platform="iOS" Value="FontAwesome5Free-Solid" />
</OnPlatform>
<OnPlatform x:TypeArguments="x:String" x:Key="FontAwesomeRegular">
<On Platform="Android" Value="FontAwesome5Regular.ttf#Regular" />
<On Platform="iOS" Value="FontAwesome5Free-Regular" />
</OnPlatform>
<Style TargetType="ContentPage" ApplyToDerivedTypes="True">
<Setter Property="BackgroundColor" Value="{StaticResource Glitter}" />
</Style>
<Style TargetType="ListView">
<Setter Property="BackgroundColor" Value="Transparent" />
</Style>
<Style TargetType="NavigationPage">
<Setter Property="BarBackgroundColor" Value="{StaticResource EbonyClay}"/>
<Setter Property="BarTextColor" Value="White" />
</Style>
</Application.Resources>
</Application> </Application>

View File

@@ -2,6 +2,7 @@
using Xamarin.Forms; using Xamarin.Forms;
using Xamarin.Forms.Xaml; using Xamarin.Forms.Xaml;
using Xamarin.Neo4j.Pages; using Xamarin.Neo4j.Pages;
using Xamarin.Neo4j.Themes;
[assembly: XamlCompilation(XamlCompilationOptions.Compile)] [assembly: XamlCompilation(XamlCompilationOptions.Compile)]
@@ -13,9 +14,24 @@ namespace Xamarin.Neo4j
{ {
InitializeComponent(); InitializeComponent();
SetTheme(Current.RequestedTheme);
MainPage = new NavigationPage(new RootPage()); MainPage = new NavigationPage(new RootPage());
} }
private void SetTheme(OSAppTheme theme)
{
Resources = theme switch
{
OSAppTheme.Dark => new DarkTheme(),
OSAppTheme.Light => new LightTheme(),
_ => new LightTheme()
};
}
protected override void OnStart() protected override void OnStart()
{ {
// Handle when your app starts // Handle when your app starts

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:fonts="clr-namespace:Xamarin.Neo4j.Fonts;assembly=Xamarin.Neo4j"
x:Class="Xamarin.Neo4j.Controls.ConnectionCell">
<Grid Padding="15, 10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" x:Name="isActiveIndicator" Text="{x:Static fonts:FontAwesomeSolid.Circle}" FontFamily="{StaticResource FontAwesomeSolid}" VerticalOptions="Center" TextColor="#34C759" Margin="0 ,0, 7.5, 0"/>
<StackLayout Grid.Column="1" Spacing="2">
<Label Text="{Binding Name}" />
<Label Text="{Binding Host}" FontSize="Small" TextColor="{StaticResource SecondaryTextColor}" />
</StackLayout>
</Grid>
</ViewCell>

View File

@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace Xamarin.Neo4j.Controls
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ConnectionCell : ViewCell
{
public ConnectionCell()
{
InitializeComponent();
isActiveIndicator.SetBinding(VisualElement.IsVisibleProperty, new Binding(nameof(IsActive), source: this));
}
#region Bindable Properties
public bool IsActive
{
get => (bool)GetValue(IsActiveProperty);
set => SetValue(IsActiveProperty, value);
}
public static BindableProperty IsActiveProperty = BindableProperty.Create("IsActive", typeof(bool),
typeof(ConnectionCell), false, BindingMode.OneWay);
#endregion
}
}

View File

@@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:pancakeView="clr-namespace:Xamarin.Forms.PancakeView;assembly=Xamarin.Forms.PancakeView"
xmlns:fonts="clr-namespace:Xamarin.Neo4j.Fonts;assembly=Xamarin.Neo4j"
x:Class="Xamarin.Neo4j.Controls.QueryBuilder">
<pancakeView:PancakeView BackgroundColor="White" CornerRadius="2" Padding="4" Margin="16">
<pancakeView:PancakeView CornerRadius="2">
<pancakeView:PancakeView.Border>
<pancakeView:Border Thickness="2" Color="#d8e5f1" />
</pancakeView:PancakeView.Border>
<StackLayout Orientation="Horizontal">
<Editor x:Name="editor" HorizontalOptions="FillAndExpand" AutoSize="TextChanges" />
<Button Clicked="InvokeExecuteQuery" Text="{x:Static fonts:FontAwesomeSolid.Play}" FontFamily="{StaticResource FontAwesomeSolid}" VerticalOptions="CenterAndExpand" HorizontalOptions="End" FontSize="20" Margin="0, 0, 5, 0" />
</StackLayout>
</pancakeView:PancakeView>
</pancakeView:PancakeView>
</ContentView>

View File

@@ -1,42 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace Xamarin.Neo4j.Controls
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class QueryBuilder : ContentView
{
public event EventHandler ExecuteQuery;
public QueryBuilder()
{
InitializeComponent();
editor.SetBinding(Editor.TextProperty, new Binding(nameof(Query), source: this));
}
#region Bindable Properties
public string Query
{
get => (string)GetValue(QueryProperty);
set => SetValue(QueryProperty, value);
}
public static BindableProperty QueryProperty = BindableProperty.Create("Query", typeof(string),
typeof(QueryBuilder), string.Empty, BindingMode.TwoWay);
#endregion
private void InvokeExecuteQuery (object sender, EventArgs e)
{
ExecuteQuery?.Invoke(sender, e);
}
}
}

View File

@@ -0,0 +1,31 @@
//
// QueryEditor.cs
//
// Trevi Awater
// 13-01-2022
//
// © Xamarin.Neo4j
//
using System;
using Xamarin.Forms;
namespace Xamarin.Neo4j.Controls
{
public class QueryEditor : Editor
{
public double MaxHeight { get; set; }
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{
var sizeRequest = base.OnMeasure(widthConstraint, heightConstraint);
var newHeight = sizeRequest.Request.Height;
if (newHeight > MaxHeight)
newHeight = MaxHeight;
return new SizeRequest(new Size(sizeRequest.Request.Width, newHeight));
}
}
}

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:fonts="clr-namespace:Xamarin.Neo4j.Fonts;assembly=Xamarin.Neo4j"
x:Class="Xamarin.Neo4j.Controls.QueryResultCell">
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Query}" />
<Button Clicked="OpenNeovis" Text="{x:Static fonts:FontAwesomeSolid.ProjectDiagram}" FontFamily="{StaticResource FontAwesomeSolid}" VerticalOptions="CenterAndExpand" FontSize="20" Margin="0, 0, 5, 0" />
</StackLayout>
</ViewCell>

View File

@@ -1,31 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using Xamarin.Neo4j.Models;
using Xamarin.Neo4j.Pages;
namespace Xamarin.Neo4j.Controls
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class QueryResultCell : ViewCell
{
public QueryResult QueryResult => (QueryResult) BindingContext;
public QueryResultCell()
{
InitializeComponent();
}
private async void OpenNeovis(object sender, EventArgs e)
{
await App.Current.MainPage.Navigation.PushAsync(new VisualizationPage(QueryResult));
}
}
}

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:fonts="clr-namespace:Xamarin.Neo4j.Fonts;assembly=Xamarin.Neo4j"
xmlns:pancakeView="clr-namespace:Xamarin.Forms.PancakeView;assembly=Xamarin.Forms.PancakeView"
Margin="0, 0, 0, 25"
x:Class="Xamarin.Neo4j.Controls.QueryResultView">
<StackLayout Orientation="Vertical" Spacing="0">
<pancakeView:PancakeView>
<pancakeView:PancakeView.Border>
<pancakeView:Border Thickness="4" Color="{StaticResource QueryTextBorder}" />
</pancakeView:PancakeView.Border>
<Label Text="{Binding Query}" Padding="8" HorizontalOptions="FillAndExpand" BackgroundColor="{StaticResource Extreme}" />
</pancakeView:PancakeView>
<WebView x:Name="graphView" HorizontalOptions="FillAndExpand" IsVisible="{Binding CanDisplayGraph}" HeightRequest="200" />
<Grid BackgroundColor="{StaticResource QueryActionBar}" Padding="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Clicked="SaveQuery" Text="{x:Static fonts:FontAwesomeSolid.Star}" BackgroundColor="Transparent" TextColor="White" FontFamily="{StaticResource FontAwesomeSolid}" FontSize="20" />
<Button Grid.Column="1" Clicked="OpenNeovis" Text="{x:Static fonts:FontAwesomeSolid.Expand}" BackgroundColor="Transparent" IsEnabled="{Binding CanDisplayGraph}" TextColor="White" FontFamily="{StaticResource FontAwesomeSolid}" FontSize="20 "/>
<Button Grid.Column="2" Clicked="OpenTableView" Text="{x:Static fonts:FontAwesomeSolid.List}" BackgroundColor="Transparent" TextColor="White" FontFamily="{StaticResource FontAwesomeSolid}" FontSize="20 "/>
<Button Grid.Column="3" Clicked="CloseResultView" Text="{x:Static fonts:FontAwesomeSolid.Times}" BackgroundColor="Transparent" TextColor="White" FontFamily="{StaticResource FontAwesomeSolid}" FontSize="20 "/>
</Grid>
</StackLayout>
</ContentView>

View File

@@ -0,0 +1,103 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Acr.UserDialogs;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using Xamarin.Neo4j.Managers;
using Xamarin.Neo4j.Models;
using Xamarin.Neo4j.Pages;
using Xamarin.Neo4j.Utilities;
namespace Xamarin.Neo4j.Controls
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class QueryResultView : ContentView
{
private QueryResult QueryResult => (QueryResult) BindingContext;
public event EventHandler<GenericEventArgs<QueryResult>> CloseRequested;
private string _neovisHtml;
public QueryResultView()
{
InitializeComponent();
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == nameof(BindingContext))
{
ParseNeovisHtml();
graphView.Source = new HtmlWebViewSource()
{
Html = _neovisHtml
};
}
}
private void ParseNeovisHtml()
{
var assembly = Assembly.GetExecutingAssembly();
var resourceName = "Xamarin.Neo4j.Visualization.neovis.html";
using (var stream = assembly.GetManifestResourceStream(resourceName))
using (var reader = new StreamReader(stream))
{
var result = reader.ReadToEnd();
result = result.Replace("{{host}}", QueryResult.ConnectionString.Host);
result = result.Replace("{{port}}", QueryResult.ConnectionString.Port.ToString());
result = result.Replace("{{database}}", QueryResult.ConnectionString.Database);
result = result.Replace("{{username}}", QueryResult.ConnectionString.Username);
result = result.Replace("{{password}}", QueryResult.ConnectionString.Password);
result = result.Replace("{{encryption}}", QueryResult.ConnectionString.Encrypted ? "ENCRYPTION_ON" : "ENCRYPTION_OFF");
result = result.Replace("{{query}}", QueryResult.Query);
result = result.Replace("{{backgroundColor}}", App.Current.RequestedTheme == OSAppTheme.Dark ? "#292C31" : "#FFFFFF");
_neovisHtml = result;
}
}
private async void OpenNeovis(object sender, EventArgs e)
{
await Application.Current.MainPage.Navigation.PushAsync(new GraphPage(_neovisHtml));
}
private async void SaveQuery(object sender, EventArgs e)
{
var queryNameResult = await UserDialogs.Instance.PromptAsync("How should the query be called?");
if (queryNameResult.Ok)
{
var query = new Query()
{
Id = Guid.NewGuid(),
QueryText = QueryResult.Query,
Name = queryNameResult.Value,
};
SavedQueryManager.AddSavedQuery(query);
}
}
private void CloseResultView(object sender, EventArgs e)
{
CloseRequested?.Invoke(sender, new GenericEventArgs<QueryResult>(QueryResult));
}
private async void OpenTableView(object sender, EventArgs e)
{
await Application.Current.MainPage.Navigation.PushAsync(new TablePage(QueryResult));
}
}
}

View File

@@ -0,0 +1,33 @@
//
// IsActiveConnectionConverter.cs
//
// Trevi Awater
// 13-01-2022
//
// © Xamarin.Neo4j
//
using System;
using System.Globalization;
using Xamarin.Forms;
using Xamarin.Neo4j.Managers;
using Xamarin.Neo4j.Models;
namespace Xamarin.Neo4j.Converters
{
public class IsActiveConnectionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Neo4jConnectionString targetConnection)
return targetConnection.Id == ConnectionStringManager.ActiveConnectionString?.Id;
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
}

View File

@@ -0,0 +1,29 @@
//
// JsonConvertConverter.cs
//
// Trevi Awater
// 13-01-2022
//
// © Xamarin.Neo4j
//
using System;
using System.Globalization;
using Newtonsoft.Json;
using Xamarin.Forms;
namespace Xamarin.Neo4j.Converters
{
public class JsonConvertConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return JsonConvert.SerializeObject(value, Formatting.Indented);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -22,6 +22,8 @@ namespace Xamarin.Neo4j.Managers
{ {
private const string ConnectionStringsKey = "connection_strings"; private const string ConnectionStringsKey = "connection_strings";
public static Neo4jConnectionString ActiveConnectionString { get; set; }
public static async Task<List<Neo4jConnectionString>> GetConnectionStrings() public static async Task<List<Neo4jConnectionString>> GetConnectionStrings()
{ {
var json = await SecureStorage.GetAsync(ConnectionStringsKey); var json = await SecureStorage.GetAsync(ConnectionStringsKey);

View File

@@ -0,0 +1,59 @@
//
// SavedQueryManager.cs
//
// Trevi Awater
// 11-01-2022
//
// © Xamarin.Neo4j
//
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Xamarin.Essentials;
using Xamarin.Neo4j.Models;
namespace Xamarin.Neo4j.Managers
{
public class SavedQueryManager
{
private const string SavedQueriesKey = "saved_queries";
public static List<Query> GetSavedQueries()
{
var json = Preferences.Get(SavedQueriesKey, null);
if (string.IsNullOrEmpty(json))
return new List<Query>();
return JsonConvert.DeserializeObject<List<Query>>(json);
}
public static void AddSavedQuery(Query query)
{
var savedQueries = GetSavedQueries();
savedQueries.Add(query);
SaveSavedQueries(savedQueries);
}
private static void SaveSavedQueries(IEnumerable<Query> savedQueries)
{
var json = JsonConvert.SerializeObject(savedQueries);
Preferences.Set(SavedQueriesKey, json);
}
public static void DeleteSavedQuery(Query query)
{
var savedQueries = GetSavedQueries();
savedQueries = savedQueries.Where(cs => cs.Id != query.Id)
.ToList();
SaveSavedQueries(savedQueries);
}
}
}

View File

@@ -0,0 +1,22 @@
//
// Query.cs
//
// Trevi Awater
// 11-01-2022
//
// © Xamarin.Neo4j
//
using System;
namespace Xamarin.Neo4j.Models
{
public class Query
{
public Guid Id { get; set; }
public string Name { get; set; }
public string QueryText { get; set; }
}
}

View File

@@ -7,6 +7,7 @@
// © Xamarin.Neo4j // © Xamarin.Neo4j
// //
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using Neo4j.Driver; using Neo4j.Driver;
@@ -15,16 +16,18 @@ namespace Xamarin.Neo4j.Models
{ {
public class QueryResult public class QueryResult
{ {
public Guid Id { get; set; }
public bool Success { get; set; } public bool Success { get; set; }
public bool CanDisplayGraph { get; set; }
public string ErrorMessage { get; set; } public string ErrorMessage { get; set; }
public string Query { get; set; } public string Query { get; set; }
public Neo4jConnectionString ConnectionString { get; set; } public Neo4jConnectionString ConnectionString { get; set; }
public IEnumerable<INode> Nodes { get; set; } public Dictionary<string, List<object>> Results { get; set; }
public IEnumerable<IRelationship> Relationships { get; set; }
} }
} }

View File

@@ -32,17 +32,9 @@
<Entry Text="{Binding Password}" IsPassword="True" /> <Entry Text="{Binding Password}" IsPassword="True" />
</StackLayout> </StackLayout>
<Grid> <Button Text="Connect" BackgroundColor="#2DB4A8" TextColor="White" Command="{Binding Commands[Connect]}" />
<Grid.ColumnDefinitions> <Button Text="Test" BackgroundColor="Orange" TextColor="White" Command="{Binding Commands[Test]}" />
<ColumnDefinition Width="1*" /> <Button Text="Save" BackgroundColor="#89972E" TextColor="White" Command="{Binding Commands[Save]}" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Text="Connect" Command="{Binding Commands[Connect]}" />
<Button Grid.Column="1" Text="Test" Command="{Binding Commands[Test]}" />
<Button Grid.Column="2" Text="Save" Command="{Binding Commands[Save]}" />
</Grid>
</StackLayout> </StackLayout>
</ScrollView> </ScrollView>
</ContentPage.Content> </ContentPage.Content>

View File

@@ -3,9 +3,17 @@
<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"
xmlns:fonts="clr-namespace:Xamarin.Neo4j.Fonts;assembly=Xamarin.Neo4j" xmlns:fonts="clr-namespace:Xamarin.Neo4j.Fonts;assembly=Xamarin.Neo4j"
xmlns:controls="clr-namespace:Xamarin.Neo4j.Controls;assembly=Xamarin.Neo4j"
xmlns:converters="clr-namespace:Xamarin.Neo4j.Converters;assembly=Xamarin.Neo4j"
x:Class="Xamarin.Neo4j.Pages.ConnectionsPage" x:Class="Xamarin.Neo4j.Pages.ConnectionsPage"
x:Name="connectionsPage" x:Name="connectionsPage"
Title="Connections"> Title="Connections">
<ContentPage.Resources>
<ResourceDictionary>
<converters:IsActiveConnectionConverter x:Key="isActiveConnection" />
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.IconImageSource> <ContentPage.IconImageSource>
<FontImageSource <FontImageSource
FontFamily="{StaticResource FontAwesomeSolid}" FontFamily="{StaticResource FontAwesomeSolid}"
@@ -14,6 +22,14 @@
</ContentPage.IconImageSource> </ContentPage.IconImageSource>
<ContentPage.ToolbarItems> <ContentPage.ToolbarItems>
<ToolbarItem Command="{Binding Commands[StartSession]}">
<ToolbarItem.IconImageSource>
<FontImageSource
FontFamily="{StaticResource FontAwesomeSolid}"
Glyph="{x:Static fonts:FontAwesomeSolid.Terminal}" Size="20" />
</ToolbarItem.IconImageSource>
</ToolbarItem>
<ToolbarItem Command="{Binding Commands[AddConnection]}"> <ToolbarItem Command="{Binding Commands[AddConnection]}">
<ToolbarItem.IconImageSource> <ToolbarItem.IconImageSource>
<FontImageSource <FontImageSource
@@ -24,14 +40,14 @@
</ContentPage.ToolbarItems> </ContentPage.ToolbarItems>
<ContentPage.Content> <ContentPage.Content>
<ListView ItemsSource="{Binding ConnectionStrings}" ItemTapped="StartSession"> <ListView ItemsSource="{Binding ConnectionStrings}" HasUnevenRows="True" SelectionMode="None" ItemTapped="SetActive">
<ListView.ItemTemplate> <ListView.ItemTemplate>
<DataTemplate> <DataTemplate>
<TextCell Text="{Binding Name}" Detail="{Binding Host}"> <controls:ConnectionCell IsActive="{Binding ., Converter={StaticResource isActiveConnection}}">
<TextCell.ContextActions> <controls:ConnectionCell.ContextActions>
<MenuItem Text="Delete" Command="{Binding BindingContext.Commands[DeleteConnectionString], Source={x:Reference connectionsPage} }" CommandParameter="{Binding .}" IsDestructive="true" /> <MenuItem Text="Delete" Command="{Binding BindingContext.Commands[DeleteConnectionString], Source={x:Reference connectionsPage} }" CommandParameter="{Binding .}" IsDestructive="true" />
</TextCell.ContextActions> </controls:ConnectionCell.ContextActions>
</TextCell> </controls:ConnectionCell>
</DataTemplate> </DataTemplate>
</ListView.ItemTemplate> </ListView.ItemTemplate>
</ListView> </ListView>

View File

@@ -6,6 +6,7 @@ using System.Threading.Tasks;
using Xamarin.Forms; using Xamarin.Forms;
using Xamarin.Forms.Xaml; using Xamarin.Forms.Xaml;
using Xamarin.Neo4j.Managers;
using Xamarin.Neo4j.Models; using Xamarin.Neo4j.Models;
using Xamarin.Neo4j.ViewModels; using Xamarin.Neo4j.ViewModels;
@@ -30,11 +31,9 @@ namespace Xamarin.Neo4j.Pages
base.OnAppearing(); base.OnAppearing();
} }
private async void StartSession(object sender, ItemTappedEventArgs e) private void SetActive(object sender, ItemTappedEventArgs e)
{ {
var connectionString = (Neo4jConnectionString)e.Item; ViewModel.SetActiveConnectionString((Neo4jConnectionString)e.Item);
await Navigation.PushAsync(new SessionPage(connectionString));
} }
} }
} }

View File

@@ -3,7 +3,7 @@
<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"
Title="Graph" Title="Graph"
x:Class="Xamarin.Neo4j.Pages.VisualizationPage"> x:Class="Xamarin.Neo4j.Pages.GraphPage">
<ContentPage.Content> <ContentPage.Content>
<WebView Source="{Binding Source}" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" /> <WebView Source="{Binding Source}" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" />
</ContentPage.Content> </ContentPage.Content>

View File

@@ -12,13 +12,13 @@ using Xamarin.Neo4j.ViewModels;
namespace Xamarin.Neo4j.Pages namespace Xamarin.Neo4j.Pages
{ {
[XamlCompilation(XamlCompilationOptions.Compile)] [XamlCompilation(XamlCompilationOptions.Compile)]
public partial class VisualizationPage : ContentPage public partial class GraphPage : ContentPage
{ {
public VisualizationPage(QueryResult queryResult) public GraphPage(string html)
{ {
InitializeComponent(); InitializeComponent();
BindingContext = new VisualizationViewModel(Navigation, queryResult); BindingContext = new GraphViewModel(Navigation, html);
} }
} }
} }

View File

@@ -4,6 +4,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:fonts="clr-namespace:Xamarin.Neo4j.Fonts;assembly=Xamarin.Neo4j" xmlns:fonts="clr-namespace:Xamarin.Neo4j.Fonts;assembly=Xamarin.Neo4j"
Title="Queries" Title="Queries"
x:Name="queriesPage"
x:Class="Xamarin.Neo4j.Pages.QueriesPage"> x:Class="Xamarin.Neo4j.Pages.QueriesPage">
<ContentPage.IconImageSource> <ContentPage.IconImageSource>
<FontImageSource <FontImageSource
@@ -13,10 +14,16 @@
</ContentPage.IconImageSource> </ContentPage.IconImageSource>
<ContentPage.Content> <ContentPage.Content>
<StackLayout> <ListView ItemsSource="{Binding Queries}" ItemTapped="StartSessionWithQuery">
<Label Text="Hello, world!" <ListView.ItemTemplate>
VerticalOptions="CenterAndExpand" <DataTemplate>
HorizontalOptions="CenterAndExpand" /> <TextCell Text="{Binding Name}" Detail="{Binding QueryText}">
</StackLayout> <TextCell.ContextActions>
<MenuItem Text="Delete" Command="{Binding BindingContext.Commands[DeleteQuery], Source={x:Reference queriesPage} }" CommandParameter="{Binding .}" IsDestructive="true" />
</TextCell.ContextActions>
</TextCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content> </ContentPage.Content>
</ContentPage> </ContentPage>

View File

@@ -6,6 +6,7 @@ using System.Threading.Tasks;
using Xamarin.Forms; using Xamarin.Forms;
using Xamarin.Forms.Xaml; using Xamarin.Forms.Xaml;
using Xamarin.Neo4j.Models;
using Xamarin.Neo4j.ViewModels; using Xamarin.Neo4j.ViewModels;
namespace Xamarin.Neo4j.Pages namespace Xamarin.Neo4j.Pages
@@ -13,11 +14,25 @@ namespace Xamarin.Neo4j.Pages
[XamlCompilation(XamlCompilationOptions.Compile)] [XamlCompilation(XamlCompilationOptions.Compile)]
public partial class QueriesPage : ContentPage public partial class QueriesPage : ContentPage
{ {
private QueriesViewModel ViewModel => (QueriesViewModel)BindingContext;
public QueriesPage() public QueriesPage()
{ {
InitializeComponent(); InitializeComponent();
BindingContext = new QueriesViewModel(Navigation); BindingContext = new QueriesViewModel(Navigation);
} }
protected override void OnAppearing()
{
ViewModel.LoadQueries();
base.OnAppearing();
}
private void StartSessionWithQuery(object sender, ItemTappedEventArgs e)
{
ViewModel.StartSession((Query)e.Item);
}
} }
} }

View File

@@ -7,5 +7,6 @@
<TabbedPage.Children> <TabbedPage.Children>
<pages:ConnectionsPage /> <pages:ConnectionsPage />
<pages:QueriesPage /> <pages:QueriesPage />
<pages:SettingsPage />
</TabbedPage.Children> </TabbedPage.Children>
</TabbedPage> </TabbedPage>

View File

@@ -4,6 +4,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:fonts="clr-namespace:Xamarin.Neo4j.Fonts;assembly=Xamarin.Neo4j" xmlns:fonts="clr-namespace:Xamarin.Neo4j.Fonts;assembly=Xamarin.Neo4j"
xmlns:controls="clr-namespace:Xamarin.Neo4j.Controls;assembly=Xamarin.Neo4j" xmlns:controls="clr-namespace:Xamarin.Neo4j.Controls;assembly=Xamarin.Neo4j"
xmlns:pancakeView="clr-namespace:Xamarin.Forms.PancakeView;assembly=Xamarin.Forms.PancakeView"
x:Class="Xamarin.Neo4j.Pages.SessionPage"> x:Class="Xamarin.Neo4j.Pages.SessionPage">
<NavigationPage.TitleView> <NavigationPage.TitleView>
<StackLayout Spacing="0"> <StackLayout Spacing="0">
@@ -24,17 +25,35 @@
</StackLayout> </StackLayout>
</NavigationPage.TitleView> </NavigationPage.TitleView>
<ContentPage.ToolbarItems>
<ToolbarItem Command="{Binding Commands[ExecuteQuery]}">
<ToolbarItem.IconImageSource>
<FontImageSource
FontFamily="{StaticResource FontAwesomeSolid}"
Glyph="{x:Static fonts:FontAwesomeSolid.Play}" Size="20" />
</ToolbarItem.IconImageSource>
</ToolbarItem>
</ContentPage.ToolbarItems>
<ContentPage.Content> <ContentPage.Content>
<StackLayout> <StackLayout>
<controls:QueryBuilder ExecuteQuery="OnExecuteQuery" Query="{Binding Query}" /> <pancakeView:PancakeView BackgroundColor="White" CornerRadius="2" Padding="4" Margin="16">
<pancakeView:PancakeView CornerRadius="2">
<pancakeView:PancakeView.Border>
<pancakeView:Border Thickness="2" Color="#d8e5f1" />
</pancakeView:PancakeView.Border>
<ListView ItemsSource="{Binding QueryResults}"> <controls:QueryEditor Text="{Binding Query}" MaxHeight="200" HorizontalOptions="FillAndExpand" AutoSize="TextChanges" />
<ListView.ItemTemplate> </pancakeView:PancakeView>
</pancakeView:PancakeView>
<CollectionView x:Name="resultsCollection" ItemsSource="{Binding QueryResults}">
<CollectionView.ItemTemplate>
<DataTemplate> <DataTemplate>
<controls:QueryResultCell /> <controls:QueryResultView CloseRequested="CloseResultView" />
</DataTemplate> </DataTemplate>
</ListView.ItemTemplate> </CollectionView.ItemTemplate>
</ListView> </CollectionView>
</StackLayout> </StackLayout>
</ContentPage.Content> </ContentPage.Content>
</ContentPage> </ContentPage>

View File

@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using Xamarin.Forms; using Xamarin.Forms;
using Xamarin.Forms.Xaml; using Xamarin.Forms.Xaml;
using Xamarin.Neo4j.Models; using Xamarin.Neo4j.Models;
using Xamarin.Neo4j.Utilities;
using Xamarin.Neo4j.ViewModels; using Xamarin.Neo4j.ViewModels;
namespace Xamarin.Neo4j.Pages namespace Xamarin.Neo4j.Pages
@@ -16,11 +17,16 @@ namespace Xamarin.Neo4j.Pages
{ {
private SessionViewModel ViewModel => (SessionViewModel) BindingContext; private SessionViewModel ViewModel => (SessionViewModel) BindingContext;
public SessionPage(Neo4jConnectionString connectionString) public SessionPage(Neo4jConnectionString connectionString, string initialQuery = null)
{ {
InitializeComponent(); InitializeComponent();
BindingContext = new SessionViewModel(Navigation, connectionString); BindingContext = new SessionViewModel(Navigation, connectionString, initialQuery);
MessagingCenter.Subscribe<SessionViewModel>(this, "ResetScroll", (sender) =>
{
resultsCollection.ScrollTo(0, 0, ScrollToPosition.Start, true);
});
} }
private void FocusDatabasePicker(object sender, EventArgs e) private void FocusDatabasePicker(object sender, EventArgs e)
@@ -28,9 +34,9 @@ namespace Xamarin.Neo4j.Pages
databasePicker.Focus(); databasePicker.Focus();
} }
private void OnExecuteQuery(object sender, EventArgs e) private void CloseResultView(object sender, GenericEventArgs<QueryResult> e)
{ {
ViewModel.ExecuteQuery(); ViewModel.DeleteQueryResult(e.Data);
} }
} }
} }

View File

@@ -0,0 +1,29 @@
<?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:fonts="clr-namespace:Xamarin.Neo4j.Fonts;assembly=Xamarin.Neo4j"
Title="Settings"
x:Class="Xamarin.Neo4j.Pages.SettingsPage">
<ContentPage.IconImageSource>
<FontImageSource
FontFamily="{StaticResource FontAwesomeSolid}"
Glyph="{x:Static fonts:FontAwesomeSolid.Cogs}"
Size="20" />
</ContentPage.IconImageSource>
<ContentPage.Content>
<ScrollView>
<StackLayout Spacing="15" Padding="25">
<StackLayout Spacing="15" VerticalOptions="EndAndExpand">
<Label Text="A product of" HorizontalOptions="Center" FontAttributes="Italic" FontSize="12" TextColor="{StaticResource SecondaryTextColor}" />
<Image Source="resoftware.png" HorizontalOptions="Center" WidthRequest="100">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Commands[OpenReSoftwareSite]}" />
</Image.GestureRecognizers>
</Image>
</StackLayout>
</StackLayout>
</ScrollView>
</ContentPage.Content>
</ContentPage>

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using Xamarin.Neo4j.ViewModels;
namespace Xamarin.Neo4j.Pages
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class SettingsPage : ContentPage
{
public SettingsPage()
{
InitializeComponent();
BindingContext = new SettingsViewModel(Navigation);
}
}
}

View File

@@ -0,0 +1,32 @@
<?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:converters="clr-namespace:Xamarin.Neo4j.Converters;assembly=Xamarin.Neo4j"
Title="{Binding Title}"
x:Class="Xamarin.Neo4j.Pages.TablePage">
<ContentPage.Resources>
<ResourceDictionary>
<converters:JsonConvertConverter x:Key="jsonConvert" />
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<CarouselView ItemsSource="{Binding Rows}" Position="{Binding Position}" HorizontalScrollBarVisibility="Never" VerticalScrollBarVisibility="Never">
<CarouselView.ItemTemplate>
<DataTemplate>
<CollectionView ItemsSource="{Binding .}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Margin="0, 0, 0, 25" Padding="10">
<Label Text="{Binding Key, StringFormat='{0}:'}" FontAttributes="Bold" />
<Label Text="{Binding Value, Converter={StaticResource jsonConvert}}" FontSize="Small" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
</ContentPage.Content>
</ContentPage>

View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using Xamarin.Neo4j.Models;
using Xamarin.Neo4j.ViewModels;
namespace Xamarin.Neo4j.Pages
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class TablePage : ContentPage
{
public TablePage(QueryResult queryResult)
{
InitializeComponent();
BindingContext = new TableViewModel(Navigation, queryResult);
}
}
}

View File

@@ -0,0 +1,9 @@
namespace Xamarin.Neo4j.Services
{
public interface IScreenSizeService
{
int GetScreenHeight();
int GetScreenWidth();
}
}

View File

@@ -87,16 +87,21 @@ namespace Xamarin.Neo4j.Services
public async Task<QueryResult> ExecuteQuery(string query, Neo4jConnectionString connectionString) public async Task<QueryResult> ExecuteQuery(string query, Neo4jConnectionString connectionString)
{ {
var results = new Dictionary<string, List<object>>();
var canDisplayGraph = false;
try try
{ {
var session = GraphClient.Driver.AsyncSession(d => d.WithDatabase(connectionString.Database)); var session = GraphClient.Driver.AsyncSession(d => d.WithDatabase(connectionString.Database));
var cursor = await session.RunAsync(query); var cursor = await session.RunAsync(query);
var nodes = new List<INode>();
var relationships = new List<IRelationship>();
while (await cursor.FetchAsync()) while (await cursor.FetchAsync())
{ {
foreach (var key in cursor.Current.Keys)
if (results.ContainsKey(key) == false)
results.Add(key, new List<object>());
foreach (var record in cursor.Current.Values) foreach (var record in cursor.Current.Values)
{ {
var value = record.Value; var value = record.Value;
@@ -104,11 +109,16 @@ namespace Xamarin.Neo4j.Services
switch (value) switch (value)
{ {
case INode _: case INode _:
nodes.Add(value.As<INode>()); canDisplayGraph = true;
results[record.Key].Add(value.As<INode>());
break; break;
case IRelationship _: case IRelationship _:
relationships.Add(value.As<IRelationship>()); results[record.Key].Add(value.As<IRelationship>());
break;
default:
results[record.Key].Add(value);
break; break;
} }
} }
@@ -116,11 +126,12 @@ namespace Xamarin.Neo4j.Services
return new QueryResult() return new QueryResult()
{ {
Id = Guid.NewGuid(),
Success = true, Success = true,
CanDisplayGraph = canDisplayGraph,
Query = query, Query = query,
ConnectionString = connectionString, ConnectionString = connectionString,
Nodes = nodes, Results = results
Relationships = relationships
}; };
} }
@@ -128,6 +139,7 @@ namespace Xamarin.Neo4j.Services
{ {
return new QueryResult() return new QueryResult()
{ {
Id = Guid.NewGuid(),
Success = false, Success = false,
Query = query, Query = query,
ConnectionString = connectionString, ConnectionString = connectionString,

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Xamarin.Neo4j.Themes.DarkTheme">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Fonts.xaml" />
</ResourceDictionary.MergedDictionaries>
<Color x:Key="PageBackground">#545863</Color>
<Color x:Key="SecondaryTextColor">#a6a7b2</Color>
<Color x:Key="Accent">#31333b</Color>
<Color x:Key="QueryTextBackground">Black</Color>
<Color x:Key="QueryTextBorder">#686868</Color>
<Color x:Key="QueryActionBar">#686868</Color>
<Color x:Key="Extreme">Black</Color>
<Style TargetType="ContentPage" ApplyToDerivedTypes="True">
<Setter Property="BackgroundColor" Value="{StaticResource PageBackground}" />
</Style>
<Style TargetType="ListView">
<Setter Property="BackgroundColor" Value="Transparent" />
<Setter Property="SeparatorColor" Value="LightGray" />
</Style>
<Style TargetType="NavigationPage">
<Setter Property="BarBackgroundColor" Value="{StaticResource Accent}"/>
<Setter Property="BarTextColor" Value="White" />
</Style>
<Style TargetType="TabbedPage" ApplyToDerivedTypes="True">
<Setter Property="BackgroundColor" Value="White"/>
<Setter Property="BarTextColor" Value="Black"/>
<Setter Property="SelectedTabColor" Value="White"/>
</Style>
<Style TargetType="Editor" ApplyToDerivedTypes="True">
<Setter Property="BackgroundColor" Value="White"/>
<Setter Property="TextColor" Value="Black"/>
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,23 @@
//
// DarkTheme_xaml.cs
//
// Trevi Awater
// 14-01-2022
//
// © Xamarin.Neo4j
//
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace Xamarin.Neo4j.Themes
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class DarkTheme : ResourceDictionary
{
public DarkTheme()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Xamarin.Neo4j.Themes.Fonts">
<OnPlatform x:TypeArguments="x:String" x:Key="FontAwesomeBrands">
<On Platform="Android" Value="FontAwesome5Brands.ttf#Regular" />
<On Platform="iOS" Value="FontAwesome5Brands-Regular" />
</OnPlatform>
<OnPlatform x:TypeArguments="x:String" x:Key="FontAwesomeSolid">
<On Platform="Android" Value="FontAwesome5Solid.ttf#Regular" />
<On Platform="iOS" Value="FontAwesome5Free-Solid" />
</OnPlatform>
<OnPlatform x:TypeArguments="x:String" x:Key="FontAwesomeRegular">
<On Platform="Android" Value="FontAwesome5Regular.ttf#Regular" />
<On Platform="iOS" Value="FontAwesome5Free-Regular" />
</OnPlatform>
</ResourceDictionary>

View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace Xamarin.Neo4j.Themes
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Fonts : ResourceDictionary
{
public Fonts()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Xamarin.Neo4j.Themes.LightTheme">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Fonts.xaml" />
</ResourceDictionary.MergedDictionaries>
<Color x:Key="PageBackground">#e4ecf5</Color>
<Color x:Key="SecondaryTextColor">#868a92</Color>
<Color x:Key="Accent">#31333b</Color>
<Color x:Key="QueryTextBackground">#e0e0e0</Color>
<Color x:Key="QueryTextBorder">#31333b</Color>
<Color x:Key="QueryActionBar">#31343B</Color>
<Color x:Key="Extreme">#FFFFFF</Color>
<Style TargetType="ContentPage" ApplyToDerivedTypes="True">
<Setter Property="BackgroundColor" Value="{StaticResource PageBackground}" />
</Style>
<Style TargetType="ListView">
<Setter Property="BackgroundColor" Value="Transparent" />
</Style>
<Style TargetType="NavigationPage">
<Setter Property="BarBackgroundColor" Value="{StaticResource Accent}"/>
<Setter Property="BarTextColor" Value="White" />
</Style>
<Style TargetType="TabbedPage" ApplyToDerivedTypes="True">
<Setter Property="BackgroundColor" Value="White"/>
<Setter Property="BarTextColor" Value="Black"/>
<Setter Property="SelectedTabColor" Value="{StaticResource Accent}"/>
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace Xamarin.Neo4j.Themes
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class LightTheme : ResourceDictionary
{
public LightTheme()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,23 @@
//
// GenericEventArgs.cs
//
// Trevi Awater
// 13-01-2022
//
// © Xamarin.Neo4j
//
using System;
namespace Xamarin.Neo4j.Utilities
{
public class GenericEventArgs<T> : EventArgs
{
public T Data { get; }
public GenericEventArgs(T data)
{
Data = data;
}
}
}

View File

@@ -10,6 +10,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Acr.UserDialogs;
using Xamarin.Forms; using Xamarin.Forms;
using Xamarin.Neo4j.Annotations; using Xamarin.Neo4j.Annotations;
using Xamarin.Neo4j.Managers; using Xamarin.Neo4j.Managers;
@@ -40,6 +41,18 @@ namespace Xamarin.Neo4j.ViewModels
LoadConnectionStrings(); LoadConnectionStrings();
})); }));
Commands.Add("StartSession", new Command(async () =>
{
if (ConnectionStringManager.ActiveConnectionString == null)
{
await UserDialogs.Instance.AlertAsync("Please select a connection before starting a session.");
return;
}
await Navigation.PushAsync(new SessionPage(ConnectionStringManager.ActiveConnectionString));
}));
} }
[NotifyPropertyChangedInvocator] [NotifyPropertyChangedInvocator]
@@ -53,6 +66,14 @@ namespace Xamarin.Neo4j.ViewModels
ConnectionStrings = await ConnectionStringManager.GetConnectionStrings(); ConnectionStrings = await ConnectionStringManager.GetConnectionStrings();
} }
public void SetActiveConnectionString(Neo4jConnectionString connectionString)
{
ConnectionStringManager.ActiveConnectionString = ConnectionStringManager.ActiveConnectionString?.Id == connectionString.Id ? null : connectionString;
// HACK: This causes the Converter to re-execute. This can be done much cleaner.
LoadConnectionStrings();
}
#region Bindable Properties #region Bindable Properties
public IEnumerable<Neo4jConnectionString> ConnectionStrings public IEnumerable<Neo4jConnectionString> ConnectionStrings
@@ -63,7 +84,7 @@ namespace Xamarin.Neo4j.ViewModels
{ {
_connectionStrings = value; _connectionStrings = value;
OnPropertyChanged(); OnPropertyChanged(nameof(ConnectionStrings));
} }
} }

View File

@@ -0,0 +1,56 @@
//
// VisualizationViewModel.cs
//
// Trevi Awater
// 10-01-2022
//
// © Xamarin.Neo4j
//
using System.ComponentModel;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using Xamarin.Forms;
using Xamarin.Neo4j.Annotations;
using Xamarin.Neo4j.Models;
namespace Xamarin.Neo4j.ViewModels
{
public class GraphViewModel : ViewModelBase, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private HtmlWebViewSource _source;
public GraphViewModel(INavigation navigation, string html) : base(navigation)
{
Source = new HtmlWebViewSource
{
Html = html
};
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#region Bindable Properties
public HtmlWebViewSource Source
{
get => _source;
set
{
_source = value;
OnPropertyChanged();
}
}
#endregion
}
}

View File

@@ -7,10 +7,15 @@
// © Xamarin.Neo4j // © Xamarin.Neo4j
// //
using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Acr.UserDialogs;
using Xamarin.Forms; using Xamarin.Forms;
using Xamarin.Neo4j.Annotations; using Xamarin.Neo4j.Annotations;
using Xamarin.Neo4j.Managers;
using Xamarin.Neo4j.Models;
using Xamarin.Neo4j.Pages;
namespace Xamarin.Neo4j.ViewModels namespace Xamarin.Neo4j.ViewModels
{ {
@@ -18,9 +23,31 @@ namespace Xamarin.Neo4j.ViewModels
{ {
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler PropertyChanged;
private IEnumerable<Query> _queries;
public QueriesViewModel(INavigation navigation) : base(navigation) public QueriesViewModel(INavigation navigation) : base(navigation)
{ {
Commands.Add("DeleteQuery", new Command((o) =>
{
if (!(o is Query query))
return;
SavedQueryManager.DeleteSavedQuery(query);
LoadQueries();
}));
}
public async void StartSession(Query query)
{
if (ConnectionStringManager.ActiveConnectionString == null)
{
await UserDialogs.Instance.AlertAsync("Please select a connection before starting a session.");
return;
}
await Navigation.PushAsync(new SessionPage(ConnectionStringManager.ActiveConnectionString, query.QueryText));
} }
[NotifyPropertyChangedInvocator] [NotifyPropertyChangedInvocator]
@@ -28,5 +55,26 @@ namespace Xamarin.Neo4j.ViewModels
{ {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
} }
public void LoadQueries()
{
Queries = SavedQueryManager.GetSavedQueries();
}
#region Bindable Properties
public IEnumerable<Query> Queries
{
get => _queries;
set
{
_queries = value;
OnPropertyChanged();
}
}
#endregion
} }
} }

View File

@@ -16,10 +16,12 @@ using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Acr.UserDialogs; using Acr.UserDialogs;
using Xamarin.Forms; using Xamarin.Forms;
using Xamarin.Forms.Internals;
using Xamarin.Neo4j.Annotations; using Xamarin.Neo4j.Annotations;
using Xamarin.Neo4j.Models; using Xamarin.Neo4j.Models;
using Xamarin.Neo4j.Pages; using Xamarin.Neo4j.Pages;
using Xamarin.Neo4j.Services; using Xamarin.Neo4j.Services;
using Xamarin.Neo4j.Utilities;
namespace Xamarin.Neo4j.ViewModels namespace Xamarin.Neo4j.ViewModels
{ {
@@ -29,6 +31,8 @@ namespace Xamarin.Neo4j.ViewModels
private readonly Neo4jService _neo4jService; private readonly Neo4jService _neo4jService;
private readonly IScreenSizeService _screenSizeService;
private Database _currentDatabase; private Database _currentDatabase;
private List<Database> _availableDatabases; private List<Database> _availableDatabases;
@@ -39,30 +43,36 @@ namespace Xamarin.Neo4j.ViewModels
private string _query; private string _query;
public SessionViewModel(INavigation navigation, Neo4jConnectionString connectionString) : base(navigation) public SessionViewModel(INavigation navigation, Neo4jConnectionString connectionString, string initialQuery) : base(navigation)
{ {
_connectionString = connectionString; _connectionString = connectionString;
_neo4jService = DependencyService.Resolve<Neo4jService>(); _neo4jService = DependencyService.Resolve<Neo4jService>();
_screenSizeService = DependencyService.Resolve<IScreenSizeService>();
Query = initialQuery;
QueryResults = new ObservableCollection<QueryResult>(); QueryResults = new ObservableCollection<QueryResult>();
Commands.Add("ExecuteQuery", new Command(async () =>
{
_connectionString.Database = CurrentDatabase.Name;
var result = await _neo4jService.ExecuteQuery(Query, _connectionString);
if (result.Success)
{
QueryResults.Insert(0, result);
MessagingCenter.Send(this, "ResetScroll");
}
else
await UserDialogs.Instance.AlertAsync(result.ErrorMessage);
}));
InitializeConnection(connectionString); InitializeConnection(connectionString);
} }
public async void ExecuteQuery()
{
_connectionString.Database = CurrentDatabase.Name;
var result = await _neo4jService.ExecuteQuery(Query, _connectionString);
if (result.Success)
QueryResults.Add(result);
else
UserDialogs.Instance.Alert(result.ErrorMessage);
}
private async void InitializeConnection(Neo4jConnectionString connectionString) private async void InitializeConnection(Neo4jConnectionString connectionString)
{ {
await _neo4jService.EstablishConnection(connectionString); await _neo4jService.EstablishConnection(connectionString);
@@ -76,6 +86,13 @@ namespace Xamarin.Neo4j.ViewModels
CurrentDatabase = AvailableDatabases.Single(ad => ad.Default); CurrentDatabase = AvailableDatabases.Single(ad => ad.Default);
} }
public void DeleteQueryResult(QueryResult queryResult)
{
var index = QueryResults.IndexOf(qr => qr.Id == queryResult.Id);
QueryResults.RemoveAt(index);
}
[NotifyPropertyChangedInvocator] [NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{ {

View File

@@ -0,0 +1,37 @@
//
// SettingsViewModel.cs
//
// Trevi Awater
// 13-01-2022
//
// © Xamarin.Neo4j
//
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Xamarin.Essentials;
using Xamarin.Forms;
using Xamarin.Neo4j.Annotations;
namespace Xamarin.Neo4j.ViewModels
{
public class SettingsViewModel : ViewModelBase, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public SettingsViewModel(INavigation navigation) : base(navigation)
{
Commands.Add("OpenReSoftwareSite", new Command(async () =>
{
await Launcher.OpenAsync(new Uri("https://resoftware.nl/"));
}));
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@@ -0,0 +1,100 @@
//
// TableViewModel.cs
//
// Trevi Awater
// 13-01-2022
//
// © Xamarin.Neo4j
//
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using Xamarin.Forms;
using Xamarin.Neo4j.Annotations;
using Xamarin.Neo4j.Models;
namespace Xamarin.Neo4j.ViewModels
{
public class TableViewModel : ViewModelBase, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private IEnumerable<List<KeyValuePair<string, object>>> _rows;
private int _position;
public TableViewModel(INavigation navigation, QueryResult queryResult) : base(navigation)
{
var headers = queryResult.Results.Keys;
var transposed = Transpose(queryResult.Results.Select(a => a.Value.ToArray()).ToArray());
Rows = transposed.Select(row => row.Select((t, i) => new KeyValuePair<string, object>(headers.ElementAt(i), t)).ToList()).ToList();
}
private static IEnumerable<T[]> Transpose<T>(T[][] arr)
{
var rowCount = arr.Length;
var columnCount = arr[0].Length;
var transposed = new T[columnCount][];
if (rowCount == columnCount)
{
transposed = (T[][])arr.Clone();
for (var i = 1; i < rowCount; i++)
for (var j = 0; j < i; j++)
(transposed[i][j], transposed[j][i]) = (transposed[j][i], transposed[i][j]);
}
else
for (var column = 0; column < columnCount; column++)
{
transposed[column] = new T[rowCount];
for (var row = 0; row < rowCount; row++)
transposed[column][row] = arr[row][column];
}
return transposed;
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#region Bindable Properties
public IEnumerable<List<KeyValuePair<string, object>>> Rows
{
get => _rows;
set
{
_rows = value;
OnPropertyChanged();
}
}
public int Position
{
get => _position;
set
{
_position = value;
OnPropertyChanged(nameof(Position));
OnPropertyChanged(nameof(Title));
}
}
public string Title => $"{Position + 1}/{Rows.Count()}";
#endregion
}
}

View File

@@ -1,78 +0,0 @@
//
// VisualizationViewModel.cs
//
// Trevi Awater
// 10-01-2022
//
// © Xamarin.Neo4j
//
using System.ComponentModel;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using Xamarin.Forms;
using Xamarin.Neo4j.Annotations;
using Xamarin.Neo4j.Models;
namespace Xamarin.Neo4j.ViewModels
{
public class VisualizationViewModel : ViewModelBase, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private HtmlWebViewSource _source;
public VisualizationViewModel(INavigation navigation, QueryResult queryResult) : base(navigation)
{
ParseNeovisHtml(queryResult);
}
private void ParseNeovisHtml(QueryResult queryResult)
{
var assembly = Assembly.GetExecutingAssembly();
var resourceName = "Xamarin.Neo4j.Visualization.neovis.html";
using (var stream = assembly.GetManifestResourceStream(resourceName))
using (var reader = new StreamReader(stream))
{
var result = reader.ReadToEnd();
result = result.Replace("{{host}}", queryResult.ConnectionString.Host);
result = result.Replace("{{port}}", queryResult.ConnectionString.Port.ToString());
result = result.Replace("{{database}}", queryResult.ConnectionString.Database);
result = result.Replace("{{username}}", queryResult.ConnectionString.Username);
result = result.Replace("{{password}}", queryResult.ConnectionString.Password);
result = result.Replace("{{encryption}}", queryResult.ConnectionString.Encrypted ? "ENCRYPTION_ON" : "ENCRYPTION_OFF");
result = result.Replace("{{query}}", queryResult.Query);
Source = new HtmlWebViewSource
{
Html = result
};
}
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#region Bindable Properties
public HtmlWebViewSource Source
{
get => _source;
set
{
_source = value;
OnPropertyChanged();
}
}
#endregion
}
}

View File

@@ -1,41 +1,46 @@
<!doctype html> <!doctype html>
<html> <html lang="en-US">
<head> <head>
<title>Neovis.js Simple Example</title> <title>Graph</title>
<style type="text/css"> <style type="text/css">
html, body { html, body {
font: 16pt arial; font: 16pt arial;
margin: 0;
padding: 0;
height: 100%;
background-color: {{backgroundColor}};
} }
#viz { #viz {
width: 100vw; width: 100%;
height: 100vh; height: 100%;
border: 1px solid lightgray;
font: 22pt arial;
} }
</style> </style>
<script src="https://cdn.neo4jlabs.com/neovis.js/v1.5.0/neovis.js"></script> <script src="https://cdn.neo4jlabs.com/neovis.js/v1.5.0/neovis.js"></script>
</head> </head>
<body onload="draw()"> <body onload="draw()">
<div id="viz"></div> <div id="viz"></div>
<script type="text/javascript">
let viz;
function draw() {
const config = {
container_id: "viz",
server_url: `bolt://{{host}}:{{port}}`,
server_user: `{{username}}`,
server_database: `{{database}}`,
server_password: `{{password}}`,
encrypted: `{{encryption}}`,
initial_cypher: `{{query}}`
};
viz = new NeoVis.default(config);
viz.render();
}
</script>
</body> </body>
<script type="text/javascript">
var viz;
function draw() {
var config = {
container_id: "viz",
server_url: "bolt://{{host}}:{{port}}",
server_user: "{{username}}",
server_database: "{{database}}",
server_password: "{{password}}",
encrypted: "{{encryption}}",
initial_cypher: "{{query}}"
};
viz = new NeoVis.default(config);
viz.render();
}
</script>
</html> </html>

View File

@@ -2,6 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@@ -17,10 +18,6 @@
<PackageReference Include="Xamarin.Forms.PancakeView" Version="2.3.0.759" /> <PackageReference Include="Xamarin.Forms.PancakeView" Version="2.3.0.759" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Converters" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<None Remove="Visualization\neovis.html" /> <None Remove="Visualization\neovis.html" />
<EmbeddedResource Include="Visualization\neovis.html" /> <EmbeddedResource Include="Visualization\neovis.html" />