mirror of
https://github.com/bitwarden/android.git
synced 2024-12-20 08:12:26 +03:00
Wired up view page functionality. Expanded LabeledValueCell. Created custom group template for vault list page.
This commit is contained in:
parent
d288116b39
commit
4e906f9370
12 changed files with 155 additions and 49 deletions
|
@ -11,6 +11,9 @@ namespace Bit.App.Controls
|
||||||
public static readonly BindableProperty EnableSelectionProperty =
|
public static readonly BindableProperty EnableSelectionProperty =
|
||||||
BindableProperty.Create(nameof(EnableSelection), typeof(bool), typeof(ExtendedTableView), true);
|
BindableProperty.Create(nameof(EnableSelection), typeof(bool), typeof(ExtendedTableView), true);
|
||||||
|
|
||||||
|
public static readonly BindableProperty SeparatorColorProperty =
|
||||||
|
BindableProperty.Create(nameof(SeparatorColor), typeof(Color), typeof(ExtendedTableView), Color.FromHex("d2d6de"));
|
||||||
|
|
||||||
public bool EnableScrolling
|
public bool EnableScrolling
|
||||||
{
|
{
|
||||||
get { return (bool)GetValue(EnableScrollingProperty); }
|
get { return (bool)GetValue(EnableScrollingProperty); }
|
||||||
|
@ -23,6 +26,12 @@ namespace Bit.App.Controls
|
||||||
set { SetValue(EnableSelectionProperty, value); }
|
set { SetValue(EnableSelectionProperty, value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Color SeparatorColor
|
||||||
|
{
|
||||||
|
get { return (Color)GetValue(SeparatorColorProperty); }
|
||||||
|
set { SetValue(SeparatorColorProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
public int EstimatedRowHeight { get; set; }
|
public int EstimatedRowHeight { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,20 +9,13 @@ namespace Bit.App.Controls
|
||||||
{
|
{
|
||||||
public class LabeledValueCell : ViewCell
|
public class LabeledValueCell : ViewCell
|
||||||
{
|
{
|
||||||
private readonly IUserDialogs _userDialogs;
|
|
||||||
private readonly IClipboardService _clipboardService;
|
|
||||||
|
|
||||||
public LabeledValueCell(
|
public LabeledValueCell(
|
||||||
string labelText,
|
string labelText = null,
|
||||||
string valueText = null,
|
string valueText = null,
|
||||||
bool copyValue = false,
|
string button1Text = null,
|
||||||
bool password = false,
|
string button2Text = null)
|
||||||
bool launch = false)
|
|
||||||
{
|
{
|
||||||
_userDialogs = Resolver.Resolve<IUserDialogs>();
|
StackLayout = new StackLayout
|
||||||
_clipboardService = Resolver.Resolve<IClipboardService>();
|
|
||||||
|
|
||||||
var stackLayout = new StackLayout
|
|
||||||
{
|
{
|
||||||
Padding = new Thickness(15, 15, 15, 0),
|
Padding = new Thickness(15, 15, 15, 0),
|
||||||
BackgroundColor = Color.White
|
BackgroundColor = Color.White
|
||||||
|
@ -37,7 +30,7 @@ namespace Bit.App.Controls
|
||||||
TextColor = Color.FromHex("777777")
|
TextColor = Color.FromHex("777777")
|
||||||
};
|
};
|
||||||
|
|
||||||
stackLayout.Children.Add(Label);
|
StackLayout.Children.Add(Label);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value = new Label
|
Value = new Label
|
||||||
|
@ -52,46 +45,42 @@ namespace Bit.App.Controls
|
||||||
{
|
{
|
||||||
Orientation = StackOrientation.Horizontal
|
Orientation = StackOrientation.Horizontal
|
||||||
};
|
};
|
||||||
|
|
||||||
valueStackLayout.Children.Add(Value);
|
valueStackLayout.Children.Add(Value);
|
||||||
|
|
||||||
if(copyValue)
|
if(button1Text != null)
|
||||||
{
|
{
|
||||||
var copyButton = new Button
|
Button1 = new Button
|
||||||
{
|
{
|
||||||
Text = AppResources.Copy,
|
Text = button1Text,
|
||||||
HorizontalOptions = LayoutOptions.End,
|
HorizontalOptions = LayoutOptions.End,
|
||||||
VerticalOptions = LayoutOptions.Center,
|
VerticalOptions = LayoutOptions.Center
|
||||||
Command = new Command(() => Copy())
|
|
||||||
};
|
};
|
||||||
|
|
||||||
valueStackLayout.Children.Add(copyButton);
|
valueStackLayout.Children.Add(Button1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(launch)
|
if(button2Text != null)
|
||||||
{
|
{
|
||||||
var launchButton = new Button
|
Button2 = new Button
|
||||||
{
|
{
|
||||||
Text = AppResources.Launch,
|
Text = button2Text,
|
||||||
HorizontalOptions = LayoutOptions.End,
|
HorizontalOptions = LayoutOptions.End,
|
||||||
VerticalOptions = LayoutOptions.Center,
|
VerticalOptions = LayoutOptions.Center
|
||||||
Command = new Command(() => Device.OpenUri(new Uri(Value.Text)))
|
|
||||||
};
|
};
|
||||||
|
|
||||||
valueStackLayout.Children.Add(launchButton);
|
valueStackLayout.Children.Add(Button2);
|
||||||
}
|
}
|
||||||
|
|
||||||
stackLayout.Children.Add(valueStackLayout);
|
StackLayout.Children.Add(valueStackLayout);
|
||||||
|
|
||||||
View = stackLayout;
|
View = StackLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public StackLayout StackLayout { get; private set; }
|
||||||
public Label Label { get; private set; }
|
public Label Label { get; private set; }
|
||||||
public Label Value { get; private set; }
|
public Label Value { get; private set; }
|
||||||
|
public Button Button1 { get; private set; }
|
||||||
private void Copy()
|
public Button Button2 { get; private set; }
|
||||||
{
|
|
||||||
_clipboardService.CopyToClipboard(Value.Text);
|
|
||||||
_userDialogs.SuccessToast(string.Format(AppResources.ValueHasBeenCopied, Label.Text));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ namespace Bit.App.Models.Page
|
||||||
PropertyChanged(this, new PropertyChangedEventArgs(nameof(PageTitle)));
|
PropertyChanged(this, new PropertyChangedEventArgs(nameof(PageTitle)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public string PageTitle => Name ?? AppResources.SiteNoName;
|
||||||
public string Username
|
public string Username
|
||||||
{
|
{
|
||||||
get { return _username; }
|
get { return _username; }
|
||||||
|
@ -34,8 +35,10 @@ namespace Bit.App.Models.Page
|
||||||
{
|
{
|
||||||
_username = value;
|
_username = value;
|
||||||
PropertyChanged(this, new PropertyChangedEventArgs(nameof(Username)));
|
PropertyChanged(this, new PropertyChangedEventArgs(nameof(Username)));
|
||||||
|
PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowUsername)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public bool ShowUsername => !string.IsNullOrWhiteSpace(Username);
|
||||||
public string Password
|
public string Password
|
||||||
{
|
{
|
||||||
get { return _password; }
|
get { return _password; }
|
||||||
|
@ -53,8 +56,31 @@ namespace Bit.App.Models.Page
|
||||||
{
|
{
|
||||||
_uri = value;
|
_uri = value;
|
||||||
PropertyChanged(this, new PropertyChangedEventArgs(nameof(Uri)));
|
PropertyChanged(this, new PropertyChangedEventArgs(nameof(Uri)));
|
||||||
|
PropertyChanged(this, new PropertyChangedEventArgs(nameof(UriHost)));
|
||||||
|
PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowUri)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public string UriHost
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if(ShowUri)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return new Uri(Uri).Host;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return Uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ShowUri => !string.IsNullOrWhiteSpace(Uri);
|
||||||
public string Notes
|
public string Notes
|
||||||
{
|
{
|
||||||
get { return _notes; }
|
get { return _notes; }
|
||||||
|
@ -65,7 +91,7 @@ namespace Bit.App.Models.Page
|
||||||
PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowNotes)));
|
PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowNotes)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public string PageTitle => Name ?? AppResources.SiteNoName;
|
public bool ShowNotes => !string.IsNullOrWhiteSpace(Notes);
|
||||||
public bool ShowPassword
|
public bool ShowPassword
|
||||||
{
|
{
|
||||||
get { return _showPassword; }
|
get { return _showPassword; }
|
||||||
|
@ -79,7 +105,6 @@ namespace Bit.App.Models.Page
|
||||||
}
|
}
|
||||||
public string MaskedPassword => ShowPassword ? Password : Password == null ? null : new string('●', Password.Length);
|
public string MaskedPassword => ShowPassword ? Password : Password == null ? null : new string('●', Password.Length);
|
||||||
public string ShowHideText => ShowPassword ? AppResources.Hide : AppResources.Show;
|
public string ShowHideText => ShowPassword ? AppResources.Hide : AppResources.Show;
|
||||||
public bool ShowNotes => !string.IsNullOrWhiteSpace(Notes);
|
|
||||||
|
|
||||||
public void Update(Site site)
|
public void Update(Site site)
|
||||||
{
|
{
|
||||||
|
|
|
@ -89,9 +89,9 @@ namespace Bit.App.Pages
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(uriCell.Entry.Text))
|
if(string.IsNullOrWhiteSpace(passwordCell.Entry.Text))
|
||||||
{
|
{
|
||||||
await DisplayAlert(AppResources.AnErrorHasOccurred, string.Format(AppResources.ValidationFieldRequired, AppResources.URI), AppResources.Ok);
|
await DisplayAlert(AppResources.AnErrorHasOccurred, string.Format(AppResources.ValidationFieldRequired, AppResources.Password), AppResources.Ok);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +127,10 @@ namespace Bit.App.Pages
|
||||||
Title = AppResources.AddSite;
|
Title = AppResources.AddSite;
|
||||||
Content = scrollView;
|
Content = scrollView;
|
||||||
ToolbarItems.Add(saveToolBarItem);
|
ToolbarItems.Add(saveToolBarItem);
|
||||||
|
if(Device.OS == TargetPlatform.iOS)
|
||||||
|
{
|
||||||
ToolbarItems.Add(new DismissModalToolBarItem(this, "Cancel"));
|
ToolbarItems.Add(new DismissModalToolBarItem(this, "Cancel"));
|
||||||
|
}
|
||||||
|
|
||||||
if(!_connectivity.IsConnected)
|
if(!_connectivity.IsConnected)
|
||||||
{
|
{
|
||||||
|
|
|
@ -111,9 +111,9 @@ namespace Bit.App.Pages
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(string.IsNullOrWhiteSpace(uriCell.Entry.Text))
|
if(string.IsNullOrWhiteSpace(passwordCell.Entry.Text))
|
||||||
{
|
{
|
||||||
await DisplayAlert(AppResources.AnErrorHasOccurred, string.Format(AppResources.ValidationFieldRequired, AppResources.URI), AppResources.Ok);
|
await DisplayAlert(AppResources.AnErrorHasOccurred, string.Format(AppResources.ValidationFieldRequired, AppResources.Password), AppResources.Ok);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,7 +150,10 @@ namespace Bit.App.Pages
|
||||||
Title = "Edit Site";
|
Title = "Edit Site";
|
||||||
Content = scrollView;
|
Content = scrollView;
|
||||||
ToolbarItems.Add(saveToolBarItem);
|
ToolbarItems.Add(saveToolBarItem);
|
||||||
|
if(Device.OS == TargetPlatform.iOS)
|
||||||
|
{
|
||||||
ToolbarItems.Add(new DismissModalToolBarItem(this, "Cancel"));
|
ToolbarItems.Add(new DismissModalToolBarItem(this, "Cancel"));
|
||||||
|
}
|
||||||
|
|
||||||
if(!_connectivity.IsConnected)
|
if(!_connectivity.IsConnected)
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,8 +35,15 @@ namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
ToolbarItems.Add(new AddSiteToolBarItem(this));
|
ToolbarItems.Add(new AddSiteToolBarItem(this));
|
||||||
|
|
||||||
var listView = new ListView { IsGroupingEnabled = true, ItemsSource = Folders };
|
var listView = new ListView
|
||||||
|
{
|
||||||
|
IsGroupingEnabled = true,
|
||||||
|
ItemsSource = Folders,
|
||||||
|
HasUnevenRows = true,
|
||||||
|
SeparatorColor = Color.FromHex("E0E0E0")
|
||||||
|
};
|
||||||
listView.GroupDisplayBinding = new Binding("Name");
|
listView.GroupDisplayBinding = new Binding("Name");
|
||||||
|
listView.GroupHeaderTemplate = new DataTemplate(() => new VaultListHeaderViewCell(this));
|
||||||
listView.ItemSelected += SiteSelected;
|
listView.ItemSelected += SiteSelected;
|
||||||
listView.ItemTemplate = new DataTemplate(() => new VaultListViewCell(this));
|
listView.ItemTemplate = new DataTemplate(() => new VaultListViewCell(this));
|
||||||
|
|
||||||
|
@ -171,6 +178,53 @@ namespace Bit.App.Pages
|
||||||
this.SetBinding<VaultListPageModel.Site>(DetailProperty, s => s.Username);
|
this.SetBinding<VaultListPageModel.Site>(DetailProperty, s => s.Username);
|
||||||
ContextActions.Add(moreAction);
|
ContextActions.Add(moreAction);
|
||||||
ContextActions.Add(deleteAction);
|
ContextActions.Add(deleteAction);
|
||||||
|
|
||||||
|
TextColor = Color.FromHex("333333");
|
||||||
|
DetailColor = Color.FromHex("777777");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class VaultListHeaderViewCell : ViewCell
|
||||||
|
{
|
||||||
|
public VaultListHeaderViewCell(VaultListPage page)
|
||||||
|
{
|
||||||
|
var image = new Image
|
||||||
|
{
|
||||||
|
Source = ImageSource.FromFile("fa-folder-open.png"),
|
||||||
|
Margin = new Thickness(16, 8, 0, 8)
|
||||||
|
};
|
||||||
|
|
||||||
|
var label = new Label
|
||||||
|
{
|
||||||
|
FontSize = 14,
|
||||||
|
TextColor = Color.FromHex("777777"),
|
||||||
|
VerticalTextAlignment = TextAlignment.Center
|
||||||
|
};
|
||||||
|
|
||||||
|
label.SetBinding<VaultListPageModel.Folder>(Label.TextProperty, s => s.Name);
|
||||||
|
|
||||||
|
var stackLayout = new StackLayout
|
||||||
|
{
|
||||||
|
Orientation = StackOrientation.Horizontal,
|
||||||
|
BackgroundColor = Color.FromHex("ecf0f5")
|
||||||
|
};
|
||||||
|
|
||||||
|
stackLayout.Children.Add(image);
|
||||||
|
stackLayout.Children.Add(label);
|
||||||
|
|
||||||
|
var borderStackLayout = new StackLayout
|
||||||
|
{
|
||||||
|
Spacing = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
var borderBoxTop = new BoxView { BackgroundColor = Color.FromHex("d2d6de"), HeightRequest = 0.5 };
|
||||||
|
var borderBoxBottom = new BoxView { BackgroundColor = Color.FromHex("d2d6de"), HeightRequest = 0.5 };
|
||||||
|
borderStackLayout.Children.Add(borderBoxTop);
|
||||||
|
borderStackLayout.Children.Add(stackLayout);
|
||||||
|
borderStackLayout.Children.Add(borderBoxBottom);
|
||||||
|
|
||||||
|
View = borderStackLayout;
|
||||||
|
Height = 30;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,26 +32,34 @@ namespace Bit.App.Pages
|
||||||
private void Init()
|
private void Init()
|
||||||
{
|
{
|
||||||
ToolbarItems.Add(new EditSiteToolBarItem(this, _siteId));
|
ToolbarItems.Add(new EditSiteToolBarItem(this, _siteId));
|
||||||
|
if(Device.OS == TargetPlatform.iOS)
|
||||||
|
{
|
||||||
ToolbarItems.Add(new DismissModalToolBarItem(this));
|
ToolbarItems.Add(new DismissModalToolBarItem(this));
|
||||||
|
}
|
||||||
|
|
||||||
// Username
|
// Username
|
||||||
var nameCell = new LabeledValueCell(AppResources.Name);
|
var nameCell = new LabeledValueCell(AppResources.Name);
|
||||||
nameCell.Value.SetBinding<VaultViewSitePageModel>(Label.TextProperty, s => s.Name);
|
nameCell.Value.SetBinding<VaultViewSitePageModel>(Label.TextProperty, s => s.Name);
|
||||||
|
|
||||||
// Username
|
// Username
|
||||||
var usernameCell = new LabeledValueCell(AppResources.Username, copyValue: true);
|
var usernameCell = new LabeledValueCell(AppResources.Username, button1Text: AppResources.Copy);
|
||||||
usernameCell.Value.SetBinding<VaultViewSitePageModel>(Label.TextProperty, s => s.Username);
|
usernameCell.Value.SetBinding<VaultViewSitePageModel>(Label.TextProperty, s => s.Username);
|
||||||
|
usernameCell.Button1.Command = new Command(() => Copy(Model.Username, AppResources.Username));
|
||||||
|
|
||||||
// Password
|
// Password
|
||||||
var passwordCell = new LabeledValueCell(AppResources.Password, copyValue: true);
|
var passwordCell = new LabeledValueCell(AppResources.Password, button1Text: AppResources.Show, button2Text: AppResources.Copy);
|
||||||
passwordCell.Value.SetBinding<VaultViewSitePageModel>(Label.TextProperty, s => s.Password);
|
passwordCell.Value.SetBinding<VaultViewSitePageModel>(Label.TextProperty, s => s.MaskedPassword);
|
||||||
|
passwordCell.Button1.SetBinding<VaultViewSitePageModel>(Button.TextProperty, s => s.ShowHideText);
|
||||||
|
passwordCell.Button1.Command = new Command(() => Model.ShowPassword = !Model.ShowPassword);
|
||||||
|
passwordCell.Button2.Command = new Command(() => Copy(Model.Password, AppResources.Password));
|
||||||
|
|
||||||
// URI
|
// URI
|
||||||
var uriCell = new LabeledValueCell(AppResources.URI, launch: true);
|
var uriCell = new LabeledValueCell(AppResources.Website, button1Text: AppResources.Launch);
|
||||||
uriCell.Value.SetBinding<VaultViewSitePageModel>(Label.TextProperty, s => s.Uri);
|
uriCell.Value.SetBinding<VaultViewSitePageModel>(Label.TextProperty, s => s.UriHost);
|
||||||
|
uriCell.Button1.Command = new Command(() => Device.OpenUri(new Uri(uriCell.Value.Text)));
|
||||||
|
|
||||||
// Notes
|
// Notes
|
||||||
var notesCell = new LabeledValueCell(AppResources.Notes);
|
var notesCell = new LabeledValueCell();
|
||||||
notesCell.Value.SetBinding<VaultViewSitePageModel>(Label.TextProperty, s => s.Notes);
|
notesCell.Value.SetBinding<VaultViewSitePageModel>(Label.TextProperty, s => s.Notes);
|
||||||
|
|
||||||
Table = new ExtendedTableView
|
Table = new ExtendedTableView
|
||||||
|
|
|
@ -23,6 +23,7 @@ namespace Bit.iOS.Controls
|
||||||
SetSelection(view);
|
SetSelection(view);
|
||||||
UpdateRowHeight(view);
|
UpdateRowHeight(view);
|
||||||
UpdateEstimatedRowHeight(view);
|
UpdateEstimatedRowHeight(view);
|
||||||
|
UpdateSeparatorColor(view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,5 +88,10 @@ namespace Bit.iOS.Controls
|
||||||
Control.EstimatedRowHeight = 0;
|
Control.EstimatedRowHeight = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateSeparatorColor(ExtendedTableView view)
|
||||||
|
{
|
||||||
|
Control.SeparatorColor = view.SeparatorColor.ToUIColor(UIColor.Gray);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
BIN
src/iOS/Resources/fa-folder-open.png
Normal file
BIN
src/iOS/Resources/fa-folder-open.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 314 B |
BIN
src/iOS/Resources/fa-folder-open@2x.png
Normal file
BIN
src/iOS/Resources/fa-folder-open@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 456 B |
BIN
src/iOS/Resources/fa-folder-open@3x.png
Normal file
BIN
src/iOS/Resources/fa-folder-open@3x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 623 B |
|
@ -330,6 +330,15 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ITunesArtwork Include="Resources\iTunesArtwork%402x.png" />
|
<ITunesArtwork Include="Resources\iTunesArtwork%402x.png" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<BundleResource Include="Resources\fa-folder-open.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<BundleResource Include="Resources\fa-folder-open%402x.png" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<BundleResource Include="Resources\fa-folder-open%403x.png" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
|
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
|
||||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|
Loading…
Reference in a new issue