1
0
mirror of https://github.com/bitwarden/mobile synced 2026-01-06 10:34:07 +00:00

Merge branch 'feature/maui-migration' of https://github.com/bitwarden/mobile into feature/maui-migration

This commit is contained in:
Federico Maccaroni
2023-11-09 16:16:26 -03:00
145 changed files with 8020 additions and 2315 deletions

View File

@@ -18,15 +18,16 @@
<StackLayout HorizontalOptions="FillAndExpand">
<Label
StyleClass="text-md"
Text="{u:I18n RememberThisDevice}"/>
Text="{u:I18n RememberThisDevice}" />
<Label
StyleClass="box-sub-label"
Text="{u:I18n TurnOffUsingPublicDevice}"/>
Text="{u:I18n TurnOffUsingPublicDevice}" />
</StackLayout>
<Switch
Scale="0.8"
IsToggled="{Binding RememberThisDevice}"
VerticalOptions="Center"/>
AutomationId="RememberThisDeviceSwitch"
VerticalOptions="Center" />
</StackLayout>
<StackLayout Margin="0, 20, 0, 0">
<Button
@@ -34,31 +35,34 @@
Text="{u:I18n Continue}"
StyleClass="btn-primary"
Command="{Binding ContinueCommand}"
IsVisible="{Binding IsNewUser}"/>
IsVisible="{Binding IsNewUser}"
AutomationId="ContinueButton" />
<Button
x:Name="_approveWithMyOtherDevice"
Text="{u:I18n ApproveWithMyOtherDevice}"
StyleClass="btn-primary"
Command="{Binding ApproveWithMyOtherDeviceCommand}"
IsVisible="{Binding ApproveWithMyOtherDeviceEnabled}"/>
IsVisible="{Binding ApproveWithMyOtherDeviceEnabled}"
AutomationId="ApproveWithMyOtherDeviceButton" />
<Button
x:Name="_requestAdminApproval"
Text="{u:I18n RequestAdminApproval}"
StyleClass="box-button-row"
Command="{Binding RequestAdminApprovalCommand}"
IsVisible="{Binding RequestAdminApprovalEnabled}"/>
IsVisible="{Binding RequestAdminApprovalEnabled}"
AutomationId="RequestAdminApprovalButton" />
<Button
x:Name="_approveWithMasterPassword"
Text="{u:I18n ApproveWithMasterPassword}"
StyleClass="box-button-row"
Command="{Binding ApproveWithMasterPasswordCommand}"
IsVisible="{Binding ApproveWithMasterPasswordEnabled}"/>
IsVisible="{Binding ApproveWithMasterPasswordEnabled}"
AutomationId="ApproveWithMasterPasswordButton" />
<Label
Text="{Binding LoggingInAsText}"
StyleClass="text-sm"
Margin="0,40,0,0"
AutomationId="LoggingInAsLabel"
/>
AutomationId="LoggingInAsLabel" />
<Label
Text="{u:I18n NotYou}"
StyleClass="text-md"

View File

@@ -107,7 +107,7 @@
</Label>
</Grid>
</StackLayout>
<StackLayout Padding="10, 10">
<StackLayout Padding="10, 10" Spacing="6">
<Button x:Name="_loginWithMasterPassword"
Text="{u:I18n LogInWithMasterPassword}"
StyleClass="btn-primary"
@@ -128,11 +128,9 @@
VerticalOptions="CenterAndExpand"
Icon="{Binding Source={x:Static core:BitwardenIcons.Suitcase}}"
Label="{u:I18n LogInSso}"
AutomationId="LogInWithSsoButton">
<controls:IconLabelButton.GestureRecognizers>
<TapGestureRecognizer Tapped="LogInSSO_Clicked" />
</controls:IconLabelButton.GestureRecognizers>
</controls:IconLabelButton>
AutomationId="LogInWithSsoButton"
Tapped="LogInSSO_Clicked"
/>
<Label
Text="{Binding LoggingInAsText}"
StyleClass="text-sm"

View File

@@ -1,16 +1,10 @@
using System;
using System.Threading.Tasks;
using Bit.App.Models;
using Bit.App.Models;
using Bit.App.Utilities;
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Services;
using Bit.Core.Utilities;
using Microsoft.Maui.Controls;
using Microsoft.Maui;
namespace Bit.App.Pages
{
public partial class LoginPage : BaseContentPage
@@ -30,11 +24,11 @@ namespace Bit.App.Pages
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>();
_vm = BindingContext as LoginPageViewModel;
_vm.Page = this;
_vm.StartTwoFactorAction = () => Device.BeginInvokeOnMainThread(async () => await StartTwoFactorAsync());
_vm.LogInSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await LogInSuccessAsync());
_vm.StartTwoFactorAction = () => MainThread.BeginInvokeOnMainThread(async () => await StartTwoFactorAsync());
_vm.LogInSuccessAction = () => MainThread.BeginInvokeOnMainThread(async () => await LogInSuccessAsync());
_vm.LogInWithDeviceAction = () => StartLoginWithDeviceAsync().FireAndForget();
_vm.StartSsoLoginAction = () => Device.BeginInvokeOnMainThread(async () => await StartSsoLoginAsync());
_vm.UpdateTempPasswordAction = () => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
_vm.StartSsoLoginAction = () => MainThread.BeginInvokeOnMainThread(async () => await StartSsoLoginAsync());
_vm.UpdateTempPasswordAction = () => MainThread.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
_vm.CloseAction = async () =>
{
await _accountListOverlay.HideAsync();
@@ -50,8 +44,7 @@ namespace Bit.App.Pages
_vm.Email = email;
MasterPasswordEntry = _masterPassword;
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.iOS)
if (DeviceInfo.Platform == DevicePlatform.iOS)
{
ToolbarItems.Add(_moreItem);
}
@@ -73,14 +66,19 @@ namespace Bit.App.Pages
public Entry MasterPasswordEntry { get; set; }
protected override async void OnAppearing()
protected override async void OnNavigatedTo(NavigatedToEventArgs args)
{
base.OnAppearing();
base.OnNavigatedTo(args);
//IsInitialized is used as a workaround to avoid duplicate initialization issues because of OnNavigatedTo being called twice.
if (HasInitialized) { return; }
HasInitialized = true;
_broadcasterService.Subscribe(nameof(LoginPage), message =>
{
if (message.Command == Constants.ClearSensitiveFields)
{
Device.BeginInvokeOnMainThread(_vm.ResetPasswordField);
MainThread.BeginInvokeOnMainThread(_vm.ResetPasswordField);
}
});
_mainContent.Content = _mainLayout;
@@ -96,13 +94,20 @@ namespace Bit.App.Pages
RequestFocus(_masterPassword);
_inputFocused = true;
}
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.Android && !_vm.CanRemoveAccount)
if (DeviceInfo.Platform == DevicePlatform.Android && !_vm.CanRemoveAccount)
{
ToolbarItems.Add(_removeAccount);
}
}
protected override void OnNavigatedFrom(NavigatedFromEventArgs args)
{
base.OnNavigatedFrom(args);
_accountAvatar?.OnDisappearing();
_broadcasterService.Unsubscribe(nameof(LoginPage));
}
protected override bool OnBackButtonPressed()
{
if (_accountListOverlay.IsVisible)
@@ -113,14 +118,6 @@ namespace Bit.App.Pages
return false;
}
protected override void OnDisappearing()
{
base.OnDisappearing();
_accountAvatar?.OnDisappearing();
_broadcasterService.Unsubscribe(nameof(LoginPage));
}
private async void LogIn_Clicked(object sender, EventArgs e)
{
if (DoOnce())

View File

@@ -155,8 +155,6 @@ namespace Bit.App.Pages
{
try
{
// TODO: [MAUI-Migration] added delay or the modal navigation doesn't happen because of modal-loading is shown
await Task.Delay(1000);
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
await _stateService.SetPreLoginEmailAsync(Email);
await AccountSwitchingOverlayViewModel.RefreshAccountViewsAsync();

View File

@@ -14,7 +14,7 @@
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem Text="{u:I18n Close}" Command="{Binding CloseCommand}" Order="Primary" Priority="-1" x:Name="_closeItem"/>
<ToolbarItem Text="{u:I18n Close}" Command="{Binding CloseCommand}" Order="Primary" Priority="-1" x:Name="_closeItem" />
</ContentPage.ToolbarItems>
<ScrollView>
@@ -29,15 +29,17 @@
<Label
Text="{Binding SubTitle}"
FontSize="Small"
Margin="0,0,0,10"/>
Margin="0,0,0,10"
AutomationId="SubTitleLabel" />
<Label
Text="{Binding Description}"
FontSize="Small"
Margin="0,0,0,24"/>
Margin="0,0,0,24"
AutomationId="DescriptionLabel" />
<Label
Text="{u:I18n FingerprintPhrase}"
FontSize="Small"
FontAttributes="Bold"/>
FontAttributes="Bold" />
<controls:MonoLabel
FormattedText="{Binding FingerprintPhrase}"
FontSize="Small"
@@ -62,7 +64,7 @@
Color="{DynamicResource DisabledIconColor}" />
<Label
Text="{Binding OtherOptions}"
FontSize="Small"/>
FontSize="Small" />
<Label
Text="{u:I18n ViewAllLoginOptions}"
StyleClass="text-sm"

View File

@@ -22,7 +22,7 @@
<StackLayout StyleClass="box">
<Label Text="{u:I18n LogInSsoSummary}"
StyleClass="text-md"
HorizontalTextAlignment="Start"></Label>
HorizontalTextAlignment="Start" />
<StackLayout StyleClass="box-row">
<Label
Text="{u:I18n OrgIdentifier}"
@@ -33,13 +33,15 @@
Keyboard="Default"
StyleClass="box-value"
ReturnType="Go"
ReturnCommand="{Binding LogInCommand}" />
ReturnCommand="{Binding LogInCommand}"
AutomationId="OrgIdentifierEntry" />
</StackLayout>
</StackLayout>
<StackLayout Padding="10, 0">
<Button Text="{u:I18n LogIn}"
StyleClass="btn-primary"
Clicked="LogIn_Clicked"></Button>
Clicked="LogIn_Clicked"
AutomationId="LogInButton" />
</StackLayout>
</StackLayout>
</ScrollView>

View File

@@ -1,11 +1,7 @@
using System;
using System.Threading.Tasks;
using Bit.App.Models;
using Bit.App.Models;
using Bit.App.Utilities;
using Bit.Core.Abstractions;
using Bit.Core.Utilities;
using Microsoft.Maui.Controls;
using Microsoft.Maui;
namespace Bit.App.Pages
{
@@ -24,28 +20,44 @@ namespace Bit.App.Pages
InitializeComponent();
_vm = BindingContext as LoginSsoPageViewModel;
_vm.Page = this;
_vm.StartTwoFactorAction = () => Device.BeginInvokeOnMainThread(async () => await StartTwoFactorAsync());
_vm.StartTwoFactorAction = () => MainThread.BeginInvokeOnMainThread(async () => await StartTwoFactorAsync());
_vm.StartSetPasswordAction = () =>
Device.BeginInvokeOnMainThread(async () => await StartSetPasswordAsync());
_vm.SsoAuthSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await SsoAuthSuccessAsync());
MainThread.BeginInvokeOnMainThread(async () => await StartSetPasswordAsync());
_vm.SsoAuthSuccessAction = () => MainThread.BeginInvokeOnMainThread(async () => await SsoAuthSuccessAsync());
_vm.UpdateTempPasswordAction =
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
() => MainThread.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
_vm.StartDeviceApprovalOptionsAction =
() => Device.BeginInvokeOnMainThread(async () => await StartDeviceApprovalOptionsAsync());
() => MainThread.BeginInvokeOnMainThread(async () => await StartDeviceApprovalOptionsAsync());
_vm.CloseAction = async () =>
{
await Navigation.PopModalAsync();
};
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.Android)
if (DeviceInfo.Platform == DevicePlatform.Android)
{
ToolbarItems.RemoveAt(0);
}
}
protected override async void OnAppearing()
protected override async void OnNavigatedTo(NavigatedToEventArgs args)
{
base.OnAppearing();
base.OnNavigatedTo(args);
//IsInitialized is used as a workaround to avoid duplicate initialization issues because of OnNavigatedTo being called twice.
if (HasInitialized) { return; }
HasInitialized = true;
await _vm.InitAsync();
if (string.IsNullOrWhiteSpace(_vm.OrgIdentifier))
{
RequestFocus(_orgIdentifier);
}
}
protected override async void OnNavigatedFrom(NavigatedFromEventArgs args)
{
base.OnNavigatedFrom(args);
await _vm.InitAsync();
if (string.IsNullOrWhiteSpace(_vm.OrgIdentifier))
{

View File

@@ -25,13 +25,16 @@ namespace Bit.App.Pages
}
}
//IsInitialized is used as a workaround to avoid duplicate initialization issues for some pages where OnNavigatedTo is called twice.
protected bool HasInitialized { get; set; }
public DateTime? LastPageAction { get; set; }
public bool IsThemeDirty { get; set; }
protected override async void OnAppearing()
protected override async void OnNavigatedTo(NavigatedToEventArgs args)
{
base.OnAppearing();
base.OnNavigatedTo(args);
if (IsThemeDirty)
{

View File

@@ -279,6 +279,15 @@
Text="{Binding AddyIoDomainName}"
StyleClass="box-value"
AutomationId="AnonAddyDomainNameEntry" />
<Label IsVisible="{Binding ForwardedEmailServiceSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:ForwardedEmailServiceType.ForwardEmail}}"
Text="{u:I18n DomainNameRequiredParenthesis}"
StyleClass="box-label"
Margin="0,10,0,0"/>
<Entry IsVisible="{Binding ForwardedEmailServiceSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:ForwardedEmailServiceType.ForwardEmail}}"
x:Name="_forwardEmailDomainNameEntry"
Text="{Binding ForwardEmailDomainName}"
StyleClass="box-value"
AutomationId="ForwardEmailDomainNameEntry" />
</StackLayout>
<!--RANDOM WORD OPTIONS-->
<Grid IsVisible="{Binding UsernameTypeSelected, Converter={StaticResource enumToBool}, ConverterParameter={x:Static enums:UsernameType.RandomWord}}">

View File

@@ -78,6 +78,7 @@ namespace Bit.App.Pages
ForwardedEmailServiceType.DuckDuckGo,
ForwardedEmailServiceType.Fastmail,
ForwardedEmailServiceType.FirefoxRelay,
ForwardedEmailServiceType.ForwardEmail,
ForwardedEmailServiceType.SimpleLogin
};
@@ -464,6 +465,8 @@ namespace Bit.App.Pages
return _usernameOptions.FirefoxRelayApiAccessToken;
case ForwardedEmailServiceType.SimpleLogin:
return _usernameOptions.SimpleLoginApiKey;
case ForwardedEmailServiceType.ForwardEmail:
return _usernameOptions.ForwardEmailApiAccessToken;
default:
return null;
}
@@ -513,6 +516,14 @@ namespace Bit.App.Pages
changed = true;
}
break;
case ForwardedEmailServiceType.ForwardEmail:
if (_usernameOptions.ForwardEmailApiAccessToken != value)
{
_usernameOptions.ForwardEmailApiAccessToken = value;
changed = true;
}
break;
default:
break;
}
@@ -537,6 +548,7 @@ namespace Bit.App.Pages
case ForwardedEmailServiceType.DuckDuckGo:
case ForwardedEmailServiceType.Fastmail:
case ForwardedEmailServiceType.SimpleLogin:
case ForwardedEmailServiceType.ForwardEmail:
return AppResources.APIKeyRequiredParenthesis;
default:
return null;
@@ -567,6 +579,20 @@ namespace Bit.App.Pages
}
}
public string ForwardEmailDomainName
{
get => _usernameOptions?.ForwardEmailDomainName;
set
{
if (_usernameOptions != null && _usernameOptions.ForwardEmailDomainName != value)
{
_usernameOptions.ForwardEmailDomainName = value;
TriggerPropertyChanged(nameof(ForwardEmailDomainName));
SaveUsernameOptionsAsync(false).FireAndForget();
}
}
}
public bool CapitalizeRandomWordUsername
{
get => _usernameOptions?.CapitalizeRandomWordUsername == true;
@@ -811,6 +837,7 @@ namespace Bit.App.Pages
TriggerPropertyChanged(nameof(GeneratorTypeSelected));
TriggerPropertyChanged(nameof(UsernameTypeDescriptionLabel));
TriggerPropertyChanged(nameof(EmailWebsite));
TriggerPropertyChanged(nameof(ForwardEmailDomainName));
}
private void SetOptions()

View File

@@ -12,6 +12,7 @@
xmlns:core="clr-namespace:Bit.Core"
x:DataType="pages:SendAddEditPageViewModel"
HideSoftInputOnTapped="True"
Unloaded="OnUnloaded"
x:Name="_page"
Title="{Binding PageTitle}">
<ContentPage.BindingContext>
@@ -310,6 +311,7 @@
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<controls:ExtendedDatePicker
x:Name="_deletionExtendedDatePicker"
NullableDate="{Binding DeletionDateTimeViewModel.Date, Mode=TwoWay}"
Format="d"
IsEnabled="{Binding SendEnabled}"
@@ -353,6 +355,7 @@
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<controls:ExtendedDatePicker
x:Name="_expirationExtendedDatePicker"
NullableDate="{Binding ExpirationDateTimeViewModel.Date, Mode=TwoWay}"
PlaceHolder="mm/dd/yyyy"
Format="d"

View File

@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Bit.App.Models;
using Bit.App.Models;
using Bit.Core.Resources.Localization;
using Bit.App.Utilities;
using Bit.Core.Abstractions;
@@ -9,8 +6,6 @@ using Bit.Core.Enums;
using Bit.Core.Utilities;
using Microsoft.Maui.Controls.PlatformConfiguration;
using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;
using Microsoft.Maui.Controls;
using Microsoft.Maui;
namespace Bit.App.Pages
{
@@ -39,8 +34,7 @@ namespace Bit.App.Pages
_vm.SendId = sendId;
_vm.Type = appOptions?.CreateSend?.Item1 ?? type;
SetActivityIndicator();
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.Android)
if (DeviceInfo.Platform == DevicePlatform.Android)
{
if (_vm.EditMode)
{
@@ -57,7 +51,7 @@ namespace Bit.App.Pages
_btnOptionsDown.WidthRequest = 30;
_btnOptionsUp.WidthRequest = 30;
}
else if (Device.RuntimePlatform == Device.iOS)
else if (DeviceInfo.Platform == DevicePlatform.iOS)
{
ToolbarItems.Add(_closeItem);
if (_vm.EditMode)
@@ -102,7 +96,7 @@ namespace Bit.App.Pages
{
if (message.Command == "selectFileResult")
{
Device.BeginInvokeOnMainThread(() =>
MainThread.BeginInvokeOnMainThread(() =>
{
var data = message.Data as Tuple<byte[], string>;
_vm.FileData = data.Item1;
@@ -141,8 +135,7 @@ namespace Bit.App.Pages
protected override bool OnBackButtonPressed()
{
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (_vm.IsAddFromShare && Device.RuntimePlatform == Device.Android)
if (_vm.IsAddFromShare && DeviceInfo.Platform == DevicePlatform.Android)
{
_appOptions.CreateSend = null;
}
@@ -152,13 +145,18 @@ namespace Bit.App.Pages
protected override void OnDisappearing()
{
base.OnDisappearing();
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform != Device.iOS)
if (DeviceInfo.Platform == DevicePlatform.iOS)
{
_broadcasterService.Unsubscribe(nameof(SendAddEditPage));
}
}
private void OnUnloaded(object sender, EventArgs e)
{
_deletionExtendedDatePicker?.DisconnectHandler();
_expirationExtendedDatePicker?.DisconnectHandler();
}
private async void TextType_Clicked(object sender, EventArgs eventArgs)
{
await _vm.TypeChangedAsync(SendType.Text);
@@ -310,8 +308,7 @@ namespace Bit.App.Pages
private void AdjustToolbar()
{
_saveItem.IsEnabled = _vm.SendEnabled;
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (!_vm.SendEnabled && _vm.EditMode && Device.RuntimePlatform == Device.Android)
if (!_vm.SendEnabled && _vm.EditMode && DeviceInfo.Platform == DevicePlatform.Android)
{
ToolbarItems.Remove(_removePassword);
ToolbarItems.Remove(_copyLink);

View File

@@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Bit.App.Abstractions;
using Bit.App.Abstractions;
using Bit.App.Controls;
using Bit.Core.Resources.Localization;
using Bit.App.Utilities;
@@ -11,9 +8,6 @@ using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
using Microsoft.Maui.Networking;
using Microsoft.Maui.Controls;
using Microsoft.Maui;
namespace Bit.App.Pages
{
@@ -395,8 +389,7 @@ namespace Bit.App.Pages
var sendId = await _sendService.SaveWithServerAsync(send, encryptedFileData);
await _deviceActionService.HideLoadingAsync();
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.Android && IsFile)
if (DeviceInfo.Platform == DevicePlatform.Android && IsFile)
{
// Workaround for https://github.com/xamarin/Xamarin.Forms/issues/5418
// Exiting and returning (file picker) calls OnAppearing on list page instead of this modal, and
@@ -455,8 +448,7 @@ namespace Bit.App.Pages
{
if (IsAddFromShare)
{
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (Device.RuntimePlatform == Device.Android)
if (DeviceInfo.Platform == DevicePlatform.Android)
{
_deviceActionService.CloseMainApp();
return;
@@ -513,8 +505,7 @@ namespace Bit.App.Pages
await _platformUtilsService.ShowDialogAsync(AppResources.SendFileEmailVerificationRequired);
}
// TODO Xamarin.Forms.Device.RuntimePlatform is no longer supported. Use Microsoft.Maui.Devices.DeviceInfo.Platform instead. For more details see https://learn.microsoft.com/en-us/dotnet/maui/migration/forms-projects#device-changes
if (IsAddFromShare && Device.RuntimePlatform == Device.Android)
if (IsAddFromShare && DeviceInfo.Platform == DevicePlatform.Android)
{
_deviceActionService.CloseMainApp();
return;
@@ -630,7 +621,7 @@ namespace Bit.App.Pages
internal void TriggerSendTextPropertyChanged()
{
Device.BeginInvokeOnMainThread(() => TriggerPropertyChanged(nameof(Send)));
MainThread.BeginInvokeOnMainThread(() => TriggerPropertyChanged(nameof(Send)));
}
}
}

View File

@@ -23,19 +23,21 @@
Title="{u:I18n EnableSyncOnRefresh}"
IsToggled="{Binding EnableSyncOnRefresh}"
Subtitle="{u:I18n EnableSyncOnRefreshDescription}"
AutomationId="SubmitCrashLogsSwitch"
AutomationId="SyncOnRefreshSwitch"
StyleClass="settings-item-view"
HorizontalOptions="FillAndExpand" />
<StackLayout StyleClass="box" Margin="0,12,0,0">
<Button
Text="{u:I18n SyncNow}"
Command="{Binding SyncCommand}"></Button>
Command="{Binding SyncCommand}"
AutomationId="SyncNowButton"></Button>
<Label
Text="{Binding LastSyncDisplay}"
StyleClass="text-muted, text-sm"
HorizontalTextAlignment="Start"
Margin="0,10" />
Margin="0,10"
AutomationId="LastSyncLabel" />
</StackLayout>
<controls:SettingChooserItemView
@@ -51,7 +53,7 @@
Title="{u:I18n AllowScreenCapture}"
IsToggled="{Binding IsScreenCaptureAllowed}"
IsEnabled="{Binding CanToggleeScreenCaptureAllowed}"
AutomationId="SubmitCrashLogsSwitch"
AutomationId="AllowScreenCaptureSwitch"
StyleClass="settings-item-view"
HorizontalOptions="FillAndExpand" />

View File

@@ -12,7 +12,8 @@
xmlns:dts="clr-namespace:Bit.App.Lists.DataTemplateSelectors"
xmlns:il="clr-namespace:Bit.App.Lists.ItemLayouts.CustomFields"
HideSoftInputOnTapped="True"
xmlns:core="clr-namespace:Bit.Core"
xmlns:core="clr-namespace:Bit.Core"
xmlns:appResources="clr-namespace:Bit.Core.Resources.Localization"
x:DataType="pages:CipherAddEditPageViewModel"
x:Name="_page"
Title="{Binding PageTitle}">
@@ -29,6 +30,8 @@
<u:InverseBoolConverter x:Key="inverseBool" />
<u:StringHasValueConverter x:Key="stringHasValue" />
<u:IsNotNullConverter x:Key="notNull" />
<u:DateTimeConverter x:Key="dateTime" Format="{x:Static appResources:AppResources.CreatedXY}" />
<ToolbarItem Text="{u:I18n Cancel}" Clicked="Close_Clicked" Order="Primary" Priority="-1"
x:Key="closeItem" x:Name="_closeItem" />
<ToolbarItem Text="{u:I18n Collections}"
@@ -230,7 +233,7 @@
Margin="0,10,0,0"
IsVisible="{Binding ShowPasskeyInfo}"/>
<Entry
Text="{Binding CreationDate}"
Text="{Binding Cipher.Login.MainFido2Credential.CreationDate, Mode=OneWay, Converter={StaticResource dateTime}, FallbackValue=''}"
IsEnabled="False"
StyleClass="box-value,text-muted"
IsVisible="{Binding ShowPasskeyInfo}" />

View File

@@ -312,7 +312,7 @@ namespace Bit.App.Pages
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
public bool HasTotpValue => IsLogin && !string.IsNullOrEmpty(Cipher?.Login?.Totp);
public string SetupTotpText => $"{BitwardenIcons.Camera} {AppResources.SetupTotp}";
public bool ShowPasskeyInfo => Cipher?.HasFido2Key == true && !CloneMode;
public bool ShowPasskeyInfo => Cipher?.HasFido2Credential == true && !CloneMode;
public void Init()
{
@@ -374,7 +374,7 @@ namespace Bit.App.Pages
if (Cipher.Type == CipherType.Login)
{
// passkeys can't be cloned
Cipher.Login.Fido2Keys = null;
Cipher.Login.Fido2Credentials = null;
}
}
if (appOptions?.OtpData != null && Cipher.Type == CipherType.Login)

View File

@@ -6,11 +6,11 @@
xmlns:pages="clr-namespace:Bit.App.Pages"
xmlns:u="clr-namespace:Bit.App.Utilities"
xmlns:controls="clr-namespace:Bit.App.Controls"
xmlns:effects="clr-namespace:Bit.App.Effects"
xmlns:views="clr-namespace:Bit.Core.Models.View"
xmlns:dts="clr-namespace:Bit.App.Lists.DataTemplateSelectors"
xmlns:il="clr-namespace:Bit.App.Lists.ItemLayouts.CustomFields"
xmlns:core="clr-namespace:Bit.Core"
xmlns:core="clr-namespace:Bit.Core"
xmlns:localization="clr-namespace:Bit.Core.Resources.Localization"
x:DataType="pages:CipherDetailsPageViewModel"
HideSoftInputOnTapped="True"
x:Name="_page"
@@ -24,6 +24,7 @@
<u:InverseBoolConverter x:Key="inverseBool" />
<u:StringHasValueConverter x:Key="stringHasValue" />
<u:IsNotNullConverter x:Key="notNull" />
<u:DateTimeConverter x:Key="dateTime" Format="{x:Static localization:AppResources.CreatedXY}" />
<ToolbarItem Text="{u:I18n Collections}"
x:Key="collectionsItem"
x:Name="_collectionsItem"
@@ -200,12 +201,12 @@
Text="{u:I18n Passkey}"
StyleClass="box-label"
Margin="0,10,0,0"
IsVisible="{Binding Cipher.Login.MainFido2Key, Converter={StaticResource notNull}}"/>
IsVisible="{Binding Cipher.Login.MainFido2Credential, Converter={StaticResource notNull}}"/>
<Entry
Text="{Binding CreationDate}"
Text="{Binding Cipher.Login.MainFido2Credential.CreationDate, Mode=OneWay, Converter={StaticResource dateTime}, FallbackValue=''}"
IsEnabled="False"
StyleClass="box-value,text-muted"
IsVisible="{Binding Cipher.Login.MainFido2Key, Converter={StaticResource notNull}}" />
IsVisible="{Binding Cipher.Login.MainFido2Credential, Converter={StaticResource notNull}, FallbackValue=False}" />
<Grid StyleClass="box-row"
IsVisible="{Binding ShowTotp}"
AutomationId="ItemRow">

View File

@@ -708,7 +708,7 @@ namespace Bit.App.Pages
private async Task<bool> CanCloneAsync()
{
if (!Cipher.HasFido2Key)
if (!Cipher.HasFido2Credential)
{
return true;
}

View File

@@ -126,24 +126,27 @@ namespace Bit.App.Pages
await LoadOnAppearedAsync(_mainLayout, false, async () =>
{
if (!_syncService.SyncInProgress || (await _cipherService.GetAllAsync()).Any())
if (_previousPage == null)
{
try
if (!_syncService.SyncInProgress || (await _cipherService.GetAllAsync()).Any())
{
await _vm.LoadAsync();
try
{
await _vm.LoadAsync();
}
catch (Exception e) when (e.Message.Contains("No key."))
{
await Task.Delay(1000);
await _vm.LoadAsync();
}
}
catch (Exception e) when (e.Message.Contains("No key."))
else
{
await Task.Delay(1000);
await _vm.LoadAsync();
}
}
else
{
await Task.Delay(5000);
if (!_vm.Loaded)
{
await _vm.LoadAsync();
await Task.Delay(5000);
if (!_vm.Loaded)
{
await _vm.LoadAsync();
}
}
}
await ShowPreviousPageAsync();

View File

@@ -113,15 +113,9 @@ namespace Bit.App.Pages
try
{
await _deviceActionService.ShowLoadingAsync(AppResources.Saving);
var error = await _cipherService.ShareWithServerAsync(cipherView, OrganizationId, checkedCollectionIds);
await _cipherService.ShareWithServerAsync(cipherView, OrganizationId, checkedCollectionIds);
await _deviceActionService.HideLoadingAsync();
if (error == ICipherService.ShareWithServerError.DuplicatedPasskeyInOrg)
{
_platformUtilsService.ShowToast(null, null, AppResources.ThisItemCannotBeSharedWithTheOrganizationBecauseThereIsOneAlreadyWithTheSamePasskey);
return false;
}
var movedItemToOrgText = string.Format(AppResources.MovedItemToOrg, cipherView.Name,
(await _organizationService.GetAsync(OrganizationId)).Name);
_platformUtilsService.ShowToast("success", null, movedItemToOrgText);