Compare commits

...

3 Commits

Author SHA1 Message Date
Federico Maccaroni
ea28fd462d Fixed flickering on iOS while loading collections for the collection crash hotfix 2022-03-18 15:59:19 -03:00
Federico Maccaroni
14b6ccca6d Fix iOS 15.4 crash from empty list to adding an item by awaiting after every header add; also added that on Settings just in case there is another crash scenario 2022-03-17 17:55:33 -03:00
github-actions[bot]
4202a90674 Bumped version to 2.16.4 (#1846)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
(cherry picked from commit c47aad0412)
2022-03-16 12:50:59 -07:00
21 changed files with 317 additions and 105 deletions

View File

@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2.16.3" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2.16.4" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30"/>

View File

@@ -0,0 +1,6 @@
namespace Bit.App.Pages
{
public interface ISendGroupingsPageListItem
{
}
}

View File

@@ -72,7 +72,29 @@
</controls:ExtendedStackLayout>
</DataTemplate>
<DataTemplate
x:Key="headerTemplate"
x:DataType="pages:SendGroupingsPageHeaderListItem">
<StackLayout
Spacing="0" Padding="0" VerticalOptions="FillAndExpand"
StyleClass="list-row-header-container, list-row-header-container-platform">
<BoxView
StyleClass="list-section-separator-top, list-section-separator-top-platform" />
<StackLayout StyleClass="list-row-header, list-row-header-platform">
<Label
Text="{Binding Title}"
StyleClass="list-header, list-header-platform" />
<Label
Text="{Binding ItemCount}"
StyleClass="list-header-sub" />
</StackLayout>
<BoxView
StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" />
</StackLayout>
</DataTemplate>
<pages:SendGroupingsPageListItemSelector x:Key="sendListItemDataTemplateSelector"
HeaderTemplate="{StaticResource headerTemplate}"
SendTemplate="{StaticResource sendTemplate}"
GroupTemplate="{StaticResource sendGroupTemplate}" />
@@ -113,32 +135,9 @@
ItemsSource="{Binding GroupedSends}"
VerticalOptions="FillAndExpand"
ItemTemplate="{StaticResource sendListItemDataTemplateSelector}"
IsGrouped="True"
SelectionMode="Single"
SelectionChanged="RowSelected"
StyleClass="list, list-platform">
<CollectionView.GroupHeaderTemplate>
<DataTemplate x:DataType="pages:SendGroupingsPageListGroup">
<StackLayout
Spacing="0" Padding="0" VerticalOptions="FillAndExpand"
StyleClass="list-row-header-container, list-row-header-container-platform">
<BoxView
StyleClass="list-section-separator-top, list-section-separator-top-platform" />
<StackLayout StyleClass="list-row-header, list-row-header-platform">
<Label
Text="{Binding Name}"
StyleClass="list-header, list-header-platform" />
<Label
Text="{Binding ItemCount}"
StyleClass="list-header-sub" />
</StackLayout>
<BoxView
StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" />
</StackLayout>
</DataTemplate>
</CollectionView.GroupHeaderTemplate>
</controls:ExtendedCollectionView>
StyleClass="list, list-platform" />
</RefreshView>
</StackLayout>
</ResourceDictionary>

View File

@@ -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; }
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
@@ -50,7 +51,7 @@ namespace Bit.App.Pages
Loading = true;
PageTitle = AppResources.Send;
GroupedSends = new ExtendedObservableCollection<SendGroupingsPageListGroup>();
GroupedSends = new ObservableRangeCollection<ISendGroupingsPageListItem>();
RefreshCommand = new Command(async () =>
{
Refreshing = true;
@@ -105,7 +106,7 @@ namespace Bit.App.Pages
get => _showList;
set => SetProperty(ref _showList, value);
}
public ExtendedObservableCollection<SendGroupingsPageListGroup> GroupedSends { get; set; }
public ObservableRangeCollection<ISendGroupingsPageListItem> GroupedSends { get; set; }
public Command RefreshCommand { get; set; }
public Command<SendView> SendOptionsCommand { get; set; }
public bool LoadedOnce { get; set; }
@@ -177,7 +178,49 @@ namespace Bit.App.Pages
MainPage ? AppResources.AllSends : AppResources.Sends, sendsListItems.Count,
uppercaseGroupNames, !MainPage));
}
GroupedSends.ResetWithRange(groupedSends);
// TODO: refactor this
if (Device.RuntimePlatform == Device.Android
||
GroupedSends.Any())
{
var items = new List<ISendGroupingsPageListItem>();
foreach (var itemGroup in groupedSends)
{
items.Add(new SendGroupingsPageHeaderListItem(itemGroup.Name, itemGroup.ItemCount));
items.AddRange(itemGroup);
}
GroupedSends.ReplaceRange(items);
}
else
{
// HACK: we need this on iOS, so that it doesn't crash when adding coming from an empty list
var first = true;
var items = new List<ISendGroupingsPageListItem>();
foreach (var itemGroup in groupedSends)
{
if (!first)
{
items.Add(new SendGroupingsPageHeaderListItem(itemGroup.Name, itemGroup.ItemCount));
}
else
{
first = false;
}
items.AddRange(itemGroup);
}
if (groupedSends.Any())
{
GroupedSends.ReplaceRange(new List<ISendGroupingsPageListItem> { new SendGroupingsPageHeaderListItem(groupedSends[0].Name, groupedSends[0].ItemCount) });
GroupedSends.AddRange(items);
}
else
{
GroupedSends.Clear();
}
}
}
finally
{

View File

@@ -521,13 +521,49 @@ namespace Bit.App.Pages
new SettingsPageListGroup(toolsItems, AppResources.Tools, doUpper),
new SettingsPageListGroup(otherItems, AppResources.Other, doUpper)
};
var settingsItems = new List<ISettingsPageListItem>();
foreach (var itemGroup in settingsListGroupItems)
// TODO: refactor this
if (Device.RuntimePlatform == Device.Android
||
GroupedItems.Any())
{
settingsItems.Add(new SettingsPageHeaderListItem(itemGroup.Name));
settingsItems.AddRange(itemGroup);
var items = new List<ISettingsPageListItem>();
foreach (var itemGroup in settingsListGroupItems)
{
items.Add(new SettingsPageHeaderListItem(itemGroup.Name));
items.AddRange(itemGroup);
}
GroupedItems.ReplaceRange(items);
}
else
{
// HACK: we need this on iOS, so that it doesn't crash when adding coming from an empty list
var first = true;
var items = new List<ISettingsPageListItem>();
foreach (var itemGroup in settingsListGroupItems)
{
if (!first)
{
items.Add(new SettingsPageHeaderListItem(itemGroup.Name));
}
else
{
first = false;
}
items.AddRange(itemGroup);
}
if (settingsListGroupItems.Any())
{
GroupedItems.ReplaceRange(new List<ISettingsPageListItem> { new SettingsPageHeaderListItem(settingsListGroupItems[0].Name) });
GroupedItems.AddRange(items);
}
else
{
GroupedItems.Clear();
}
}
GroupedItems.ReplaceRange(settingsItems);
}
private bool IncludeLinksWithSubscriptionInfo()

View File

@@ -29,8 +29,28 @@
ButtonCommand="{Binding BindingContext.CipherOptionsCommand, Source={x:Reference _page}}"
WebsiteIconsEnabled="{Binding BindingContext.WebsiteIconsEnabled, Source={x:Reference _page}}" />
</DataTemplate>
<DataTemplate
x:Key="headerTemplate"
x:DataType="pages:GroupingsPageHeaderListItem">
<StackLayout
Spacing="0" Padding="0" VerticalOptions="FillAndExpand"
StyleClass="list-row-header-container, list-row-header-container-platform">
<BoxView
StyleClass="list-section-separator-top, list-section-separator-top-platform" />
<StackLayout StyleClass="list-row-header, list-row-header-platform">
<Label
Text="{Binding Title}"
StyleClass="list-header, list-header-platform" />
<Label
Text="{Binding ItemCount}"
StyleClass="list-header-sub" />
</StackLayout>
</StackLayout>
</DataTemplate>
<pages:GroupingsPageListItemSelector x:Key="listItemDataTemplateSelector"
HeaderTemplate="{StaticResource headerTemplate}"
CipherTemplate="{StaticResource cipherTemplate}" />
<StackLayout x:Key="mainLayout" x:Name="_mainLayout">
@@ -52,30 +72,9 @@
ItemsSource="{Binding GroupedItems}"
VerticalOptions="FillAndExpand"
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
IsGrouped="True"
SelectionMode="Single"
SelectionChanged="RowSelected"
StyleClass="list, list-platform">
<CollectionView.GroupHeaderTemplate>
<DataTemplate x:DataType="pages:GroupingsPageListGroup">
<StackLayout
Spacing="0" Padding="0" VerticalOptions="FillAndExpand"
StyleClass="list-row-header-container, list-row-header-container-platform">
<BoxView
StyleClass="list-section-separator-top, list-section-separator-top-platform" />
<StackLayout StyleClass="list-row-header, list-row-header-platform">
<Label
Text="{Binding Name}"
StyleClass="list-header, list-header-platform" />
<Label
Text="{Binding ItemCount}"
StyleClass="list-header-sub" />
</StackLayout>
</StackLayout>
</DataTemplate>
</CollectionView.GroupHeaderTemplate>
</controls:ExtendedCollectionView>
StyleClass="list, list-platform" />
</StackLayout>
</ResourceDictionary>
</ContentPage.Resources>

View File

@@ -13,6 +13,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Xamarin.CommunityToolkit.ObjectModel;
using Xamarin.Forms;
namespace Bit.App.Pages
@@ -38,14 +39,14 @@ namespace Bit.App.Pages
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
GroupedItems = new ExtendedObservableCollection<GroupingsPageListGroup>();
GroupedItems = new ObservableRangeCollection<IGroupingsPageListItem>();
CipherOptionsCommand = new Command<CipherView>(CipherOptionsAsync);
}
public string Name { get; set; }
public string Uri { get; set; }
public Command CipherOptionsCommand { get; set; }
public ExtendedObservableCollection<GroupingsPageListGroup> GroupedItems { get; set; }
public ObservableRangeCollection<IGroupingsPageListItem> GroupedItems { get; set; }
public bool ShowList
{
@@ -108,7 +109,49 @@ namespace Bit.App.Pages
new GroupingsPageListGroup(fuzzy, AppResources.PossibleMatchingItems, fuzzy.Count, false,
!hasMatching));
}
GroupedItems.ResetWithRange(groupedItems);
// TODO: refactor this
if (Device.RuntimePlatform == Device.Android
||
GroupedItems.Any())
{
var items = new List<IGroupingsPageListItem>();
foreach (var itemGroup in groupedItems)
{
items.Add(new GroupingsPageHeaderListItem(itemGroup.Name, itemGroup.ItemCount));
items.AddRange(itemGroup);
}
GroupedItems.ReplaceRange(items);
}
else
{
// HACK: we need this on iOS, so that it doesn't crash when adding coming from an empty list
var first = true;
var items = new List<IGroupingsPageListItem>();
foreach (var itemGroup in groupedItems)
{
if (!first)
{
items.Add(new GroupingsPageHeaderListItem(itemGroup.Name, itemGroup.ItemCount));
}
else
{
first = false;
}
items.AddRange(itemGroup);
}
if (groupedItems.Any())
{
GroupedItems.ReplaceRange(new List<IGroupingsPageListItem> { new GroupingsPageHeaderListItem(groupedItems[0].Name, groupedItems[0].ItemCount) });
GroupedItems.AddRange(items);
}
else
{
GroupedItems.Clear();
}
}
ShowList = groupedItems.Any();
}

View File

@@ -46,7 +46,7 @@
<DataTemplate x:Key="groupTemplate"
x:DataType="pages:GroupingsPageListItem">
<controls:ExtendedStackLayout Orientation="Horizontal"
StyleClass="list-row, list-row-platform">
StyleClass="list-row, list-row-platform">
<controls:IconLabel Text="{Binding Icon, Mode=OneWay}"
HorizontalOptions="Start"
VerticalOptions="Center"
@@ -56,19 +56,42 @@
</controls:IconLabel.Effects>
</controls:IconLabel>
<Label Text="{Binding Name, Mode=OneWay}"
LineBreakMode="TailTruncation"
HorizontalOptions="FillAndExpand"
VerticalOptions="CenterAndExpand"
StyleClass="list-title"/>
LineBreakMode="TailTruncation"
HorizontalOptions="FillAndExpand"
VerticalOptions="CenterAndExpand"
StyleClass="list-title"/>
<Label Text="{Binding ItemCount, Mode=OneWay}"
HorizontalOptions="End"
VerticalOptions="CenterAndExpand"
HorizontalTextAlignment="End"
StyleClass="list-sub"/>
HorizontalOptions="End"
VerticalOptions="CenterAndExpand"
HorizontalTextAlignment="End"
StyleClass="list-sub"/>
</controls:ExtendedStackLayout>
</DataTemplate>
<DataTemplate
x:Key="headerTemplate"
x:DataType="pages:GroupingsPageHeaderListItem">
<StackLayout
Spacing="0"
Padding="0"
VerticalOptions="FillAndExpand"
StyleClass="list-row-header-container, list-row-header-container-platform">
<BoxView
StyleClass="list-section-separator-top, list-section-separator-top-platform" />
<StackLayout StyleClass="list-row-header, list-row-header-platform">
<Label
Text="{Binding Title}"
StyleClass="list-header, list-header-platform" />
<Label
Text="{Binding ItemCount}"
StyleClass="list-header-sub" />
</StackLayout>
<BoxView StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" />
</StackLayout>
</DataTemplate>
<pages:GroupingsPageListItemSelector x:Key="listItemDataTemplateSelector"
HeaderTemplate="{StaticResource headerTemplate}"
CipherTemplate="{StaticResource cipherTemplate}"
GroupTemplate="{StaticResource groupTemplate}" />
@@ -95,31 +118,9 @@
ItemsSource="{Binding GroupedItems}"
VerticalOptions="FillAndExpand"
ItemTemplate="{StaticResource listItemDataTemplateSelector}"
IsGrouped="True"
SelectionMode="Single"
SelectionChanged="RowSelected"
StyleClass="list, list-platform">
<CollectionView.GroupHeaderTemplate>
<DataTemplate x:DataType="pages:GroupingsPageListGroup">
<StackLayout
Spacing="0" Padding="0" VerticalOptions="FillAndExpand"
StyleClass="list-row-header-container, list-row-header-container-platform">
<BoxView
StyleClass="list-section-separator-top, list-section-separator-top-platform" />
<StackLayout StyleClass="list-row-header, list-row-header-platform">
<Label
Text="{Binding Name}"
StyleClass="list-header, list-header-platform" />
<Label
Text="{Binding ItemCount}"
StyleClass="list-header-sub" />
</StackLayout>
<BoxView StyleClass="list-section-separator-bottom, list-section-separator-bottom-platform" />
</StackLayout>
</DataTemplate>
</CollectionView.GroupHeaderTemplate>
</controls:ExtendedCollectionView>
StyleClass="list, list-platform" />
</RefreshView>
</StackLayout>
</ResourceDictionary>

View File

@@ -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; }
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -11,6 +11,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.CommunityToolkit.ObjectModel;
using Xamarin.Forms;
namespace Bit.App.Pages
@@ -65,7 +66,7 @@ namespace Bit.App.Pages
Loading = true;
PageTitle = AppResources.MyVault;
GroupedItems = new ExtendedObservableCollection<GroupingsPageListGroup>();
GroupedItems = new ObservableRangeCollection<IGroupingsPageListItem>();
RefreshCommand = new Command(async () =>
{
Refreshing = true;
@@ -139,7 +140,7 @@ namespace Bit.App.Pages
get => _websiteIconsEnabled;
set => SetProperty(ref _websiteIconsEnabled, value);
}
public ExtendedObservableCollection<GroupingsPageListGroup> GroupedItems { get; set; }
public ObservableRangeCollection<IGroupingsPageListItem> GroupedItems { get; set; }
public Command RefreshCommand { get; set; }
public Command<CipherView> CipherOptionsCommand { get; set; }
public bool LoadedOnce { get; set; }
@@ -271,12 +272,54 @@ 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
||
GroupedItems.Any())
{
var items = new List<IGroupingsPageListItem>();
foreach (var itemGroup in groupedItems)
{
items.Add(new GroupingsPageHeaderListItem(itemGroup.Name, itemGroup.ItemCount));
items.AddRange(itemGroup);
}
GroupedItems.ReplaceRange(items);
}
else
{
// HACK: we need this on iOS, so that it doesn't crash when adding coming from an empty list
var first = true;
var items = new List<IGroupingsPageListItem>();
foreach (var itemGroup in groupedItems)
{
if (!first)
{
items.Add(new GroupingsPageHeaderListItem(itemGroup.Name, itemGroup.ItemCount));
}
else
{
first = false;
}
items.AddRange(itemGroup);
}
if (groupedItems.Any())
{
GroupedItems.ReplaceRange(new List<IGroupingsPageListItem> { new GroupingsPageHeaderListItem(groupedItems[0].Name, groupedItems[0].ItemCount) });
GroupedItems.AddRange(items);
}
else
{
GroupedItems.Clear();
}
}
}
finally
{

View File

@@ -0,0 +1,6 @@
namespace Bit.App.Pages
{
public interface IGroupingsPageListItem
{
}
}

View File

@@ -11,7 +11,7 @@
<key>CFBundleIdentifier</key>
<string>com.8bit.bitwarden.autofill</string>
<key>CFBundleShortVersionString</key>
<string>2.16.3</string>
<string>2.16.4</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>CFBundleLocalizations</key>

View File

@@ -11,7 +11,7 @@
<key>CFBundleIdentifier</key>
<string>com.8bit.bitwarden.find-login-action-extension</string>
<key>CFBundleShortVersionString</key>
<string>2.16.3</string>
<string>2.16.4</string>
<key>CFBundleLocalizations</key>
<array>
<string>en</string>

View File

@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>2.16.3</string>
<string>2.16.4</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>MinimumOSVersion</key>

View File

@@ -234,11 +234,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,

View File

@@ -11,7 +11,7 @@
<key>CFBundleIdentifier</key>
<string>com.8bit.bitwarden</string>
<key>CFBundleShortVersionString</key>
<string>2.16.3</string>
<string>2.16.4</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>CFBundleIconName</key>