1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-10 21:33:36 +00:00

Merge branch 'main' into DEVOPS-1822-upload-latest-ios-native-mobile-build-to-beta-app

This commit is contained in:
Opeyemi
2024-02-22 15:29:47 +01:00
committed by GitHub
14 changed files with 266 additions and 78 deletions

View File

@@ -770,7 +770,7 @@ jobs:
secrets: "crowdin-api-token" secrets: "crowdin-api-token"
- name: Upload Sources - name: Upload Sources
uses: crowdin/github-action@198daeb2d30636c4608d6a6bb96c009dbefc02a2 # v1.18.0 uses: crowdin/github-action@c953b17499daa6be3e5afbf7a63616fb02d8b18d # v1.19.0
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}

View File

@@ -30,7 +30,7 @@ jobs:
secrets: "crowdin-api-token, github-gpg-private-key, github-gpg-private-key-passphrase" secrets: "crowdin-api-token, github-gpg-private-key, github-gpg-private-key-passphrase"
- name: Download translations - name: Download translations
uses: crowdin/github-action@198daeb2d30636c4608d6a6bb96c009dbefc02a2 # v1.18.0 uses: crowdin/github-action@c953b17499daa6be3e5afbf7a63616fb02d8b18d # v1.19.0
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}

View File

@@ -68,7 +68,7 @@ jobs:
- name: Download all artifacts - name: Download all artifacts
if: ${{ github.event.inputs.release_type != 'Dry Run' }} if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: dawidd6/action-download-artifact@e7466d1a7587ed14867642c2ca74b5bcc1e19a2d # v3.0.0 uses: dawidd6/action-download-artifact@72aaadce3bc708349fc665eee3785cbb1b6e51d0 # v3.1.1
with: with:
workflow: build.yml workflow: build.yml
workflow_conclusion: success workflow_conclusion: success
@@ -76,7 +76,7 @@ jobs:
- name: Dry Run - Download all artifacts - name: Dry Run - Download all artifacts
if: ${{ github.event.inputs.release_type == 'Dry Run' }} if: ${{ github.event.inputs.release_type == 'Dry Run' }}
uses: dawidd6/action-download-artifact@e7466d1a7587ed14867642c2ca74b5bcc1e19a2d # v3.0.0 uses: dawidd6/action-download-artifact@72aaadce3bc708349fc665eee3785cbb1b6e51d0 # v3.1.1
with: with:
workflow: build.yml workflow: build.yml
workflow_conclusion: success workflow_conclusion: success
@@ -130,7 +130,7 @@ jobs:
- name: Download F-Droid .apk artifact - name: Download F-Droid .apk artifact
if: ${{ github.event.inputs.release_type != 'Dry Run' }} if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: dawidd6/action-download-artifact@e7466d1a7587ed14867642c2ca74b5bcc1e19a2d # v3.0.0 uses: dawidd6/action-download-artifact@72aaadce3bc708349fc665eee3785cbb1b6e51d0 # v3.1.1
with: with:
workflow: build.yml workflow: build.yml
workflow_conclusion: success workflow_conclusion: success
@@ -139,7 +139,7 @@ jobs:
- name: Dry Run - Download F-Droid .apk artifact - name: Dry Run - Download F-Droid .apk artifact
if: ${{ github.event.inputs.release_type == 'Dry Run' }} if: ${{ github.event.inputs.release_type == 'Dry Run' }}
uses: dawidd6/action-download-artifact@e7466d1a7587ed14867642c2ca74b5bcc1e19a2d # v3.0.0 uses: dawidd6/action-download-artifact@72aaadce3bc708349fc665eee3785cbb1b6e51d0 # v3.1.1
with: with:
workflow: build.yml workflow: build.yml
workflow_conclusion: success workflow_conclusion: success

View File

@@ -47,6 +47,7 @@ namespace Bit.Core
public const string ConfigsKey = "configsKey"; public const string ConfigsKey = "configsKey";
public const string DisplayEuEnvironmentFlag = "display-eu-environment"; public const string DisplayEuEnvironmentFlag = "display-eu-environment";
public const string RegionEnvironment = "regionEnvironment"; public const string RegionEnvironment = "regionEnvironment";
public const string DuoCallback = "bitwarden://duo-callback";
/// <summary> /// <summary>
/// This key is used to store the value of "ShouldConnectToWatch" of the last user that had logged in /// This key is used to store the value of "ShouldConnectToWatch" of the last user that had logged in

View File

@@ -132,14 +132,26 @@
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
<StackLayout Spacing="0" Padding="0" IsVisible="{Binding DuoMethod, Mode=OneWay}" <StackLayout
VerticalOptions="StartAndExpand"> Spacing="0"
Padding="0"
IsVisible="{Binding DuoMethod, Mode=OneWay}"
VerticalOptions="FillAndExpand">
<Label
StyleClass="box"
Text="{Binding DuoFramelessLabel}"
HorizontalOptions="StartAndExpand"
Margin="10,21"
IsVisible="{Binding IsDuoFrameless}"/>
<controls:HybridWebView <controls:HybridWebView
x:Name="_duoWebView" x:Name="_duoWebView"
HorizontalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
HeightRequest="{Binding DuoWebViewHeight, Mode=OneWay}" /> HeightRequest="{Binding DuoWebViewHeight, Mode=OneWay}"
<StackLayout StyleClass="box" VerticalOptions="End"> IsVisible="{Binding IsDuoFrameless, Converter={StaticResource inverseBool}}"/>
<StackLayout
StyleClass="box"
VerticalOptions="End">
<StackLayout StyleClass="box-row, box-row-switch"> <StackLayout StyleClass="box-row, box-row-switch">
<Label <Label
Text="{u:I18n RememberMe}" Text="{u:I18n RememberMe}"
@@ -151,6 +163,12 @@
HorizontalOptions="End" /> HorizontalOptions="End" />
</StackLayout> </StackLayout>
</StackLayout> </StackLayout>
<Button Text="{u:I18n LaunchDuo}"
Margin="10,21"
StyleClass="btn-primary"
Command="{Binding AuthenticateWithDuoFramelessCommand}"
AutomationId="DuoFramelessButton"
IsVisible="{Binding IsDuoFrameless}"/>
</StackLayout> </StackLayout>
<StackLayout <StackLayout
Spacing="0" Spacing="0"

View File

@@ -2,6 +2,7 @@
using System.Windows.Input; using System.Windows.Input;
using Bit.App.Abstractions; using Bit.App.Abstractions;
using Bit.App.Utilities; using Bit.App.Utilities;
using Bit.Core;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
@@ -34,6 +35,7 @@ namespace Bit.App.Pages
private string _webVaultUrl = "https://vault.bitwarden.com"; private string _webVaultUrl = "https://vault.bitwarden.com";
private bool _enableContinue = false; private bool _enableContinue = false;
private bool _showContinue = true; private bool _showContinue = true;
private bool _isDuoFrameless = false;
private double _duoWebViewHeight; private double _duoWebViewHeight;
public TwoFactorPageViewModel() public TwoFactorPageViewModel()
@@ -56,6 +58,7 @@ namespace Bit.App.Pages
PageTitle = AppResources.TwoStepLogin; PageTitle = AppResources.TwoStepLogin;
SubmitCommand = CreateDefaultAsyncRelayCommand(() => MainThread.InvokeOnMainThreadAsync(async () => await SubmitAsync()), allowsMultipleExecutions: false); SubmitCommand = CreateDefaultAsyncRelayCommand(() => MainThread.InvokeOnMainThreadAsync(async () => await SubmitAsync()), allowsMultipleExecutions: false);
MoreCommand = CreateDefaultAsyncRelayCommand(MoreAsync, onException: _logger.Exception, allowsMultipleExecutions: false); MoreCommand = CreateDefaultAsyncRelayCommand(MoreAsync, onException: _logger.Exception, allowsMultipleExecutions: false);
AuthenticateWithDuoFramelessCommand = CreateDefaultAsyncRelayCommand(DuoFramelessAuthenticateAsync, allowsMultipleExecutions: false);
} }
public string TotpInstruction public string TotpInstruction
@@ -103,6 +106,16 @@ namespace Bit.App.Pages
set => SetProperty(ref _enableContinue, value); set => SetProperty(ref _enableContinue, value);
} }
public bool IsDuoFrameless
{
get => _isDuoFrameless;
set => SetProperty(ref _isDuoFrameless, value, additionalPropertyNames: new string[] { nameof(DuoFramelessLabel) });
}
public string DuoFramelessLabel => SelectedProviderType == TwoFactorProviderType.OrganizationDuo ?
$"{AppResources.DuoTwoStepLoginIsRequiredForYourAccount} {AppResources.FollowTheStepsFromDuoToFinishLoggingIn}" :
AppResources.FollowTheStepsFromDuoToFinishLoggingIn;
#if IOS #if IOS
public string YubikeyInstruction => AppResources.YubiKeyInstructionIos; public string YubikeyInstruction => AppResources.YubiKeyInstructionIos;
#else #else
@@ -125,6 +138,7 @@ namespace Bit.App.Pages
} }
public ICommand SubmitCommand { get; } public ICommand SubmitCommand { get; }
public ICommand MoreCommand { get; } public ICommand MoreCommand { get; }
public ICommand AuthenticateWithDuoFramelessCommand { get; }
public Action TwoFactorAuthSuccessAction { get; set; } public Action TwoFactorAuthSuccessAction { get; set; }
public Action LockAction { get; set; } public Action LockAction { get; set; }
public Action StartDeviceApprovalOptionsAction { get; set; } public Action StartDeviceApprovalOptionsAction { get; set; }
@@ -179,15 +193,29 @@ namespace Bit.App.Pages
break; break;
case TwoFactorProviderType.Duo: case TwoFactorProviderType.Duo:
case TwoFactorProviderType.OrganizationDuo: case TwoFactorProviderType.OrganizationDuo:
SetDuoWebViewHeight(); IsDuoFrameless = providerData.ContainsKey("AuthUrl");
var host = WebUtility.UrlEncode(providerData["Host"] as string); if (!IsDuoFrameless)
var req = WebUtility.UrlEncode(providerData["Signature"] as string);
page.DuoWebView.Uri = $"{_webVaultUrl}/duo-connector.html?host={host}&request={req}";
page.DuoWebView.RegisterAction(sig =>
{ {
Token = sig; SetDuoWebViewHeight();
SubmitCommand.Execute(null); var host = WebUtility.UrlEncode(providerData["Host"] as string);
}); var req = WebUtility.UrlEncode(providerData["Signature"] as string);
page.DuoWebView.Uri = $"{_webVaultUrl}/duo-connector.html?host={host}&request={req}";
page.DuoWebView.RegisterAction(sig =>
{
Token = sig;
MainThread.BeginInvokeOnMainThread(async () =>
{
try
{
await SubmitAsync();
}
catch (Exception ex)
{
HandleException(ex);
}
});
});
}
break; break;
case TwoFactorProviderType.Email: case TwoFactorProviderType.Email:
TotpInstruction = string.Format(AppResources.EnterVerificationCodeEmail, TotpInstruction = string.Format(AppResources.EnterVerificationCodeEmail,
@@ -211,6 +239,77 @@ namespace Bit.App.Pages
ShowContinue = !(SelectedProviderType == null || DuoMethod || Fido2Method); ShowContinue = !(SelectedProviderType == null || DuoMethod || Fido2Method);
} }
private async Task DuoFramelessAuthenticateAsync()
{
await _deviceActionService.ShowLoadingAsync(AppResources.Validating);
if (!_authService.TwoFactorProvidersData.TryGetValue(SelectedProviderType.Value, out var providerData) ||
!providerData.TryGetValue("AuthUrl", out var urlObject))
{
throw new InvalidOperationException("Duo authentication error: Could not get ProviderData or AuthUrl");
}
var url = urlObject as string;
if (string.IsNullOrWhiteSpace(url))
{
throw new ArgumentNullException("Duo authentication error: Could not get valid auth url");
}
WebAuthenticatorResult authResult;
try
{
authResult = await WebAuthenticator.AuthenticateAsync(new WebAuthenticatorOptions
{
Url = new Uri(url),
CallbackUrl = new Uri(Constants.DuoCallback)
});
}
catch (TaskCanceledException)
{
// user canceled
await _deviceActionService.HideLoadingAsync();
return;
}
await _deviceActionService.HideLoadingAsync();
if (authResult == null || authResult.Properties == null)
{
throw new InvalidOperationException("Duo authentication error: Could not get result from authentication");
}
if (authResult.Properties.TryGetValue("error", out var resultError))
{
_logger.Error(resultError);
await _platformUtilsService.ShowDialogAsync(AppResources.AnErrorHasOccurred, AppResources.Ok);
return;
}
string code = null;
if (authResult.Properties.TryGetValue("code", out var resultCodeData))
{
code = Uri.UnescapeDataString(resultCodeData);
}
if (string.IsNullOrWhiteSpace(code))
{
throw new ArgumentException("Duo authentication error: response code is null or empty/whitespace");
}
string state = null;
if (authResult.Properties.TryGetValue("state", out var resultStateData))
{
state = Uri.UnescapeDataString(resultStateData);
}
if (string.IsNullOrWhiteSpace(state))
{
throw new ArgumentException("Duo authentication error: response state is null or empty/whitespace");
}
Token = $"{code}|{state}";
await SubmitAsync(true);
}
public void SetDuoWebViewHeight() public void SetDuoWebViewHeight()
{ {
var screenHeight = DeviceDisplay.MainDisplayInfo.Height / DeviceDisplay.MainDisplayInfo.Density; var screenHeight = DeviceDisplay.MainDisplayInfo.Height / DeviceDisplay.MainDisplayInfo.Density;

View File

@@ -50,6 +50,7 @@
x:DataType="pages:SendGroupingsPageListItem"> x:DataType="pages:SendGroupingsPageListItem">
<controls:ExtendedStackLayout Orientation="Horizontal" <controls:ExtendedStackLayout Orientation="Horizontal"
StyleClass="list-row, list-row-platform" StyleClass="list-row, list-row-platform"
Spacing="6"
AutomationId="{Binding AutomationId}"> AutomationId="{Binding AutomationId}">
<controls:IconLabel Text="{Binding Icon, Mode=OneWay}" <controls:IconLabel Text="{Binding Icon, Mode=OneWay}"
HorizontalOptions="Start" HorizontalOptions="Start"

View File

@@ -37,7 +37,6 @@
<Label <Label
Text="{u:I18n FingerprintPhrase}" Text="{u:I18n FingerprintPhrase}"
FontSize="Small" FontSize="Small"
Padding="0, 10, 0 ,0"
FontAttributes="Bold"/> FontAttributes="Bold"/>
<controls:MonoLabel <controls:MonoLabel
FormattedText="{Binding FingerprintPhrase}" FormattedText="{Binding FingerprintPhrase}"
@@ -70,64 +69,70 @@
Grid.ColumnSpan="2"/> Grid.ColumnSpan="2"/>
</Grid> </Grid>
</DataTemplate> </DataTemplate>
<StackLayout
x:Key="mainLayout"
x:Name="_mainLayout"
Padding="0, 10">
<RefreshView
IsRefreshing="{Binding IsRefreshing}"
Command="{Binding RefreshCommand}"
VerticalOptions="FillAndExpand"
BackgroundColor="{DynamicResource BackgroundColor}">
<StackLayout>
<Image
x:Name="_emptyPlaceholder"
Source="empty_login_requests"
HorizontalOptions="Center"
WidthRequest="160"
HeightRequest="160"
Margin="0,70,0,0"
IsVisible="{Binding HasLoginRequests, Converter={StaticResource inverseBool}}"
SemanticProperties.Description="{u:I18n NoPendingRequests}" />
<controls:CustomLabel
StyleClass="box-label-regular"
Text="{u:I18n NoPendingRequests}"
IsVisible="{Binding HasLoginRequests, Converter={StaticResource inverseBool}}"
FontAttributes="{OnPlatform iOS=Bold}"
FontWeight="500"
HorizontalTextAlignment="Center"
Margin="14,10,14,0"/>
<controls:ExtendedCollectionView
ItemsSource="{Binding LoginRequests}"
ItemTemplate="{StaticResource loginRequestTemplate}"
SelectionMode="Single"
IsVisible="{Binding HasLoginRequests}"
ExtraDataForLogging="Login requests page" >
<controls:ExtendedCollectionView.Behaviors>
<xct:EventToCommandBehavior
EventName="SelectionChanged"
Command="{Binding AnswerRequestCommand}"
EventArgsConverter="{StaticResource SelectionChangedEventArgsConverter}" />
</controls:ExtendedCollectionView.Behaviors>
</controls:ExtendedCollectionView>
</StackLayout>
</RefreshView>
<controls:IconLabelButton
VerticalOptions="End"
Margin="10,0"
Icon="{Binding Source={x:Static core:BitwardenIcons.Trash}}"
Label="{u:I18n DeclineAllRequests}"
ButtonCommand="{Binding DeclineAllRequestsCommand}"
IsVisible="{Binding HasLoginRequests}"
AutomationId="DeleteAllRequestsButton" />
</StackLayout>
</ResourceDictionary> </ResourceDictionary>
</ContentPage.Resources> </ContentPage.Resources>
<ContentView <Grid
x:Name="_mainContent"> RowDefinitions="*, Auto"
</ContentView> Padding="0, 10">
<RefreshView
Grid.Row="0"
IsRefreshing="{Binding IsRefreshing}"
Command="{Binding RefreshCommand}"
VerticalOptions="Fill"
BackgroundColor="{DynamicResource BackgroundColor}">
<Grid RowDefinitions="Auto, *">
<VerticalStackLayout Grid.Row="0"
HorizontalOptions="Center">
<Image
x:Name="_emptyPlaceholder"
Source="empty_login_requests"
WidthRequest="160"
HeightRequest="160"
Margin="0,70,0,0"
IsVisible="{Binding HasLoginRequests, Converter={StaticResource inverseBool}}"
SemanticProperties.Description="{u:I18n NoPendingRequests}" />
<controls:CustomLabel
StyleClass="box-label-regular"
Text="{u:I18n NoPendingRequests}"
IsVisible="{Binding HasLoginRequests, Converter={StaticResource inverseBool}}"
FontAttributes="{OnPlatform iOS=Bold}"
FontWeight="500"
Margin="14,10,14,0"/>
</VerticalStackLayout>
<controls:ExtendedCollectionView
Grid.Row="1"
ItemsSource="{Binding LoginRequests}"
ItemTemplate="{StaticResource loginRequestTemplate}"
SelectionMode="Single"
IsVisible="{Binding HasLoginRequests}"
ExtraDataForLogging="Login requests page" >
<controls:ExtendedCollectionView.Behaviors>
<xct:EventToCommandBehavior
EventName="SelectionChanged"
Command="{Binding AnswerRequestCommand}"
EventArgsConverter="{StaticResource SelectionChangedEventArgsConverter}" />
</controls:ExtendedCollectionView.Behaviors>
</controls:ExtendedCollectionView>
</Grid>
</RefreshView>
<controls:IconLabelButton
Grid.Row="1"
VerticalOptions="End"
Margin="10,0"
Icon="{Binding Source={x:Static core:BitwardenIcons.Trash}}"
Label="{u:I18n DeclineAllRequests}"
ButtonCommand="{Binding DeclineAllRequestsCommand}"
IsVisible="{Binding HasLoginRequests}"
AutomationId="DeleteAllRequestsButton" />
<Grid x:Name="_activityIndicatorGrid" Grid.Row="0" Grid.RowSpan="2" BackgroundColor="{DynamicResource BackgroundColor}">
<ActivityIndicator IsRunning="True"
VerticalOptions="Center"
HorizontalOptions="Center" />
</Grid>
</Grid>
</pages:BaseContentPage> </pages:BaseContentPage>

View File

@@ -5,6 +5,7 @@ using System.Threading.Tasks;
using Bit.App.Utilities; using Bit.App.Utilities;
using Bit.Core.Abstractions; using Bit.Core.Abstractions;
using Bit.Core.Models.Response; using Bit.Core.Models.Response;
using Bit.Core.Services;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Microsoft.Maui.ApplicationModel; using Microsoft.Maui.ApplicationModel;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
@@ -19,7 +20,6 @@ namespace Bit.App.Pages
public LoginPasswordlessRequestsListPage() public LoginPasswordlessRequestsListPage()
{ {
InitializeComponent(); InitializeComponent();
SetActivityIndicator(_mainContent);
_vm = BindingContext as LoginPasswordlessRequestsListViewModel; _vm = BindingContext as LoginPasswordlessRequestsListViewModel;
_vm.Page = this; _vm.Page = this;
} }
@@ -27,9 +27,21 @@ namespace Bit.App.Pages
protected override async void OnAppearing() protected override async void OnAppearing()
{ {
base.OnAppearing(); base.OnAppearing();
await LoadOnAppearedAsync(_mainLayout, false, _vm.RefreshAsync, _mainContent); try
{
_activityIndicatorGrid.IsVisible = true;
UpdatePlaceholder(); await _vm.RefreshAsync();
UpdatePlaceholder();
}
catch (Exception ex)
{
LoggerHelper.LogEvenIfCantBeResolved(ex);
}
finally
{
_activityIndicatorGrid.IsVisible = false;
}
} }
private async void Close_Clicked(object sender, System.EventArgs e) private async void Close_Clicked(object sender, System.EventArgs e)

View File

@@ -108,7 +108,7 @@ namespace Bit.App.Pages
Origin = loginRequestData.Origin Origin = loginRequestData.Origin
}); });
await Device.InvokeOnMainThreadAsync(() => Application.Current.MainPage.Navigation.PushModalAsync(new NavigationPage(page))); await MainThread.InvokeOnMainThreadAsync(() => Application.Current.MainPage.Navigation.PushModalAsync(new NavigationPage(page)));
} }
private async Task DeclineAllRequestsAsync() private async Task DeclineAllRequestsAsync()

View File

@@ -2335,6 +2335,15 @@ namespace Bit.Core.Resources.Localization {
} }
} }
/// <summary>
/// Looks up a localized string similar to Duo two-step login is required for your account. .
/// </summary>
public static string DuoTwoStepLoginIsRequiredForYourAccount {
get {
return ResourceManager.GetString("DuoTwoStepLoginIsRequiredForYourAccount", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Edit. /// Looks up a localized string similar to Edit.
/// </summary> /// </summary>
@@ -3199,6 +3208,15 @@ namespace Bit.Core.Resources.Localization {
} }
} }
/// <summary>
/// Looks up a localized string similar to Follow the steps from Duo to finish logging in..
/// </summary>
public static string FollowTheStepsFromDuoToFinishLoggingIn {
get {
return ResourceManager.GetString("FollowTheStepsFromDuoToFinishLoggingIn", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to {0} is not correctly formatted.. /// Looks up a localized string similar to {0} is not correctly formatted..
/// </summary> /// </summary>
@@ -3793,6 +3811,15 @@ namespace Bit.Core.Resources.Localization {
} }
} }
/// <summary>
/// Looks up a localized string similar to Launch Duo.
/// </summary>
public static string LaunchDuo {
get {
return ResourceManager.GetString("LaunchDuo", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Bitwarden allows you to share your vault items with others by using an organization. Learn more on the bitwarden.com website.. /// Looks up a localized string similar to Bitwarden allows you to share your vault items with others by using an organization. Learn more on the bitwarden.com website..
/// </summary> /// </summary>

View File

@@ -2877,4 +2877,13 @@ Do you want to switch to this account?</value>
<data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve"> <data name="SetUpAnUnlockOptionToChangeYourVaultTimeoutAction" xml:space="preserve">
<value>Set up an unlock option to change your vault timeout action.</value> <value>Set up an unlock option to change your vault timeout action.</value>
</data> </data>
<data name="DuoTwoStepLoginIsRequiredForYourAccount" xml:space="preserve">
<value>Duo two-step login is required for your account. </value>
</data>
<data name="FollowTheStepsFromDuoToFinishLoggingIn" xml:space="preserve">
<value>Follow the steps from Duo to finish logging in.</value>
</data>
<data name="LaunchDuo" xml:space="preserve">
<value>Launch Duo</value>
</data>
</root> </root>

View File

@@ -11,11 +11,15 @@
Value="{DynamicResource TextColor}" /> Value="{DynamicResource TextColor}" />
<Setter Property="Margin" <Setter Property="Margin"
Value="-4, 0, -4, -4" /> Value="-4, 0, -4, -4" />
<Setter Property="FontSize"
Value="18" />
</Style> </Style>
<Style TargetType="Picker" <Style TargetType="Picker"
ApplyToDerivedTypes="True"> ApplyToDerivedTypes="True">
<Setter Property="TextColor" <Setter Property="TextColor"
Value="{DynamicResource TextColor}" /> Value="{DynamicResource TextColor}" />
<Setter Property="FontSize"
Value="18" />
<Setter Property="Margin" <Setter Property="Margin"
Value="-4, 0, -4, -4" /> Value="-4, 0, -4, -4" />
</Style> </Style>
@@ -39,6 +43,8 @@
Value="{DynamicResource TextColor}" /> Value="{DynamicResource TextColor}" />
<Setter Property="PlaceholderColor" <Setter Property="PlaceholderColor"
Value="{DynamicResource InputPlaceholderColor}" /> Value="{DynamicResource InputPlaceholderColor}" />
<Setter Property="FontSize"
Value="18" />
<Setter Property="Margin" <Setter Property="Margin"
Value="-4, 0, -4, -4" /> Value="-4, 0, -4, -4" />
</Style> </Style>
@@ -46,6 +52,8 @@
ApplyToDerivedTypes="True"> ApplyToDerivedTypes="True">
<Setter Property="BackgroundColor" <Setter Property="BackgroundColor"
Value="Transparent" /> Value="Transparent" />
<Setter Property="FontSize"
Value="18" />
<Setter Property="TextColor" <Setter Property="TextColor"
Value="{DynamicResource TitleEntryTextColor}" /> Value="{DynamicResource TitleEntryTextColor}" />
<Setter Property="CancelButtonColor" <Setter Property="CancelButtonColor"

View File

@@ -9,6 +9,8 @@
Value="{DynamicResource InputPlaceholderColor}" /> Value="{DynamicResource InputPlaceholderColor}" />
<Setter Property="TextColor" <Setter Property="TextColor"
Value="{DynamicResource TextColor}" /> Value="{DynamicResource TextColor}" />
<Setter Property="FontSize"
Value="18" />
<Setter Property="Margin" <Setter Property="Margin"
Value="0, 5, 0, 12" /> Value="0, 5, 0, 12" />
</Style> </Style>
@@ -16,6 +18,8 @@
ApplyToDerivedTypes="True"> ApplyToDerivedTypes="True">
<Setter Property="TextColor" <Setter Property="TextColor"
Value="{DynamicResource TextColor}" /> Value="{DynamicResource TextColor}" />
<Setter Property="FontSize"
Value="18" />
<Setter Property="Margin" <Setter Property="Margin"
Value="0, 5, 0, 12" /> Value="0, 5, 0, 12" />
</Style> </Style>
@@ -37,6 +41,8 @@
ApplyToDerivedTypes="True"> ApplyToDerivedTypes="True">
<Setter Property="TextColor" <Setter Property="TextColor"
Value="{DynamicResource TextColor}" /> Value="{DynamicResource TextColor}" />
<Setter Property="FontSize"
Value="18" />
<Setter Property="BackgroundColor" <Setter Property="BackgroundColor"
Value="{DynamicResource BackgroundColor}" /> Value="{DynamicResource BackgroundColor}" />
<Setter Property="PlaceholderColor" <Setter Property="PlaceholderColor"
@@ -51,6 +57,8 @@
</Style> </Style>
<Style TargetType="SearchBar" <Style TargetType="SearchBar"
ApplyToDerivedTypes="True"> ApplyToDerivedTypes="True">
<Setter Property="FontSize"
Value="18" />
<Setter Property="BackgroundColor" <Setter Property="BackgroundColor"
Value="{DynamicResource ListHeaderBackgroundColor}" /> Value="{DynamicResource ListHeaderBackgroundColor}" />
<Setter Property="TextColor" <Setter Property="TextColor"