Public Access
Give the application a settings area built from what really exists
This commit is contained in:
@@ -0,0 +1,407 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="using:DodoSSH.Client.Shell.ViewModels"
|
||||
xmlns:contracts="using:DodoSSH.Contracts"
|
||||
x:Class="DodoSSH.Client.App.Views.SettingsVaultsPage"
|
||||
x:DataType="vm:MainWindowViewModel"
|
||||
x:Name="Root">
|
||||
|
||||
<!--
|
||||
v5c-2: Vaults, against Settings-Vaults.dc.html. Replaces the old full-bleed VaultsScreen, which this
|
||||
settings page now covers completely — every command it had is reachable here exactly once, and
|
||||
VaultsScreen.axaml is gone. Its data is DataContext.Vaults (VaultsViewModel), reached as {Binding Vaults.*}
|
||||
throughout because this page's own DataContext is the shell, the same choice every other settings page
|
||||
makes.
|
||||
|
||||
── WHAT THE DESIGN DREW AND THIS PAGE DOES NOT ─────────────────────────────────────────────────────────
|
||||
"Manage devices" and "3 devices" in the sync line — no list-devices endpoint exists. The whole VAULT
|
||||
DEFAULTS card (auto-lock, require-password-on-unlock, relay toggle) and the whole RECOVERY card (kit,
|
||||
export) — none of the three exists; sync is always on. The magenta "Default" badge — the app does track a
|
||||
"new items go to" vault (VaultViewModel.TargetVaultId), but that lives on a different view model than the
|
||||
row being drawn here and cross-referencing the two per card would be more machinery than the badge is
|
||||
worth; dropped rather than faked. Per-vault UNLOCKED/LOCKED chips — this app locks the keychain as a
|
||||
whole, not one vault at a time, so there is no per-vault state for a chip to draw; the keychain-level fact
|
||||
lives in the sync card's dot instead. The "Unlock Rocateq" modal — same reason, there is nothing per-vault
|
||||
to unlock. Member avatar stacks on the card list — VaultRowViewModel carries a member *count*, not the
|
||||
members themselves; only the selected vault's Members collection is actually loaded, which is why avatars
|
||||
are drawn in the members panel below and nowhere else. The "Sorted by name ▾" control — decorative in the
|
||||
mock; Vaults.Vaults is already ordered personal-first-then-name and there is no second order to switch to.
|
||||
|
||||
── THE MEMBERS PANEL ────────────────────────────────────────────────────────────────────────────────────
|
||||
The design draws an 860px modal. This app's only overlay idiom below a whole-window mode is the scrim a
|
||||
Border with a translucent Background and a PointerPressed handler draws — see QuickConnect.axaml, the
|
||||
other place a click-away-to-close panel exists — so that is what this is, capped at MaxWidth 860 rather
|
||||
than fixed to it: the settings content column is narrower than 860 at the window's minimum, and a modal
|
||||
that insisted on the full width would arrange its own rows past the edge of what this page is given.
|
||||
|
||||
Inside it is the old screen's own right-hand column, restyled rather than reinvented: a selectable list of
|
||||
members, the SET ROLE buttons, the ADD row, REMOVE, HAND OVER, SHARE KEY/WITHDRAW KEY and the KEY HOLDERS
|
||||
list, on the same VaultsViewModel.SelectedMember selection the old screen used. Deleting a vault stays a
|
||||
card-level action (the danger icon below), not a members-panel one, so PendingAction is drawn in only one
|
||||
of the two places at a time — see the two IsVisible guards below, which key off IsMembersPanelOpen because
|
||||
HAND OVER can only ever be armed with the panel open (it needs a selected member) and DELETE only with it
|
||||
closed (the card's own icon is what arms it).
|
||||
|
||||
The sentence that must survive from the old screen: a vault cannot be deleted through the membership list
|
||||
behind it — there is no such call anywhere in the server, and archiving a team while its vault still
|
||||
exists is refused. That is not the same fact as "a vault cannot be deleted" — the DELETE icon below does
|
||||
delete the vault itself — and both sentences are kept, each where it is true.
|
||||
-->
|
||||
|
||||
<Panel>
|
||||
|
||||
<ScrollViewer>
|
||||
<StackPanel MaxWidth="1100" Margin="40" HorizontalAlignment="Stretch">
|
||||
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal" Spacing="14" VerticalAlignment="Center">
|
||||
<TextBlock Classes="settingstitle" Text="Vaults" />
|
||||
<Border Background="{StaticResource Chip}" CornerRadius="9" MinWidth="34" Height="30">
|
||||
<TextBlock Text="{Binding Vaults.Vaults.Count}" FontSize="13.5" FontWeight="SemiBold"
|
||||
Foreground="{StaticResource TextFaint}" Margin="8,0"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</Border>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="12" VerticalAlignment="Center">
|
||||
<Button Classes="ghost" Height="40" Content="SYNC NOW" Command="{Binding Vault.SyncCommand}" />
|
||||
<Button Classes="accent" Height="40" Content="+ NEW VAULT"
|
||||
Command="{Binding Vaults.NewVaultCommand}"
|
||||
IsEnabled="{Binding !Vaults.IsBusy}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- The name-a-vault and rename-a-vault forms, in place — this window has no modal idiom for a bare
|
||||
text field, and the members panel below is a different kind of overlay: over one vault's people,
|
||||
not over a form. -->
|
||||
<Border Classes="settingscard" Margin="0,20,0,0" Padding="20"
|
||||
IsVisible="{Binding Vaults.IsCreatingVault}">
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock Classes="settingsrowtitle" Text="New vault" />
|
||||
<TextBox PlaceholderText="Name" Text="{Binding Vaults.NewVaultName}" />
|
||||
<TextBlock Classes="settingsrowcaption"
|
||||
Text="Its key is made on this machine and nobody else has it. Add people to it once it exists, then press SHARE KEY." />
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Button Classes="accent" Height="36" Content="CREATE"
|
||||
Command="{Binding Vaults.CreateVaultCommand}" IsEnabled="{Binding !Vaults.IsBusy}" />
|
||||
<Button Classes="ghost" Height="36" Content="CANCEL"
|
||||
Command="{Binding Vaults.CancelNewVaultCommand}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border Classes="settingscard" Margin="0,20,0,0" Padding="20"
|
||||
IsVisible="{Binding Vaults.IsRenamingVault}">
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock Classes="settingsrowtitle" Text="Rename vault" />
|
||||
<TextBox PlaceholderText="Name" Text="{Binding Vaults.EditVaultName}" />
|
||||
<TextBlock Classes="settingsrowcaption"
|
||||
Text="Everybody who shares this vault sees the new name. Nothing is re-encrypted and no key changes — the name has always been stored in plain text, because a person has to be able to pick a vault before anything is decrypted." />
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Button Classes="accent" Height="36" Content="SAVE"
|
||||
Command="{Binding Vaults.SaveVaultNameCommand}" IsEnabled="{Binding !Vaults.IsBusy}" />
|
||||
<Button Classes="ghost" Height="36" Content="CANCEL"
|
||||
Command="{Binding Vaults.CancelRenameVaultCommand}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- A vault's own destructive confirmation. Only while the members panel is shut — see the remark
|
||||
above for why the two never compete for this one PendingAction. Both conditions on the same
|
||||
Border, not one nested inside the other: a Border visible with its content hidden would still
|
||||
draw an empty danger-tinted card while the members panel's own copy of this confirmation is
|
||||
showing. -->
|
||||
<Border Classes="settingscard" Margin="0,20,0,0" Padding="20"
|
||||
Background="{StaticResource DangerWash}" BorderBrush="{StaticResource DangerSoft}">
|
||||
<Border.IsVisible>
|
||||
<MultiBinding Converter="{x:Static BoolConverters.And}">
|
||||
<Binding Path="Vaults.IsConfirming" />
|
||||
<Binding Path="!Vaults.IsMembersPanelOpen" />
|
||||
</MultiBinding>
|
||||
</Border.IsVisible>
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock Text="{Binding Vaults.PendingAction.Question}" FontSize="14.5" FontWeight="Bold"
|
||||
Foreground="{StaticResource Text}" TextWrapping="Wrap" />
|
||||
<TextBlock Text="{Binding Vaults.PendingAction.Consequence}" FontSize="12.5"
|
||||
Foreground="{StaticResource WarnText}" TextWrapping="Wrap" />
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Button Classes="danger" Height="36" Content="CONFIRM"
|
||||
Command="{Binding Vaults.ConfirmActionCommand}" IsEnabled="{Binding !Vaults.IsBusy}" />
|
||||
<Button Classes="ghost" Height="36" Content="CANCEL"
|
||||
Command="{Binding Vaults.CancelActionCommand}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- Sync status: only what is real — no device count, no "ran N minutes ago" (this session tracks no
|
||||
such timestamp). The dot and the word are MainWindowViewModel.IsFullySynced/SyncLabel, the exact
|
||||
fact the titlebar's own dot already tells the truth with. -->
|
||||
<Border Classes="settingscard" Margin="0,20,0,0" Padding="20,18">
|
||||
<StackPanel Orientation="Horizontal" Spacing="14">
|
||||
<Ellipse Classes="dot" Classes.live="{Binding IsFullySynced}" Width="9" Height="9"
|
||||
Margin="0,7,0,0" VerticalAlignment="Top" />
|
||||
<StackPanel Spacing="6">
|
||||
<TextBlock Classes="mono" Text="{Binding SyncLabel}" FontSize="13" FontWeight="Bold"
|
||||
LetterSpacing="0.5" Foreground="{StaticResource Text}" />
|
||||
<TextBlock Classes="settingsrowcaption" Text="End-to-end encrypted." />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<TextBlock Classes="settingssection" Text="YOUR VAULTS" Margin="0,32,0,12" />
|
||||
|
||||
<StackPanel Spacing="10" Margin="0,0,0,40" IsVisible="{Binding Vaults.HasVaults}">
|
||||
<ItemsControl ItemsSource="{Binding Vaults.Vaults}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Spacing="10" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="vm:VaultRowViewModel">
|
||||
<Border Classes="settingscard" Padding="22,18">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto">
|
||||
|
||||
<Border Grid.Column="0" Width="52" Height="52" CornerRadius="12"
|
||||
Background="{StaticResource AvatarGradient}">
|
||||
<TextBlock Text="{Binding Initial}" FontWeight="Bold" FontSize="20"
|
||||
Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</Border>
|
||||
|
||||
<StackPanel Grid.Column="1" Spacing="7" Margin="16,0" VerticalAlignment="Center">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<TextBlock Text="{Binding Name}" FontSize="17" FontWeight="Bold"
|
||||
LetterSpacing="-0.25" Foreground="{StaticResource Text}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
<Border Classes="chip">
|
||||
<TextBlock Text="{Binding RoleLabel}" />
|
||||
</Border>
|
||||
</StackPanel>
|
||||
<TextBlock Classes="mono" FontSize="11.5" Foreground="{StaticResource TextFaint}"
|
||||
Text="{Binding Detail}" TextWrapping="Wrap" />
|
||||
<TextBlock Classes="mono" FontSize="10.5" Foreground="{StaticResource WarnText}"
|
||||
Text="{Binding State}" IsVisible="{Binding HasState}" TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="8" VerticalAlignment="Center">
|
||||
<Button Classes="paneicon" Width="34" Height="34" FontSize="15"
|
||||
Command="{Binding #Root.((vm:MainWindowViewModel)DataContext).Vaults.OpenMembersPanelCommand}"
|
||||
CommandParameter="{Binding}" IsVisible="{Binding IsShared}"
|
||||
ToolTip.Tip="See and manage who is in this vault.">
|
||||
<TextBlock FontFamily="{StaticResource IconFont}" Text="" />
|
||||
</Button>
|
||||
<Button Classes="paneicon" Width="34" Height="34" FontSize="15"
|
||||
Command="{Binding #Root.((vm:MainWindowViewModel)DataContext).Vaults.RenameVaultRowCommand}"
|
||||
CommandParameter="{Binding}" IsVisible="{Binding CanAdminister}"
|
||||
ToolTip.Tip="Changes what this vault is called. The name is plaintext on the server, as it always was; nothing inside is re-encrypted.">
|
||||
<TextBlock FontFamily="{StaticResource IconFont}" Text="" />
|
||||
</Button>
|
||||
<Button Classes="paneicon danger" Width="34" Height="34" FontSize="15"
|
||||
Command="{Binding #Root.((vm:MainWindowViewModel)DataContext).Vaults.DeleteVaultRowCommand}"
|
||||
CommandParameter="{Binding}"
|
||||
IsVisible="{Binding !IsPersonal}"
|
||||
ToolTip.Tip="Deletes this vault and withdraws everybody's key to it. It cannot reach a machine that has already synced it.">
|
||||
<TextBlock FontFamily="{StaticResource IconFont}" Text="" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Classes="settingsrowcaption" Margin="0,20,0,40" TextWrapping="Wrap"
|
||||
IsVisible="{Binding !Vaults.HasVaults}"
|
||||
Text="No vaults yet. Unlock your keychain to see the personal one, or make a vault to share hosts and credentials with colleagues." />
|
||||
|
||||
<TextBlock Margin="0,0,0,40" FontSize="11.5" TextWrapping="Wrap"
|
||||
Foreground="{StaticResource TextFaint}"
|
||||
IsVisible="{Binding Vaults.Status, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"
|
||||
Text="{Binding Vaults.Status}" />
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
<!-- ============ THE MEMBERS PANEL ============ -->
|
||||
<Border x:Name="MembersBackdrop" Background="#9905050A"
|
||||
IsVisible="{Binding Vaults.IsMembersPanelOpen}" PointerPressed="OnBackdropPressed">
|
||||
<Border MaxWidth="860" MaxHeight="620" Margin="30"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
Background="{StaticResource Chrome}" BorderBrush="{StaticResource BorderMid}"
|
||||
BorderThickness="1" CornerRadius="14">
|
||||
<Grid RowDefinitions="Auto,*" Margin="26,22">
|
||||
|
||||
<Grid Grid.Row="0" ColumnDefinitions="*,Auto" Margin="0,0,0,14">
|
||||
<TextBlock Grid.Column="0" Text="{Binding Vaults.SelectedVault.Name, StringFormat='{}{0} · Members'}"
|
||||
FontSize="18" FontWeight="Bold" LetterSpacing="-0.25"
|
||||
Foreground="{StaticResource Text}" TextTrimming="CharacterEllipsis" />
|
||||
<Button Grid.Column="1" Classes="paneicon" Width="26" Height="26"
|
||||
Command="{Binding Vaults.CloseMembersPanelCommand}" ToolTip.Tip="Close">
|
||||
<TextBlock FontFamily="{StaticResource IconFont}" Text="" FontSize="15" />
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<ScrollViewer Grid.Row="1">
|
||||
<StackPanel Spacing="16">
|
||||
|
||||
<TextBlock Classes="settingsrowcaption" TextWrapping="Wrap"
|
||||
IsVisible="{Binding Vaults.HasSharedMembershipWarning}"
|
||||
Text="{Binding Vaults.SharedMembershipWarning}" />
|
||||
|
||||
<StackPanel Spacing="8" IsVisible="{Binding !Vaults.SelectedIsPersonal}">
|
||||
|
||||
<TextBlock Classes="settingssection" Text="MEMBERS" Margin="0" />
|
||||
|
||||
<ListBox ItemsSource="{Binding Vaults.Members}" SelectedItem="{Binding Vaults.SelectedMember}"
|
||||
Background="Transparent" BorderThickness="0" MaxHeight="220">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="vm:VaultMemberRowViewModel">
|
||||
<Grid ColumnDefinitions="Auto,*,150,Auto" Margin="0,4">
|
||||
<Border Grid.Column="0" Width="30" Height="30" CornerRadius="15"
|
||||
Background="{StaticResource AvatarGradient}">
|
||||
<TextBlock Text="{Binding Initials}" FontWeight="Bold" FontSize="10"
|
||||
Foreground="White" HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center" />
|
||||
</Border>
|
||||
<StackPanel Grid.Column="1" Spacing="2" Margin="10,0" VerticalAlignment="Center">
|
||||
<TextBlock Text="{Binding Name}" FontSize="13" FontWeight="Medium"
|
||||
Foreground="{StaticResource Text}" TextTrimming="CharacterEllipsis" />
|
||||
<TextBlock Classes="settingsrowcaption" FontSize="11" Text="{Binding Email}" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2" Spacing="2" VerticalAlignment="Center">
|
||||
<TextBlock Classes="settingsrowcaption" FontSize="10.5" Text="{Binding KeyState}"
|
||||
TextWrapping="Wrap" />
|
||||
<TextBlock Classes="settingsrowcaption" FontSize="10" Text="{Binding LastActive}" />
|
||||
</StackPanel>
|
||||
<TextBlock Grid.Column="3" Classes="mono" Text="{Binding Role}" FontSize="10.5"
|
||||
Foreground="{StaticResource TextFaint}" VerticalAlignment="Center"
|
||||
Margin="10,0,0,0" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
|
||||
<!-- The hand-over confirmation, only while this panel is the one that armed it. -->
|
||||
<Border Padding="14" CornerRadius="10" Background="{StaticResource DangerWash}"
|
||||
BorderBrush="{StaticResource DangerSoft}" BorderThickness="1"
|
||||
IsVisible="{Binding Vaults.IsConfirming}">
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock Text="{Binding Vaults.PendingAction.Question}" FontSize="13.5" FontWeight="Bold"
|
||||
Foreground="{StaticResource Text}" TextWrapping="Wrap" />
|
||||
<TextBlock Text="{Binding Vaults.PendingAction.Consequence}" FontSize="11.5"
|
||||
Foreground="{StaticResource WarnText}" TextWrapping="Wrap" />
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Button Classes="danger" Content="CONFIRM" Command="{Binding Vaults.ConfirmActionCommand}"
|
||||
IsEnabled="{Binding !Vaults.IsBusy}" />
|
||||
<Button Classes="ghost" Content="CANCEL" Command="{Binding Vaults.CancelActionCommand}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<StackPanel Spacing="8" IsVisible="{Binding Vaults.CanAdministerSelected}">
|
||||
<TextBlock Classes="label" Text="SET THE SELECTED MEMBER'S ROLE" />
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<Button Classes="flat choice" Content="VIEWER" Command="{Binding Vaults.ChangeRoleCommand}"
|
||||
CommandParameter="{x:Static contracts:TeamMemberRole.Viewer}"
|
||||
IsEnabled="{Binding !Vaults.IsBusy}" />
|
||||
<Button Classes="flat choice" Content="MEMBER" Command="{Binding Vaults.ChangeRoleCommand}"
|
||||
CommandParameter="{x:Static contracts:TeamMemberRole.Member}"
|
||||
IsEnabled="{Binding !Vaults.IsBusy}" />
|
||||
<Button Classes="flat choice" Content="ADMIN" Command="{Binding Vaults.ChangeRoleCommand}"
|
||||
CommandParameter="{x:Static contracts:TeamMemberRole.Admin}"
|
||||
IsEnabled="{Binding !Vaults.IsBusy}" />
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Button Classes="ghost" Content="HAND OVER" Command="{Binding Vaults.HandOverCommand}"
|
||||
IsEnabled="{Binding !Vaults.IsBusy}" IsVisible="{Binding Vaults.OwnsSelected}"
|
||||
ToolTip.Tip="Hands this vault to the selected member. They become its owner and you become an admin; only the new owner can hand it on again." />
|
||||
<Button Classes="danger" Content="REMOVE" Command="{Binding Vaults.RemoveMemberCommand}"
|
||||
IsEnabled="{Binding !Vaults.IsBusy}"
|
||||
ToolTip.Tip="Removes the selected member and withdraws every key they hold to this vault. It blocks future reads only." />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="8" IsVisible="{Binding Vaults.CanAdministerSelected}">
|
||||
<TextBlock Classes="settingssection" Text="INVITE" Margin="0" />
|
||||
<Grid ColumnDefinitions="*,140,44">
|
||||
<TextBox Grid.Column="0" PlaceholderText="colleague@example.com"
|
||||
Text="{Binding Vaults.NewMemberEmail}" Margin="0,0,8,0" />
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" Spacing="4">
|
||||
<Button Classes="flat choice" Content="VIEWER"
|
||||
Classes.active="{Binding Vaults.AddsAsViewer}"
|
||||
Command="{Binding Vaults.ChooseNewMemberRoleCommand}"
|
||||
CommandParameter="{x:Static contracts:TeamMemberRole.Viewer}" />
|
||||
<Button Classes="flat choice" Content="MEMBER"
|
||||
Classes.active="{Binding Vaults.AddsAsMember}"
|
||||
Command="{Binding Vaults.ChooseNewMemberRoleCommand}"
|
||||
CommandParameter="{x:Static contracts:TeamMemberRole.Member}" />
|
||||
</StackPanel>
|
||||
<Button Grid.Column="2" Classes="accent" Width="44" Content="+"
|
||||
Command="{Binding Vaults.AddMemberCommand}" IsEnabled="{Binding !Vaults.IsBusy}"
|
||||
Margin="8,0,0,0"
|
||||
ToolTip.Tip="Adds the account that signs in with this address. An address with no account here is refused and says so." />
|
||||
</Grid>
|
||||
<TextBlock Classes="settingsrowcaption" TextWrapping="Wrap"
|
||||
Text="Adding somebody lets the server serve them this vault. It does not let them read it — that needs a vault key, which SHARE KEY below wraps for them." />
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="8" IsVisible="{Binding Vaults.SelectedIsPersonal}">
|
||||
<TextBlock Classes="settingssection" Text="YOURS ALONE" Margin="0" />
|
||||
<TextBlock Classes="settingsrowcaption" TextWrapping="Wrap"
|
||||
Text="Nobody can be added to your personal vault, and the server refuses a key grant on one outright. Make a vault for the things you want to share, and put them in it." />
|
||||
</StackPanel>
|
||||
|
||||
<Border Height="1" Background="{StaticResource BorderSubtle}" />
|
||||
|
||||
<StackPanel Spacing="8">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8" IsVisible="{Binding Vaults.SelectedIsShared}">
|
||||
<Button Classes="accent" Content="SHARE KEY" Command="{Binding Vaults.ShareVaultCommand}"
|
||||
IsEnabled="{Binding !Vaults.IsBusy}"
|
||||
ToolTip.Tip="Wraps this vault's key to the selected member. Their published key is checked against the server's append-only key log first." />
|
||||
<Button Classes="danger" Content="WITHDRAW KEY" Command="{Binding Vaults.RevokeVaultCommand}"
|
||||
IsEnabled="{Binding !Vaults.IsBusy}"
|
||||
ToolTip.Tip="Withdraws the selected member's key to this vault. Blocks future reads only." />
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock Classes="settingssection" Text="KEY HOLDERS" Margin="0" />
|
||||
|
||||
<ListBox ItemsSource="{Binding Vaults.Grants}" Background="Transparent" BorderThickness="0"
|
||||
MaxHeight="120">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="vm:VaultGrantRowViewModel">
|
||||
<Grid ColumnDefinitions="10,*" Margin="0,3">
|
||||
<Ellipse Grid.Column="0" Width="6" Height="6" VerticalAlignment="Center"
|
||||
IsVisible="{Binding IsLive}" Fill="{StaticResource Live}" />
|
||||
<StackPanel Grid.Column="1" Spacing="2" Margin="6,0,0,0">
|
||||
<TextBlock Text="{Binding Name}" FontSize="13" FontWeight="Medium"
|
||||
Foreground="{StaticResource Text}" TextTrimming="CharacterEllipsis" />
|
||||
<TextBlock Classes="settingsrowcaption" FontSize="11" Text="{Binding State}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
|
||||
<!--
|
||||
Load-bearing, from the old screen: there is no DELETE here for a reason, and the reason is
|
||||
not "not built yet". The membership list behind a shared vault cannot be archived while the
|
||||
vault exists — that is a different fact from the vault itself, which the card's own DELETE
|
||||
icon does remove.
|
||||
-->
|
||||
<TextBlock Classes="settingsrowcaption" TextWrapping="Wrap"
|
||||
Text="A vault's membership list cannot be deleted on its own — the server refuses to archive it while the vault it carries still exists. Deleting the vault itself is the card's own icon, outside this panel." />
|
||||
</StackPanel>
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
</Grid>
|
||||
</Border>
|
||||
</Border>
|
||||
|
||||
</Panel>
|
||||
|
||||
</UserControl>
|
||||
Reference in New Issue
Block a user