mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 15:15:34 +03:00
Add ability to clone personal vault items (#734)
* Add clone ability to personal vault items * Fixed formatter * Made requested changes and removed some extra whitespace added by Rider formatter * Removed formatting on AppResources file * Fixed casing on UpdateCipherId method * Update calling method
This commit is contained in:
parent
33df456cfd
commit
36fb23d467
7 changed files with 2415 additions and 3734 deletions
|
@ -607,7 +607,7 @@
|
|||
<Button Text="{u:I18n NewCustomField}" StyleClass="box-button-row"
|
||||
Clicked="NewField_Clicked"></Button>
|
||||
</StackLayout>
|
||||
<StackLayout StyleClass="box" IsVisible="{Binding EditMode, Converter={StaticResource inverseBool}}">
|
||||
<StackLayout StyleClass="box" IsVisible="{Binding ShowOwnershipOptions}">
|
||||
<StackLayout StyleClass="box-row-header">
|
||||
<Label Text="{u:I18n Ownership, Header=True}"
|
||||
StyleClass="box-header, box-header-platform" />
|
||||
|
|
|
@ -30,7 +30,9 @@ namespace Bit.App.Pages
|
|||
string name = null,
|
||||
string uri = null,
|
||||
bool fromAutofill = false,
|
||||
AppOptions appOptions = null)
|
||||
AppOptions appOptions = null,
|
||||
bool cloneMode = false,
|
||||
ViewPage viewPage = null)
|
||||
{
|
||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||
|
@ -46,9 +48,11 @@ namespace Bit.App.Pages
|
|||
_vm.Type = type;
|
||||
_vm.DefaultName = name ?? appOptions?.SaveName;
|
||||
_vm.DefaultUri = uri ?? appOptions?.Uri;
|
||||
_vm.CloneMode = cloneMode;
|
||||
_vm.ViewPage = viewPage;
|
||||
_vm.Init();
|
||||
SetActivityIndicator();
|
||||
if(_vm.EditMode && Device.RuntimePlatform == Device.Android)
|
||||
if(_vm.EditMode && !_vm.CloneMode && Device.RuntimePlatform == Device.Android)
|
||||
{
|
||||
ToolbarItems.Add(_attachmentsItem);
|
||||
ToolbarItems.Add(_deleteItem);
|
||||
|
@ -56,7 +60,7 @@ namespace Bit.App.Pages
|
|||
if(Device.RuntimePlatform == Device.iOS)
|
||||
{
|
||||
ToolbarItems.Add(_closeItem);
|
||||
if(_vm.EditMode)
|
||||
if(_vm.EditMode && !_vm.CloneMode)
|
||||
{
|
||||
ToolbarItems.Add(_moreItem);
|
||||
}
|
||||
|
@ -263,7 +267,7 @@ namespace Bit.App.Pages
|
|||
options.Add(_vm.Cipher.OrganizationId == null ? AppResources.Share : AppResources.Collections);
|
||||
}
|
||||
var selection = await DisplayActionSheet(AppResources.Options, AppResources.Cancel,
|
||||
_vm.EditMode ? AppResources.Delete : null, options.ToArray());
|
||||
(_vm.EditMode && !_vm.CloneMode) ? AppResources.Delete : null, options.ToArray());
|
||||
if(selection == AppResources.Delete)
|
||||
{
|
||||
if(await _vm.DeleteAsync())
|
||||
|
@ -334,7 +338,7 @@ namespace Bit.App.Pages
|
|||
|
||||
private void AdjustToolbar()
|
||||
{
|
||||
if(_vm.EditMode && Device.RuntimePlatform == Device.Android)
|
||||
if((_vm.EditMode || _vm.CloneMode) && Device.RuntimePlatform == Device.Android)
|
||||
{
|
||||
if(_vm.Cipher == null)
|
||||
{
|
||||
|
@ -346,7 +350,7 @@ namespace Bit.App.Pages
|
|||
{
|
||||
ToolbarItems.Remove(_collectionsItem);
|
||||
}
|
||||
if(!ToolbarItems.Contains(_shareItem))
|
||||
if(!ToolbarItems.Contains(_shareItem) && !_vm.CloneMode)
|
||||
{
|
||||
ToolbarItems.Insert(2, _shareItem);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.Forms;
|
||||
using View = Xamarin.Forms.View;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
|
@ -260,8 +261,11 @@ namespace Bit.App.Pages
|
|||
nameof(ShowCollections)
|
||||
});
|
||||
}
|
||||
public bool ShowCollections => !EditMode && Cipher.OrganizationId != null;
|
||||
public bool ShowCollections => (!EditMode || CloneMode) && Cipher.OrganizationId != null;
|
||||
public bool EditMode => !string.IsNullOrWhiteSpace(CipherId);
|
||||
public bool ShowOwnershipOptions => !EditMode || CloneMode;
|
||||
public bool CloneMode { get; set; }
|
||||
public ViewPage ViewPage { get; set; }
|
||||
public bool IsLogin => Cipher?.Type == CipherType.Login;
|
||||
public bool IsIdentity => Cipher?.Type == CipherType.Identity;
|
||||
public bool IsCard => Cipher?.Type == CipherType.Card;
|
||||
|
@ -273,7 +277,7 @@ namespace Bit.App.Pages
|
|||
|
||||
public void Init()
|
||||
{
|
||||
PageTitle = EditMode ? AppResources.EditItem : AppResources.AddItem;
|
||||
PageTitle = EditMode && !CloneMode ? AppResources.EditItem : AppResources.AddItem;
|
||||
}
|
||||
|
||||
public async Task<bool> LoadAsync(AppOptions appOptions = null)
|
||||
|
@ -310,6 +314,10 @@ namespace Bit.App.Pages
|
|||
return false;
|
||||
}
|
||||
Cipher = await cipher.DecryptAsync();
|
||||
if(CloneMode)
|
||||
{
|
||||
Cipher.Name += " - " + AppResources.Clone;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -355,7 +363,7 @@ namespace Bit.App.Pages
|
|||
OwnershipSelectedIndex = string.IsNullOrWhiteSpace(Cipher.OrganizationId) ? 0 :
|
||||
OwnershipOptions.FindIndex(k => k.Value == Cipher.OrganizationId);
|
||||
|
||||
if(!EditMode && (CollectionIds?.Any() ?? false))
|
||||
if((!EditMode || CloneMode) && (CollectionIds?.Any() ?? false))
|
||||
{
|
||||
foreach(var col in Collections)
|
||||
{
|
||||
|
@ -406,14 +414,14 @@ namespace Bit.App.Pages
|
|||
if(Cipher.Login != null)
|
||||
{
|
||||
Cipher.Login.Uris = Uris?.ToList();
|
||||
if(!EditMode && Cipher.Type == CipherType.Login && Cipher.Login.Uris != null &&
|
||||
if((!EditMode || CloneMode) && Cipher.Type == CipherType.Login && Cipher.Login.Uris != null &&
|
||||
Cipher.Login.Uris.Count == 1 && string.IsNullOrWhiteSpace(Cipher.Login.Uris[0].Uri))
|
||||
{
|
||||
Cipher.Login.Uris = null;
|
||||
}
|
||||
}
|
||||
|
||||
if(!EditMode && Cipher.OrganizationId != null)
|
||||
if((!EditMode || CloneMode) && Cipher.OrganizationId != null)
|
||||
{
|
||||
if(Collections == null || !Collections.Any(c => c != null && c.Checked))
|
||||
{
|
||||
|
@ -427,6 +435,10 @@ namespace Bit.App.Pages
|
|||
.Select(c => c.Collection.Id)) : null;
|
||||
}
|
||||
|
||||
if(CloneMode)
|
||||
{
|
||||
Cipher.Id = null;
|
||||
}
|
||||
var cipher = await _cipherService.EncryptAsync(Cipher);
|
||||
if(cipher == null)
|
||||
{
|
||||
|
@ -439,8 +451,8 @@ namespace Bit.App.Pages
|
|||
Cipher.Id = cipher.Id;
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
_platformUtilsService.ShowToast("success", null,
|
||||
EditMode ? AppResources.ItemUpdated : AppResources.NewItemCreated);
|
||||
_messagingService.Send(EditMode ? "editedCipher" : "addedCipher", Cipher.Id);
|
||||
EditMode && !CloneMode ? AppResources.ItemUpdated : AppResources.NewItemCreated);
|
||||
_messagingService.Send(EditMode && !CloneMode ? "editedCipher" : "addedCipher", Cipher.Id);
|
||||
|
||||
if(Page is AddEditPage page && page.FromAutofillFramework)
|
||||
{
|
||||
|
@ -449,6 +461,10 @@ namespace Bit.App.Pages
|
|||
}
|
||||
else
|
||||
{
|
||||
if(CloneMode)
|
||||
{
|
||||
ViewPage?.UpdateCipherId(this.Cipher.Id);
|
||||
}
|
||||
await Page.Navigation.PopModalAsync();
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -43,6 +43,8 @@
|
|||
x:Name="_attachmentsItem" x:Key="attachmentsItem" />
|
||||
<ToolbarItem Text="{u:I18n Delete}" Clicked="Delete_Clicked" Order="Secondary" IsDestructive="True"
|
||||
x:Name="_deleteItem" x:Key="deleteItem" />
|
||||
<ToolbarItem Text="{u:I18n Clone}" Clicked="Clone_Clicked" Order="Secondary"
|
||||
x:Name="_cloneItem" x:Key="cloneItem" />
|
||||
|
||||
<ScrollView x:Key="scrollView" x:Name="_scrollView">
|
||||
<StackLayout Spacing="20" x:Name="_mainLayout">
|
||||
|
@ -185,7 +187,7 @@
|
|||
Grid.RowSpan="2"
|
||||
HorizontalOptions="End"
|
||||
HorizontalTextAlignment="End"
|
||||
VerticalOptions="CenterAndExpand"/>
|
||||
VerticalOptions="CenterAndExpand" />
|
||||
<controls:FaButton
|
||||
StyleClass="box-row-button, box-row-button-platform"
|
||||
Text=""
|
||||
|
@ -210,7 +212,7 @@
|
|||
StyleClass="box-value" />
|
||||
</StackLayout>
|
||||
<BoxView StyleClass="box-row-separator"
|
||||
IsVisible="{Binding Cipher.Card.CardholderName, Converter={StaticResource stringHasValue}}"/>
|
||||
IsVisible="{Binding Cipher.Card.CardholderName, Converter={StaticResource stringHasValue}}" />
|
||||
<Grid StyleClass="box-row"
|
||||
IsVisible="{Binding Cipher.Card.Number, Converter={StaticResource stringHasValue}}">
|
||||
<Grid.RowDefinitions>
|
||||
|
@ -361,7 +363,7 @@
|
|||
StyleClass="box-value" />
|
||||
</StackLayout>
|
||||
<BoxView StyleClass="box-row-separator"
|
||||
IsVisible="{Binding Cipher.Identity.SSN, Converter={StaticResource stringHasValue}}"/>
|
||||
IsVisible="{Binding Cipher.Identity.SSN, Converter={StaticResource stringHasValue}}" />
|
||||
<StackLayout StyleClass="box-row"
|
||||
IsVisible="{Binding Cipher.Identity.PassportNumber, Converter={StaticResource stringHasValue}}">
|
||||
<Label
|
||||
|
|
|
@ -41,6 +41,11 @@ namespace Bit.App.Pages
|
|||
|
||||
public ViewPageViewModel ViewModel => _vm;
|
||||
|
||||
public void UpdateCipherId(string cipherId)
|
||||
{
|
||||
_vm.CipherId = cipherId;
|
||||
}
|
||||
|
||||
protected override async void OnAppearing()
|
||||
{
|
||||
base.OnAppearing();
|
||||
|
@ -149,14 +154,33 @@ namespace Bit.App.Pages
|
|||
}
|
||||
}
|
||||
|
||||
private async void Clone_Clicked(object sender, System.EventArgs e)
|
||||
{
|
||||
if(DoOnce())
|
||||
{
|
||||
var page = new AddEditPage(_vm.CipherId, cloneMode: true, viewPage: this);
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
}
|
||||
|
||||
private async void More_Clicked(object sender, System.EventArgs e)
|
||||
{
|
||||
if(!DoOnce())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var options = new List<string> { AppResources.Attachments };
|
||||
options.Add(_vm.Cipher.OrganizationId == null ? AppResources.Share : AppResources.Collections);
|
||||
|
||||
var options = new List<string> {AppResources.Attachments};
|
||||
if(_vm.Cipher.OrganizationId == null)
|
||||
{
|
||||
options.Add(AppResources.Clone);
|
||||
options.Add(AppResources.Share);
|
||||
}
|
||||
else
|
||||
{
|
||||
options.Add(AppResources.Collections);
|
||||
}
|
||||
|
||||
var selection = await DisplayActionSheet(AppResources.Options, AppResources.Cancel,
|
||||
AppResources.Delete, options.ToArray());
|
||||
if(selection == AppResources.Delete)
|
||||
|
@ -181,6 +205,11 @@ namespace Bit.App.Pages
|
|||
var page = new SharePage(_vm.CipherId);
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
else if(selection == AppResources.Clone)
|
||||
{
|
||||
var page = new AddEditPage(_vm.CipherId, cloneMode: true, viewPage: this);
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
}
|
||||
|
||||
private async void Close_Clicked(object sender, System.EventArgs e)
|
||||
|
@ -203,13 +232,21 @@ namespace Bit.App.Pages
|
|||
{
|
||||
ToolbarItems.Remove(_collectionsItem);
|
||||
}
|
||||
if(!ToolbarItems.Contains(_cloneItem))
|
||||
{
|
||||
ToolbarItems.Insert(1, _cloneItem);
|
||||
}
|
||||
if(!ToolbarItems.Contains(_shareItem))
|
||||
{
|
||||
ToolbarItems.Insert(1, _shareItem);
|
||||
ToolbarItems.Insert(2, _shareItem);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(ToolbarItems.Contains(_cloneItem))
|
||||
{
|
||||
ToolbarItems.Remove(_cloneItem);
|
||||
}
|
||||
if(ToolbarItems.Contains(_shareItem))
|
||||
{
|
||||
ToolbarItems.Remove(_shareItem);
|
||||
|
|
6038
src/App/Resources/AppResources.Designer.cs
generated
6038
src/App/Resources/AppResources.Designer.cs
generated
File diff suppressed because it is too large
Load diff
|
@ -1608,4 +1608,8 @@
|
|||
<data name="ExportVaultSuccess" xml:space="preserve">
|
||||
<value>Vault exported successfully</value>
|
||||
</data>
|
||||
<data name="Clone" xml:space="preserve">
|
||||
<value>Clone</value>
|
||||
<comment>Clone an entity (verb).</comment>
|
||||
</data>
|
||||
</root>
|
Loading…
Reference in a new issue