Feature: settings toggles, editable connection name, settings layout improvements

Settings:
- Add Editor section with auto-capitalize and auto-zoom toggles (blue when on)
- Reorder sections: Editor, Data, Support, About
- Move footer ("A product of" + logo) inside scroll instead of fixed at bottom

AddConnection:
- Add editable Name field at top of form
- Remove colons from field labels
- Save uses Name field instead of popup prompt
- Name falls back to host if left empty
- Fix UpdateConnectionString to persist Name field

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Gerwin Kuijntjes
2026-04-02 11:52:14 +02:00
parent eefb7fc0ea
commit d7a1a1e4ee
5 changed files with 220 additions and 156 deletions

View File

@@ -69,6 +69,7 @@ namespace Xamarin.Neo4j.Managers
if (connectionStringToUpdate == null) if (connectionStringToUpdate == null)
return; return;
connectionStringToUpdate.Name = connectionString.Name;
connectionStringToUpdate.Scheme = connectionString.Scheme; connectionStringToUpdate.Scheme = connectionString.Scheme;
connectionStringToUpdate.Host = connectionString.Host; connectionStringToUpdate.Host = connectionString.Host;
connectionStringToUpdate.Username = connectionString.Username; connectionStringToUpdate.Username = connectionString.Username;

View File

@@ -9,7 +9,12 @@
<ScrollView> <ScrollView>
<StackLayout Padding="24" Spacing="12"> <StackLayout Padding="24" Spacing="12">
<StackLayout Spacing="4"> <StackLayout Spacing="4">
<Label Text="Scheme:" FontAttributes="Bold" FontSize="Small" /> <Label Text="Name" FontAttributes="Bold" FontSize="Small" />
<Entry Text="{Binding Name}" Placeholder="My Database" />
</StackLayout>
<StackLayout Spacing="4">
<Label Text="Scheme" FontAttributes="Bold" FontSize="Small" />
<Picker SelectedItem="{Binding Scheme}"> <Picker SelectedItem="{Binding Scheme}">
<Picker.ItemsSource> <Picker.ItemsSource>
<x:Array Type="{x:Type x:String}"> <x:Array Type="{x:Type x:String}">
@@ -25,17 +30,17 @@
</StackLayout> </StackLayout>
<StackLayout Spacing="4"> <StackLayout Spacing="4">
<Label Text="Host:" FontAttributes="Bold" FontSize="Small" /> <Label Text="Host" FontAttributes="Bold" FontSize="Small" />
<Entry Text="{Binding Host}" /> <Entry Text="{Binding Host}" />
</StackLayout> </StackLayout>
<StackLayout Spacing="4"> <StackLayout Spacing="4">
<Label Text="Username:" FontAttributes="Bold" FontSize="Small" /> <Label Text="Username" FontAttributes="Bold" FontSize="Small" />
<Entry Text="{Binding Username}" Keyboard="Default" /> <Entry Text="{Binding Username}" Keyboard="Default" />
</StackLayout> </StackLayout>
<StackLayout Spacing="4"> <StackLayout Spacing="4">
<Label Text="Password:" FontAttributes="Bold" FontSize="Small" /> <Label Text="Password" FontAttributes="Bold" FontSize="Small" />
<Grid ColumnDefinitions="*,Auto"> <Grid ColumnDefinitions="*,Auto">
<Entry Grid.Column="0" x:Name="passwordEntry" <Entry Grid.Column="0" x:Name="passwordEntry"
Text="{Binding Password}" Text="{Binding Password}"

View File

@@ -16,147 +16,180 @@
<ScrollView> <ScrollView>
<StackLayout Padding="16" Spacing="24"> <StackLayout Padding="16" Spacing="24">
<!-- Support --> <!-- Editor -->
<StackLayout Spacing="8"> <StackLayout Spacing="8">
<Label Text="SUPPORT" <Label Text="EDITOR"
FontSize="11" FontAttributes="Bold" FontSize="11" FontAttributes="Bold"
CharacterSpacing="2" CharacterSpacing="2"
TextColor="{DynamicResource SecondaryTextColor}" TextColor="{DynamicResource SecondaryTextColor}"
Margin="4,0,0,0" /> Margin="4,0,0,0" />
<Border BackgroundColor="{DynamicResource Extreme}" <Border BackgroundColor="{DynamicResource Extreme}"
Stroke="{DynamicResource QueryTextBorder}" Stroke="{DynamicResource QueryTextBorder}"
StrokeThickness="1" StrokeThickness="1"
StrokeShape="RoundRectangle 4"> StrokeShape="RoundRectangle 4">
<StackLayout Spacing="0"> <StackLayout Spacing="0">
<Grid ColumnDefinitions="*,Auto" Padding="16,14"> <Grid ColumnDefinitions="*,Auto" Padding="16,10">
<Grid.GestureRecognizers> <Label Grid.Column="0" Text="Auto-capitalize keywords"
<TapGestureRecognizer Command="{Binding Commands[ContactSupport]}" /> TextColor="{DynamicResource PrimaryTextColor}"
</Grid.GestureRecognizers> VerticalOptions="Center" />
<Label Grid.Column="0" Text="Contact Support" <Switch Grid.Column="1" OnColor="#007AFF"
TextColor="{DynamicResource PrimaryTextColor}" IsToggled="{Binding AutoCapitalize}"
VerticalOptions="Center" /> VerticalOptions="Center" />
<Label Grid.Column="1" </Grid>
Text="{x:Static fonts:FontAwesomeSolid.ChevronRight}" <BoxView HeightRequest="1" BackgroundColor="{DynamicResource QueryTextBorder}" Margin="16,0" />
FontFamily="{StaticResource FontAwesomeSolid}" <Grid ColumnDefinitions="*,Auto" Padding="16,10">
FontSize="12" <Label Grid.Column="0" Text="Auto-zoom on node tap"
TextColor="{DynamicResource SecondaryTextColor}" TextColor="{DynamicResource PrimaryTextColor}"
VerticalOptions="Center" /> VerticalOptions="Center" />
</Grid> <Switch Grid.Column="1" OnColor="#007AFF"
<BoxView HeightRequest="1" BackgroundColor="{DynamicResource QueryTextBorder}" Margin="16,0" /> IsToggled="{Binding AutoZoom}"
<Grid ColumnDefinitions="*,Auto" Padding="16,14"> VerticalOptions="Center" />
<Grid.GestureRecognizers> </Grid>
<TapGestureRecognizer Command="{Binding Commands[RateApp]}" /> </StackLayout>
</Grid.GestureRecognizers> </Border>
<Label Grid.Column="0" Text="Rate PocketGraph"
TextColor="{DynamicResource PrimaryTextColor}"
VerticalOptions="Center" />
<Label Grid.Column="1"
Text="{x:Static fonts:FontAwesomeSolid.ChevronRight}"
FontFamily="{StaticResource FontAwesomeSolid}"
FontSize="12"
TextColor="{DynamicResource SecondaryTextColor}"
VerticalOptions="Center" />
</Grid>
</StackLayout>
</Border>
</StackLayout>
<!-- Data -->
<StackLayout Spacing="8">
<Label Text="DATA"
FontSize="11" FontAttributes="Bold"
CharacterSpacing="2"
TextColor="{DynamicResource SecondaryTextColor}"
Margin="4,0,0,0" />
<Border BackgroundColor="{DynamicResource Extreme}"
Stroke="{DynamicResource QueryTextBorder}"
StrokeThickness="1"
StrokeShape="RoundRectangle 4">
<StackLayout Spacing="0">
<Grid ColumnDefinitions="*,Auto" Padding="16,14">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Commands[ClearConnections]}" />
</Grid.GestureRecognizers>
<Label Grid.Column="0" Text="Clear Saved Connections"
TextColor="{DynamicResource PrimaryTextColor}"
VerticalOptions="Center" />
<Label Grid.Column="1"
Text="{x:Static fonts:FontAwesomeSolid.ChevronRight}"
FontFamily="{StaticResource FontAwesomeSolid}"
FontSize="12"
TextColor="{DynamicResource SecondaryTextColor}"
VerticalOptions="Center" />
</Grid>
<BoxView HeightRequest="1" BackgroundColor="{DynamicResource QueryTextBorder}" Margin="16,0" />
<Grid ColumnDefinitions="*,Auto" Padding="16,14">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Commands[ClearQueries]}" />
</Grid.GestureRecognizers>
<Label Grid.Column="0" Text="Clear Saved Queries"
TextColor="{DynamicResource PrimaryTextColor}"
VerticalOptions="Center" />
<Label Grid.Column="1"
Text="{x:Static fonts:FontAwesomeSolid.ChevronRight}"
FontFamily="{StaticResource FontAwesomeSolid}"
FontSize="12"
TextColor="{DynamicResource SecondaryTextColor}"
VerticalOptions="Center" />
</Grid>
</StackLayout>
</Border>
</StackLayout>
<!-- About -->
<StackLayout Spacing="8">
<Label Text="ABOUT"
FontSize="11" FontAttributes="Bold"
CharacterSpacing="2"
TextColor="{DynamicResource SecondaryTextColor}"
Margin="4,0,0,0" />
<Border BackgroundColor="{DynamicResource Extreme}"
Stroke="{DynamicResource QueryTextBorder}"
StrokeThickness="1"
StrokeShape="RoundRectangle 4">
<StackLayout Spacing="0">
<Grid ColumnDefinitions="*,Auto" Padding="16,14">
<Label Grid.Column="0" Text="{Binding VersionLabel}"
TextColor="{DynamicResource PrimaryTextColor}"
VerticalOptions="Center" />
<Label Grid.Column="1"
TextColor="{DynamicResource SecondaryTextColor}"
VerticalOptions="Center" />
</Grid>
<BoxView HeightRequest="1" BackgroundColor="{DynamicResource QueryTextBorder}" Margin="16,0" />
<Grid ColumnDefinitions="*,Auto" Padding="16,14">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Commands[OpenLicensesPage]}" />
</Grid.GestureRecognizers>
<Label Grid.Column="0" Text="Software Licenses"
TextColor="{DynamicResource PrimaryTextColor}"
VerticalOptions="Center" />
<Label Grid.Column="1"
Text="{x:Static fonts:FontAwesomeSolid.ChevronRight}"
FontFamily="{StaticResource FontAwesomeSolid}"
FontSize="12"
TextColor="{DynamicResource SecondaryTextColor}"
VerticalOptions="Center" />
</Grid>
</StackLayout>
</Border>
</StackLayout>
<!-- Footer -->
<StackLayout Spacing="15" Padding="9,16">
<Label Text="A product of" HorizontalOptions="Center" FontAttributes="Italic" FontSize="12"
TextColor="{DynamicResource SecondaryTextColor}" />
<Image Source="resoftware.png" HorizontalOptions="Center" WidthRequest="100">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Commands[OpenReSoftwareSite]}" />
</Image.GestureRecognizers>
</Image>
</StackLayout>
</StackLayout> </StackLayout>
</ScrollView>
<!-- Data -->
<StackLayout Spacing="8">
<Label Text="DATA"
FontSize="11" FontAttributes="Bold"
CharacterSpacing="2"
TextColor="{DynamicResource SecondaryTextColor}"
Margin="4,0,0,0" />
<Border BackgroundColor="{DynamicResource Extreme}"
Stroke="{DynamicResource QueryTextBorder}"
StrokeThickness="1"
StrokeShape="RoundRectangle 4">
<StackLayout Spacing="0">
<Grid ColumnDefinitions="*,Auto" Padding="16,14">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Commands[ClearConnections]}" />
</Grid.GestureRecognizers>
<Label Grid.Column="0" Text="Clear Saved Connections"
TextColor="{DynamicResource PrimaryTextColor}"
VerticalOptions="Center" />
<Label Grid.Column="1"
Text="{x:Static fonts:FontAwesomeSolid.ChevronRight}"
FontFamily="{StaticResource FontAwesomeSolid}"
FontSize="12"
TextColor="{DynamicResource SecondaryTextColor}"
VerticalOptions="Center" />
</Grid>
<BoxView HeightRequest="1" BackgroundColor="{DynamicResource QueryTextBorder}" Margin="16,0" />
<Grid ColumnDefinitions="*,Auto" Padding="16,14">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Commands[ClearQueries]}" />
</Grid.GestureRecognizers>
<Label Grid.Column="0" Text="Clear Saved Queries"
TextColor="{DynamicResource PrimaryTextColor}"
VerticalOptions="Center" />
<Label Grid.Column="1"
Text="{x:Static fonts:FontAwesomeSolid.ChevronRight}"
FontFamily="{StaticResource FontAwesomeSolid}"
FontSize="12"
TextColor="{DynamicResource SecondaryTextColor}"
VerticalOptions="Center" />
</Grid>
</StackLayout>
</Border>
</StackLayout>
<!-- Support -->
<StackLayout Spacing="8">
<Label Text="SUPPORT"
FontSize="11" FontAttributes="Bold"
CharacterSpacing="2"
TextColor="{DynamicResource SecondaryTextColor}"
Margin="4,0,0,0" />
<Border BackgroundColor="{DynamicResource Extreme}"
Stroke="{DynamicResource QueryTextBorder}"
StrokeThickness="1"
StrokeShape="RoundRectangle 4">
<StackLayout Spacing="0">
<Grid ColumnDefinitions="*,Auto" Padding="16,14">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Commands[ContactSupport]}" />
</Grid.GestureRecognizers>
<Label Grid.Column="0" Text="Contact Support"
TextColor="{DynamicResource PrimaryTextColor}"
VerticalOptions="Center" />
<Label Grid.Column="1"
Text="{x:Static fonts:FontAwesomeSolid.ChevronRight}"
FontFamily="{StaticResource FontAwesomeSolid}"
FontSize="12"
TextColor="{DynamicResource SecondaryTextColor}"
VerticalOptions="Center" />
</Grid>
<BoxView HeightRequest="1" BackgroundColor="{DynamicResource QueryTextBorder}" Margin="16,0" />
<Grid ColumnDefinitions="*,Auto" Padding="16,14">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Commands[RateApp]}" />
</Grid.GestureRecognizers>
<Label Grid.Column="0" Text="Rate PocketGraph"
TextColor="{DynamicResource PrimaryTextColor}"
VerticalOptions="Center" />
<Label Grid.Column="1"
Text="{x:Static fonts:FontAwesomeSolid.ChevronRight}"
FontFamily="{StaticResource FontAwesomeSolid}"
FontSize="12"
TextColor="{DynamicResource SecondaryTextColor}"
VerticalOptions="Center" />
</Grid>
</StackLayout>
</Border>
</StackLayout>
<!-- About -->
<StackLayout Spacing="8">
<Label Text="ABOUT"
FontSize="11" FontAttributes="Bold"
CharacterSpacing="2"
TextColor="{DynamicResource SecondaryTextColor}"
Margin="4,0,0,0" />
<Border BackgroundColor="{DynamicResource Extreme}"
Stroke="{DynamicResource QueryTextBorder}"
StrokeThickness="1"
StrokeShape="RoundRectangle 4">
<StackLayout Spacing="0">
<Grid ColumnDefinitions="*,Auto" Padding="16,14">
<Label Grid.Column="0" Text="{Binding VersionLabel}"
TextColor="{DynamicResource PrimaryTextColor}"
VerticalOptions="Center" />
<Label Grid.Column="1"
TextColor="{DynamicResource SecondaryTextColor}"
VerticalOptions="Center" />
</Grid>
<BoxView HeightRequest="1" BackgroundColor="{DynamicResource QueryTextBorder}" Margin="16,0" />
<Grid ColumnDefinitions="*,Auto" Padding="16,14">
<Grid.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Commands[OpenLicensesPage]}" />
</Grid.GestureRecognizers>
<Label Grid.Column="0" Text="Software Licenses"
TextColor="{DynamicResource PrimaryTextColor}"
VerticalOptions="Center" />
<Label Grid.Column="1"
Text="{x:Static fonts:FontAwesomeSolid.ChevronRight}"
FontFamily="{StaticResource FontAwesomeSolid}"
FontSize="12"
TextColor="{DynamicResource SecondaryTextColor}"
VerticalOptions="Center" />
</Grid>
</StackLayout>
</Border>
</StackLayout>
<!-- Footer — at end of scroll -->
<StackLayout Spacing="15" Padding="9,16,9,32">
<Label Text="A product of" HorizontalOptions="Center" FontAttributes="Italic" FontSize="12"
TextColor="{DynamicResource 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.Content>
</ContentPage> </ContentPage>

View File

@@ -26,7 +26,7 @@ namespace Xamarin.Neo4j.ViewModels
{ {
private Guid? _id; private Guid? _id;
private string _scheme, _host, _username, _password; private string _name, _scheme, _host, _username, _password;
private readonly Neo4jService _neo4jService; private readonly Neo4jService _neo4jService;
@@ -55,20 +55,13 @@ namespace Xamarin.Neo4j.ViewModels
{ {
var connectionString = BuildConnectionString(); var connectionString = BuildConnectionString();
if (string.IsNullOrWhiteSpace(connectionString.Name))
connectionString.Name = connectionString.Host;
if (_id.HasValue) if (_id.HasValue)
await ConnectionStringManager.UpdateConnectionString(_id.Value, connectionString); await ConnectionStringManager.UpdateConnectionString(_id.Value, connectionString);
else else
{
var name = await Application.Current.MainPage.DisplayPromptAsync("Save Connection", "How do you want to name this connection?", "Save", "Cancel");
if (string.IsNullOrWhiteSpace(name))
return;
connectionString.Name = name;
await ConnectionStringManager.AddConnectionString(connectionString); await ConnectionStringManager.AddConnectionString(connectionString);
}
await Navigation.PopAsync(); await Navigation.PopAsync();
})); }));
@@ -91,6 +84,7 @@ namespace Xamarin.Neo4j.ViewModels
{ {
_id = neo4JConnectionString.Id; _id = neo4JConnectionString.Id;
Name = neo4JConnectionString.Name;
Scheme = neo4JConnectionString.Scheme; Scheme = neo4JConnectionString.Scheme;
Host = neo4JConnectionString.Host; Host = neo4JConnectionString.Host;
Username = neo4JConnectionString.Username; Username = neo4JConnectionString.Username;
@@ -114,6 +108,7 @@ namespace Xamarin.Neo4j.ViewModels
return new Neo4jConnectionString return new Neo4jConnectionString
{ {
Id = Guid.NewGuid(), Id = Guid.NewGuid(),
Name = Name,
Scheme = Scheme, Scheme = Scheme,
Host = Host, Host = Host,
Username = Username, Username = Username,
@@ -123,6 +118,16 @@ namespace Xamarin.Neo4j.ViewModels
#region Bindable Properties #region Bindable Properties
public string Name
{
get => _name;
set
{
_name = value;
OnPropertyChanged(nameof(Name));
}
}
public string Scheme public string Scheme
{ {
get => _scheme; get => _scheme;

View File

@@ -141,6 +141,26 @@ namespace Xamarin.Neo4j.ViewModels
} }
} }
public bool AutoCapitalize
{
get => Preferences.Default.Get("auto_capitalize", true);
set
{
Preferences.Default.Set("auto_capitalize", value);
OnPropertyChanged(nameof(AutoCapitalize));
}
}
public bool AutoZoom
{
get => Preferences.Default.Get("auto_zoom", true);
set
{
Preferences.Default.Set("auto_zoom", value);
OnPropertyChanged(nameof(AutoZoom));
}
}
#endregion #endregion
} }
} }