diff --git a/src/App/Pages/Send/SendGroupingsPage/ISendGroupingsPageListItem.cs b/src/App/Pages/Send/SendGroupingsPage/ISendGroupingsPageListItem.cs
new file mode 100644
index 000000000..6b8b84801
--- /dev/null
+++ b/src/App/Pages/Send/SendGroupingsPage/ISendGroupingsPageListItem.cs
@@ -0,0 +1,6 @@
+namespace Bit.App.Pages
+{
+ public interface ISendGroupingsPageListItem
+ {
+ }
+}
diff --git a/src/App/Pages/Send/SendGroupingsPage/SendGroupingsPage.xaml b/src/App/Pages/Send/SendGroupingsPage/SendGroupingsPage.xaml
index 114feede1..ec6b33043 100644
--- a/src/App/Pages/Send/SendGroupingsPage/SendGroupingsPage.xaml
+++ b/src/App/Pages/Send/SendGroupingsPage/SendGroupingsPage.xaml
@@ -73,7 +73,29 @@
+
+
+
+
+
+
+
+
+
+
+
@@ -114,32 +136,9 @@
ItemsSource="{Binding GroupedSends}"
VerticalOptions="FillAndExpand"
ItemTemplate="{StaticResource sendListItemDataTemplateSelector}"
- IsGrouped="True"
SelectionMode="Single"
SelectionChanged="RowSelected"
- StyleClass="list, list-platform">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ StyleClass="list, list-platform" />
diff --git a/src/App/Pages/Send/SendGroupingsPage/SendGroupingsPageHeaderListItem.cs b/src/App/Pages/Send/SendGroupingsPage/SendGroupingsPageHeaderListItem.cs
new file mode 100644
index 000000000..c8c7943be
--- /dev/null
+++ b/src/App/Pages/Send/SendGroupingsPage/SendGroupingsPageHeaderListItem.cs
@@ -0,0 +1,14 @@
+namespace Bit.App.Pages
+{
+ public class SendGroupingsPageHeaderListItem : ISendGroupingsPageListItem
+ {
+ public SendGroupingsPageHeaderListItem(string title, string itemCount)
+ {
+ Title = title;
+ ItemCount = itemCount;
+ }
+
+ public string Title { get; }
+ public string ItemCount { get; }
+ }
+}
diff --git a/src/App/Pages/Send/SendGroupingsPage/SendGroupingsPageListItem.cs b/src/App/Pages/Send/SendGroupingsPage/SendGroupingsPageListItem.cs
index 00d1bccbd..a89ebf18d 100644
--- a/src/App/Pages/Send/SendGroupingsPage/SendGroupingsPageListItem.cs
+++ b/src/App/Pages/Send/SendGroupingsPage/SendGroupingsPageListItem.cs
@@ -5,7 +5,7 @@ using Bit.Core.Models.View;
namespace Bit.App.Pages
{
- public class SendGroupingsPageListItem
+ public class SendGroupingsPageListItem : ISendGroupingsPageListItem
{
private string _icon;
private string _name;
diff --git a/src/App/Pages/Send/SendGroupingsPage/SendGroupingsPageListItemSelector.cs b/src/App/Pages/Send/SendGroupingsPage/SendGroupingsPageListItemSelector.cs
index 885bc0fb5..3eb8a6019 100644
--- a/src/App/Pages/Send/SendGroupingsPage/SendGroupingsPageListItemSelector.cs
+++ b/src/App/Pages/Send/SendGroupingsPage/SendGroupingsPageListItemSelector.cs
@@ -4,11 +4,17 @@ namespace Bit.App.Pages
{
public class SendGroupingsPageListItemSelector : DataTemplateSelector
{
+ public DataTemplate HeaderTemplate { get; set; }
public DataTemplate SendTemplate { get; set; }
public DataTemplate GroupTemplate { get; set; }
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
+ if (item is SendGroupingsPageHeaderListItem)
+ {
+ return HeaderTemplate;
+ }
+
if (item is SendGroupingsPageListItem listItem)
{
return listItem.Send != null ? SendTemplate : GroupTemplate;
diff --git a/src/App/Pages/Send/SendGroupingsPage/SendGroupingsPageViewModel.cs b/src/App/Pages/Send/SendGroupingsPage/SendGroupingsPageViewModel.cs
index 971bb0b67..66f207211 100644
--- a/src/App/Pages/Send/SendGroupingsPage/SendGroupingsPageViewModel.cs
+++ b/src/App/Pages/Send/SendGroupingsPage/SendGroupingsPageViewModel.cs
@@ -10,6 +10,7 @@ using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
+using Xamarin.CommunityToolkit.ObjectModel;
using Xamarin.Essentials;
using Xamarin.Forms;
using DeviceType = Bit.Core.Enums.DeviceType;
@@ -48,7 +49,7 @@ namespace Bit.App.Pages
Loading = true;
PageTitle = AppResources.Send;
- GroupedSends = new ExtendedObservableCollection();
+ GroupedSends = new ObservableRangeCollection();
RefreshCommand = new Command(async () =>
{
Refreshing = true;
@@ -103,7 +104,7 @@ namespace Bit.App.Pages
get => _showList;
set => SetProperty(ref _showList, value);
}
- public ExtendedObservableCollection GroupedSends { get; set; }
+ public ObservableRangeCollection GroupedSends { get; set; }
public Command RefreshCommand { get; set; }
public Command SendOptionsCommand { get; set; }
public bool LoadedOnce { get; set; }
@@ -175,7 +176,33 @@ namespace Bit.App.Pages
MainPage ? AppResources.AllSends : AppResources.Sends, sendsListItems.Count,
uppercaseGroupNames, !MainPage));
}
- GroupedSends.ResetWithRange(groupedSends);
+
+ // TODO: refactor this
+ if (Device.RuntimePlatform == Device.Android)
+ {
+ var items = new List();
+ foreach (var itemGroup in groupedSends)
+ {
+ items.Add(new SendGroupingsPageHeaderListItem(itemGroup.Name, itemGroup.ItemCount));
+ items.AddRange(itemGroup);
+ }
+
+ GroupedSends.ReplaceRange(items);
+ }
+ else
+ {
+ // HACK: This waitings are to avoid crash on iOS
+ GroupedSends.Clear();
+ await Task.Delay(60);
+
+ foreach (var itemGroup in groupedSends)
+ {
+ GroupedSends.Add(new SendGroupingsPageHeaderListItem(itemGroup.Name, itemGroup.ItemCount));
+ await Task.Delay(60);
+
+ GroupedSends.AddRange(itemGroup);
+ }
+ }
}
finally
{
diff --git a/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs b/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs
index 85cd338b4..0af8a2ea6 100644
--- a/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs
+++ b/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs
@@ -513,13 +513,36 @@ namespace Bit.App.Pages
new SettingsPageListGroup(toolsItems, AppResources.Tools, doUpper),
new SettingsPageListGroup(otherItems, AppResources.Other, doUpper)
};
- var settingsItems = new List();
- foreach (var itemGroup in settingsListGroupItems)
+
+ // TODO: refactor this
+ if (Device.RuntimePlatform == Device.Android)
{
- settingsItems.Add(new SettingsPageHeaderListItem(itemGroup.Name));
- settingsItems.AddRange(itemGroup);
+ var items = new List();
+ foreach (var itemGroup in settingsListGroupItems)
+ {
+ items.Add(new SettingsPageHeaderListItem(itemGroup.Name));
+ items.AddRange(itemGroup);
+ }
+
+ GroupedItems.ReplaceRange(items);
+ }
+ else
+ {
+ Device.InvokeOnMainThreadAsync(async () =>
+ {
+ // HACK: This waitings are to avoid crash on iOS
+ GroupedItems.Clear();
+ await Task.Delay(60);
+
+ foreach (var itemGroup in settingsListGroupItems)
+ {
+ GroupedItems.Add(new SettingsPageHeaderListItem(itemGroup.Name));
+ await Task.Delay(60);
+
+ GroupedItems.AddRange(itemGroup);
+ }
+ }).FireAndForget();
}
- GroupedItems.ReplaceRange(settingsItems);
}
private bool IncludeLinksWithSubscriptionInfo()
diff --git a/src/App/Pages/Vault/AutofillCiphersPage.xaml b/src/App/Pages/Vault/AutofillCiphersPage.xaml
index 8b9bdeff0..5bb8b4004 100644
--- a/src/App/Pages/Vault/AutofillCiphersPage.xaml
+++ b/src/App/Pages/Vault/AutofillCiphersPage.xaml
@@ -29,8 +29,28 @@
ButtonCommand="{Binding BindingContext.CipherOptionsCommand, Source={x:Reference _page}}"
WebsiteIconsEnabled="{Binding BindingContext.WebsiteIconsEnabled, Source={x:Reference _page}}" />
+
+
+
+
+
+
+
+
+
+
@@ -52,30 +72,9 @@
ItemsSource="{Binding GroupedItems}"
VerticalOptions="FillAndExpand"
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
- IsGrouped="True"
SelectionMode="Single"
SelectionChanged="RowSelected"
- StyleClass="list, list-platform">
-
-
-
-
-
-
-
-
-
-
-
-
-
+ StyleClass="list, list-platform" />
diff --git a/src/App/Pages/Vault/AutofillCiphersPageViewModel.cs b/src/App/Pages/Vault/AutofillCiphersPageViewModel.cs
index dc57e9915..5cad8f53e 100644
--- a/src/App/Pages/Vault/AutofillCiphersPageViewModel.cs
+++ b/src/App/Pages/Vault/AutofillCiphersPageViewModel.cs
@@ -11,6 +11,7 @@ using Bit.Core.Utilities;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using Xamarin.CommunityToolkit.ObjectModel;
using Xamarin.Forms;
namespace Bit.App.Pages
@@ -36,14 +37,14 @@ namespace Bit.App.Pages
_stateService = ServiceContainer.Resolve("stateService");
_passwordRepromptService = ServiceContainer.Resolve("passwordRepromptService");
- GroupedItems = new ExtendedObservableCollection();
+ GroupedItems = new ObservableRangeCollection();
CipherOptionsCommand = new Command(CipherOptionsAsync);
}
public string Name { get; set; }
public string Uri { get; set; }
public Command CipherOptionsCommand { get; set; }
- public ExtendedObservableCollection GroupedItems { get; set; }
+ public ObservableRangeCollection GroupedItems { get; set; }
public bool ShowList
{
@@ -105,7 +106,33 @@ namespace Bit.App.Pages
new GroupingsPageListGroup(fuzzy, AppResources.PossibleMatchingItems, fuzzy.Count, false,
!hasMatching));
}
- GroupedItems.ResetWithRange(groupedItems);
+
+ // TODO: refactor this
+ if (Device.RuntimePlatform == Device.Android)
+ {
+ var items = new List();
+ foreach (var itemGroup in groupedItems)
+ {
+ items.Add(new GroupingsPageHeaderListItem(itemGroup.Name, itemGroup.ItemCount));
+ items.AddRange(itemGroup);
+ }
+
+ GroupedItems.ReplaceRange(items);
+ }
+ else
+ {
+ // HACK: This waitings are to avoid crash on iOS
+ GroupedItems.Clear();
+ await Task.Delay(60);
+
+ foreach (var itemGroup in groupedItems)
+ {
+ GroupedItems.Add(new GroupingsPageHeaderListItem(itemGroup.Name, itemGroup.ItemCount));
+ await Task.Delay(60);
+
+ GroupedItems.AddRange(itemGroup);
+ }
+ }
ShowList = groupedItems.Any();
}
diff --git a/src/App/Pages/Vault/GroupingsPage/GroupingsPage.xaml b/src/App/Pages/Vault/GroupingsPage/GroupingsPage.xaml
index 2d5a000f5..bb7c71535 100644
--- a/src/App/Pages/Vault/GroupingsPage/GroupingsPage.xaml
+++ b/src/App/Pages/Vault/GroupingsPage/GroupingsPage.xaml
@@ -55,30 +55,53 @@
+ StyleClass="list-row, list-row-platform">
+ HorizontalOptions="Start"
+ VerticalOptions="Center"
+ StyleClass="list-icon, list-icon-platform"
+ ShouldUpdateFontSizeDynamicallyForAccesibility="True">
+ LineBreakMode="TailTruncation"
+ HorizontalOptions="FillAndExpand"
+ VerticalOptions="CenterAndExpand"
+ StyleClass="list-title"/>
+ HorizontalOptions="End"
+ VerticalOptions="CenterAndExpand"
+ HorizontalTextAlignment="End"
+ StyleClass="list-sub"/>
+
+
+
+
+
+
+
+
+
+
+
@@ -105,31 +128,9 @@
ItemsSource="{Binding GroupedItems}"
VerticalOptions="FillAndExpand"
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
- IsGrouped="True"
SelectionMode="Single"
SelectionChanged="RowSelected"
- StyleClass="list, list-platform">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ StyleClass="list, list-platform" />
diff --git a/src/App/Pages/Vault/GroupingsPage/GroupingsPageHeaderListItem.cs b/src/App/Pages/Vault/GroupingsPage/GroupingsPageHeaderListItem.cs
new file mode 100644
index 000000000..e7912ebc2
--- /dev/null
+++ b/src/App/Pages/Vault/GroupingsPage/GroupingsPageHeaderListItem.cs
@@ -0,0 +1,14 @@
+namespace Bit.App.Pages
+{
+ public class GroupingsPageHeaderListItem : IGroupingsPageListItem
+ {
+ public GroupingsPageHeaderListItem(string title, string itemCount)
+ {
+ Title = title;
+ ItemCount = itemCount;
+ }
+
+ public string Title { get; }
+ public string ItemCount { get; set; }
+ }
+}
diff --git a/src/App/Pages/Vault/GroupingsPage/GroupingsPageListItem.cs b/src/App/Pages/Vault/GroupingsPage/GroupingsPageListItem.cs
index 11e15677a..34674d961 100644
--- a/src/App/Pages/Vault/GroupingsPage/GroupingsPageListItem.cs
+++ b/src/App/Pages/Vault/GroupingsPage/GroupingsPageListItem.cs
@@ -5,7 +5,7 @@ using Bit.Core.Models.View;
namespace Bit.App.Pages
{
- public class GroupingsPageListItem
+ public class GroupingsPageListItem : IGroupingsPageListItem
{
private string _icon;
private string _name;
diff --git a/src/App/Pages/Vault/GroupingsPage/GroupingsPageListItemSelector.cs b/src/App/Pages/Vault/GroupingsPage/GroupingsPageListItemSelector.cs
index 667aeee96..a2e2207b4 100644
--- a/src/App/Pages/Vault/GroupingsPage/GroupingsPageListItemSelector.cs
+++ b/src/App/Pages/Vault/GroupingsPage/GroupingsPageListItemSelector.cs
@@ -4,11 +4,17 @@ namespace Bit.App.Pages
{
public class GroupingsPageListItemSelector : DataTemplateSelector
{
+ public DataTemplate HeaderTemplate { get; set; }
public DataTemplate CipherTemplate { get; set; }
public DataTemplate GroupTemplate { get; set; }
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
+ if (item is GroupingsPageHeaderListItem)
+ {
+ return HeaderTemplate;
+ }
+
if (item is GroupingsPageListItem listItem)
{
return listItem.Cipher != null ? CipherTemplate : GroupTemplate;
diff --git a/src/App/Pages/Vault/GroupingsPage/GroupingsPageViewModel.cs b/src/App/Pages/Vault/GroupingsPage/GroupingsPageViewModel.cs
index 941660b37..7ffe1f982 100644
--- a/src/App/Pages/Vault/GroupingsPage/GroupingsPageViewModel.cs
+++ b/src/App/Pages/Vault/GroupingsPage/GroupingsPageViewModel.cs
@@ -11,6 +11,7 @@ using Bit.Core.Enums;
using Bit.Core.Models.Domain;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
+using Xamarin.CommunityToolkit.ObjectModel;
using Xamarin.Forms;
namespace Bit.App.Pages
@@ -63,7 +64,7 @@ namespace Bit.App.Pages
Loading = true;
PageTitle = AppResources.MyVault;
- GroupedItems = new ExtendedObservableCollection();
+ GroupedItems = new ObservableRangeCollection();
RefreshCommand = new Command(async () =>
{
Refreshing = true;
@@ -144,7 +145,7 @@ namespace Bit.App.Pages
public AccountSwitchingOverlayViewModel AccountSwitchingOverlayViewModel { get; }
- public ExtendedObservableCollection GroupedItems { get; set; }
+ public ObservableRangeCollection GroupedItems { get; set; }
public Command RefreshCommand { get; set; }
public Command CipherOptionsCommand { get; set; }
public bool LoadedOnce { get; set; }
@@ -275,12 +276,38 @@ namespace Bit.App.Pages
{
new GroupingsPageListItem()
{
- IsTrash = true,
+ IsTrash = true,
ItemCount = _deletedCount.ToString("N0")
}
}, AppResources.Trash, _deletedCount, uppercaseGroupNames, false));
}
- GroupedItems.ResetWithRange(groupedItems);
+
+ // TODO: refactor this
+ if (Device.RuntimePlatform == Device.Android)
+ {
+ var items = new List();
+ foreach (var itemGroup in groupedItems)
+ {
+ items.Add(new GroupingsPageHeaderListItem(itemGroup.Name, itemGroup.ItemCount));
+ items.AddRange(itemGroup);
+ }
+
+ GroupedItems.ReplaceRange(items);
+ }
+ else
+ {
+ // HACK: This waitings are to avoid crash on iOS
+ GroupedItems.Clear();
+ await Task.Delay(60);
+
+ foreach (var itemGroup in groupedItems)
+ {
+ GroupedItems.Add(new GroupingsPageHeaderListItem(itemGroup.Name, itemGroup.ItemCount));
+ await Task.Delay(60);
+
+ GroupedItems.AddRange(itemGroup);
+ }
+ }
}
finally
{
diff --git a/src/App/Pages/Vault/GroupingsPage/IGroupingsPageListItem.cs b/src/App/Pages/Vault/GroupingsPage/IGroupingsPageListItem.cs
new file mode 100644
index 000000000..56cb68d87
--- /dev/null
+++ b/src/App/Pages/Vault/GroupingsPage/IGroupingsPageListItem.cs
@@ -0,0 +1,6 @@
+namespace Bit.App.Pages
+{
+ public interface IGroupingsPageListItem
+ {
+ }
+}
diff --git a/src/iOS/AppDelegate.cs b/src/iOS/AppDelegate.cs
index 7de023dc7..0b213e832 100644
--- a/src/iOS/AppDelegate.cs
+++ b/src/iOS/AppDelegate.cs
@@ -228,11 +228,7 @@ namespace Bit.iOS
public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
{
- if (Xamarin.Essentials.Platform.OpenUrl(app, url, options))
- {
- return true;
- }
- return base.OpenUrl(app, url, options);
+ return Xamarin.Essentials.Platform.OpenUrl(app, url, options);
}
public override bool ContinueUserActivity(UIApplication application, NSUserActivity userActivity,