bitwarden-mobile/src/App/Pages/Send/SendAddEditPageViewModel.cs

658 lines
24 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Bit.App.Abstractions;
using Bit.App.Resources;
using Bit.App.Utilities;
2022-01-21 10:31:03 +01:00
using Bit.Core;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
using Xamarin.Essentials;
using Xamarin.Forms;
namespace Bit.App.Pages
{
public class SendAddEditPageViewModel : BaseViewModel
{
private readonly IDeviceActionService _deviceActionService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IMessagingService _messagingService;
Account Switching (#1807) * Account Switching (#1720) * Account switching * WIP * wip * wip * updates to send test logic * fixed Send tests * fixes for theme handling on account switching and re-adding existing account * switch fixes * fixes * fixes * cleanup * vault timeout fixes * account list status enhancements * logout fixes and token handling improvements * merge latest (#1727) * remove duplicate dependency * fix for initial login token storage paradox (#1730) * Fix avatar color update toolbar item issue on iOS for account switching (#1735) * Updated account switching menu UI (#1733) * updated account switching menu UI * additional changes * add key suffix to constant * GetFirstLetters method tweaks * Fix crash on account switching when logging out when having more than user at a time (#1740) * single account migration to multi-account on app update (#1741) * Account Switching Tap to dismiss (#1743) * Added tap to dismiss on the Account switching overlay and improved a bit the code * Fix account switching overlay background transparent on the proper place * Fixed transparent background and the shadow on the account switching overlay * Fix iOS top space on Account switching list overlay after modal (#1746) * Fix top space added to Account switching list overlay after closing modal * Fix top space added to Account switching list overlay after closing modal on lock, login and home views just in case we add modals in the future there as well * Usability: dismiss account list on certain events (#1748) * dismiss account list on certain events * use new FireAndForget method for back button logic * Create and use Account Switching overlay control (#1753) * Added Account switching overlay control and its own ViewModel and refactored accordingly * Fix account switching Accounts list binding update * Implemented dismiss account switching overlay when changing tabs and when selecting the same tab. Also updated the deprecated listener on CustomTabbedRenderer on Android (#1755) * Overriden Equals on AvatarImageSource so it doesn't get set multiple times when it's the same image thus producing blinking on tab chaged (#1756) * Usability improvements for logout on vault timeout (#1781) * accountswitching fixes (#1784) * Fix for invalid PIN lock state when switching accounts (#1792) * fix for pin lock flow * named tuple values and updated async * clear send service cache on account switch (#1796) * Global theme and account removal (#1793) * Global theme and account removal * remove redundant call to hide account list overlay * cleanup and additional tweaks * add try/catch to remove account dialog flow Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
2022-02-23 18:40:17 +01:00
private readonly IStateService _stateService;
private readonly ISendService _sendService;
private readonly ILogger _logger;
private bool _sendEnabled;
private bool _canAccessPremium;
private bool _emailVerified;
private SendView _send;
private string _fileName;
2021-02-16 21:24:51 +01:00
private bool _showOptions;
private bool _showPassword;
private int _deletionDateTypeSelectedIndex;
private int _expirationDateTypeSelectedIndex;
private DateTime _simpleDeletionDateTime;
private DateTime _deletionDate;
private TimeSpan _deletionTime;
private DateTime? _simpleExpirationDateTime;
private DateTime? _expirationDate;
private TimeSpan? _expirationTime;
private bool _isOverridingPickers;
private int? _maxAccessCount;
2022-04-26 17:21:17 +02:00
private string[] _additionalSendProperties = new[]
{
nameof(IsText),
nameof(IsFile),
};
private bool _disableHideEmail;
private bool _sendOptionsPolicyInEffect;
private bool _copyInsteadOfShareAfterSaving;
public SendAddEditPageViewModel()
{
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
Account Switching (#1807) * Account Switching (#1720) * Account switching * WIP * wip * wip * updates to send test logic * fixed Send tests * fixes for theme handling on account switching and re-adding existing account * switch fixes * fixes * fixes * cleanup * vault timeout fixes * account list status enhancements * logout fixes and token handling improvements * merge latest (#1727) * remove duplicate dependency * fix for initial login token storage paradox (#1730) * Fix avatar color update toolbar item issue on iOS for account switching (#1735) * Updated account switching menu UI (#1733) * updated account switching menu UI * additional changes * add key suffix to constant * GetFirstLetters method tweaks * Fix crash on account switching when logging out when having more than user at a time (#1740) * single account migration to multi-account on app update (#1741) * Account Switching Tap to dismiss (#1743) * Added tap to dismiss on the Account switching overlay and improved a bit the code * Fix account switching overlay background transparent on the proper place * Fixed transparent background and the shadow on the account switching overlay * Fix iOS top space on Account switching list overlay after modal (#1746) * Fix top space added to Account switching list overlay after closing modal * Fix top space added to Account switching list overlay after closing modal on lock, login and home views just in case we add modals in the future there as well * Usability: dismiss account list on certain events (#1748) * dismiss account list on certain events * use new FireAndForget method for back button logic * Create and use Account Switching overlay control (#1753) * Added Account switching overlay control and its own ViewModel and refactored accordingly * Fix account switching Accounts list binding update * Implemented dismiss account switching overlay when changing tabs and when selecting the same tab. Also updated the deprecated listener on CustomTabbedRenderer on Android (#1755) * Overriden Equals on AvatarImageSource so it doesn't get set multiple times when it's the same image thus producing blinking on tab chaged (#1756) * Usability improvements for logout on vault timeout (#1781) * accountswitching fixes (#1784) * Fix for invalid PIN lock state when switching accounts (#1792) * fix for pin lock flow * named tuple values and updated async * clear send service cache on account switch (#1796) * Global theme and account removal (#1793) * Global theme and account removal * remove redundant call to hide account list overlay * cleanup and additional tweaks * add try/catch to remove account dialog flow Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
2022-02-23 18:40:17 +01:00
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
_sendService = ServiceContainer.Resolve<ISendService>("sendService");
_logger = ServiceContainer.Resolve<ILogger>("logger");
TogglePasswordCommand = new Command(TogglePassword);
TypeOptions = new List<KeyValuePair<string, SendType>>
{
new KeyValuePair<string, SendType>(AppResources.TypeText, SendType.Text),
new KeyValuePair<string, SendType>(AppResources.TypeFile, SendType.File),
};
DeletionTypeOptions = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>(AppResources.OneHour, AppResources.OneHour),
new KeyValuePair<string, string>(AppResources.OneDay, AppResources.OneDay),
new KeyValuePair<string, string>(AppResources.TwoDays, AppResources.TwoDays),
new KeyValuePair<string, string>(AppResources.ThreeDays, AppResources.ThreeDays),
new KeyValuePair<string, string>(AppResources.SevenDays, AppResources.SevenDays),
new KeyValuePair<string, string>(AppResources.ThirtyDays, AppResources.ThirtyDays),
new KeyValuePair<string, string>(AppResources.Custom, AppResources.Custom),
};
ExpirationTypeOptions = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>(AppResources.Never, AppResources.Never),
new KeyValuePair<string, string>(AppResources.OneHour, AppResources.OneHour),
new KeyValuePair<string, string>(AppResources.OneDay, AppResources.OneDay),
new KeyValuePair<string, string>(AppResources.TwoDays, AppResources.TwoDays),
new KeyValuePair<string, string>(AppResources.ThreeDays, AppResources.ThreeDays),
new KeyValuePair<string, string>(AppResources.SevenDays, AppResources.SevenDays),
new KeyValuePair<string, string>(AppResources.ThirtyDays, AppResources.ThirtyDays),
new KeyValuePair<string, string>(AppResources.Custom, AppResources.Custom),
};
}
public Command TogglePasswordCommand { get; set; }
public string SendId { get; set; }
public int SegmentedButtonHeight { get; set; }
public int SegmentedButtonFontSize { get; set; }
public Thickness SegmentedButtonMargins { get; set; }
public bool ShowEditorSeparators { get; set; }
public Thickness EditorMargins { get; set; }
public SendType? Type { get; set; }
public byte[] FileData { get; set; }
public string NewPassword { get; set; }
public bool ShareOnSave { get; set; }
public bool DisableHideEmailControl { get; set; }
public bool IsAddFromShare { get; set; }
public string ShareOnSaveText => CopyInsteadOfShareAfterSaving ? AppResources.CopySendLinkOnSave : AppResources.ShareOnSave;
public List<KeyValuePair<string, SendType>> TypeOptions { get; }
public List<KeyValuePair<string, string>> DeletionTypeOptions { get; }
public List<KeyValuePair<string, string>> ExpirationTypeOptions { get; }
public bool SendEnabled
{
get => _sendEnabled;
set => SetProperty(ref _sendEnabled, value);
}
public int DeletionDateTypeSelectedIndex
{
get => _deletionDateTypeSelectedIndex;
set
{
if (SetProperty(ref _deletionDateTypeSelectedIndex, value))
{
DeletionTypeChanged();
}
}
}
public DateTime DeletionDate
{
get => _deletionDate;
set => SetProperty(ref _deletionDate, value);
}
public TimeSpan DeletionTime
{
get => _deletionTime;
set => SetProperty(ref _deletionTime, value);
}
2021-02-16 21:24:51 +01:00
public bool ShowOptions
{
get => _showOptions;
set => SetProperty(ref _showOptions, value);
}
public int ExpirationDateTypeSelectedIndex
{
get => _expirationDateTypeSelectedIndex;
set
{
if (SetProperty(ref _expirationDateTypeSelectedIndex, value))
{
ExpirationTypeChanged();
}
}
}
public DateTime? ExpirationDate
{
get => _expirationDate;
set
{
if (SetProperty(ref _expirationDate, value))
{
ExpirationDateChanged();
}
}
}
public TimeSpan? ExpirationTime
{
get => _expirationTime;
set
{
if (SetProperty(ref _expirationTime, value))
{
ExpirationTimeChanged();
}
}
}
public int? MaxAccessCount
{
get => _maxAccessCount;
set
{
if (SetProperty(ref _maxAccessCount, value))
{
MaxAccessCountChanged();
}
}
}
public bool CopyInsteadOfShareAfterSaving
{
get => _copyInsteadOfShareAfterSaving;
set
{
SetProperty(ref _copyInsteadOfShareAfterSaving, value);
TriggerPropertyChanged(nameof(ShareOnSaveText));
}
}
public SendView Send
{
get => _send;
set => SetProperty(ref _send, value, additionalPropertyNames: _additionalSendProperties);
}
public string FileName
{
get => _fileName;
set
{
if (SetProperty(ref _fileName, value))
{
Send.File.FileName = _fileName;
}
}
}
public bool ShowPassword
{
get => _showPassword;
set => SetProperty(ref _showPassword, value,
2022-04-26 17:21:17 +02:00
additionalPropertyNames: new[]
{
nameof(ShowPasswordIcon)
});
}
public bool DisableHideEmail
{
get => _disableHideEmail;
set => SetProperty(ref _disableHideEmail, value);
}
public bool SendOptionsPolicyInEffect
{
get => _sendOptionsPolicyInEffect;
set => SetProperty(ref _sendOptionsPolicyInEffect, value);
}
public bool ShowTypeButtons => !EditMode && !IsAddFromShare;
public bool EditMode => !string.IsNullOrWhiteSpace(SendId);
public bool IsText => Send?.Type == SendType.Text;
public bool IsFile => Send?.Type == SendType.File;
public bool ShowDeletionCustomPickers => EditMode || DeletionDateTypeSelectedIndex == 6;
public bool ShowExpirationCustomPickers => EditMode || ExpirationDateTypeSelectedIndex == 7;
2022-01-21 10:31:03 +01:00
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
public async Task InitAsync()
{
PageTitle = EditMode ? AppResources.EditSend : AppResources.AddSend;
Account Switching (#1807) * Account Switching (#1720) * Account switching * WIP * wip * wip * updates to send test logic * fixed Send tests * fixes for theme handling on account switching and re-adding existing account * switch fixes * fixes * fixes * cleanup * vault timeout fixes * account list status enhancements * logout fixes and token handling improvements * merge latest (#1727) * remove duplicate dependency * fix for initial login token storage paradox (#1730) * Fix avatar color update toolbar item issue on iOS for account switching (#1735) * Updated account switching menu UI (#1733) * updated account switching menu UI * additional changes * add key suffix to constant * GetFirstLetters method tweaks * Fix crash on account switching when logging out when having more than user at a time (#1740) * single account migration to multi-account on app update (#1741) * Account Switching Tap to dismiss (#1743) * Added tap to dismiss on the Account switching overlay and improved a bit the code * Fix account switching overlay background transparent on the proper place * Fixed transparent background and the shadow on the account switching overlay * Fix iOS top space on Account switching list overlay after modal (#1746) * Fix top space added to Account switching list overlay after closing modal * Fix top space added to Account switching list overlay after closing modal on lock, login and home views just in case we add modals in the future there as well * Usability: dismiss account list on certain events (#1748) * dismiss account list on certain events * use new FireAndForget method for back button logic * Create and use Account Switching overlay control (#1753) * Added Account switching overlay control and its own ViewModel and refactored accordingly * Fix account switching Accounts list binding update * Implemented dismiss account switching overlay when changing tabs and when selecting the same tab. Also updated the deprecated listener on CustomTabbedRenderer on Android (#1755) * Overriden Equals on AvatarImageSource so it doesn't get set multiple times when it's the same image thus producing blinking on tab chaged (#1756) * Usability improvements for logout on vault timeout (#1781) * accountswitching fixes (#1784) * Fix for invalid PIN lock state when switching accounts (#1792) * fix for pin lock flow * named tuple values and updated async * clear send service cache on account switch (#1796) * Global theme and account removal (#1793) * Global theme and account removal * remove redundant call to hide account list overlay * cleanup and additional tweaks * add try/catch to remove account dialog flow Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
2022-02-23 18:40:17 +01:00
_canAccessPremium = await _stateService.CanAccessPremiumAsync();
_emailVerified = await _stateService.GetEmailVerifiedAsync();
2022-04-26 17:21:17 +02:00
SendEnabled = !await AppHelpers.IsSendDisabledByPolicyAsync();
DisableHideEmail = await AppHelpers.IsHideEmailDisabledByPolicyAsync();
SendOptionsPolicyInEffect = SendEnabled && DisableHideEmail;
}
public async Task<bool> LoadAsync()
{
if (Send == null)
{
_isOverridingPickers = true;
if (EditMode)
{
var send = await _sendService.GetAsync(SendId);
if (send == null)
{
return false;
}
Send = await send.DecryptAsync();
DeletionDate = Send.DeletionDate.ToLocalTime();
DeletionTime = DeletionDate.TimeOfDay;
ExpirationDate = Send.ExpirationDate?.ToLocalTime();
ExpirationTime = ExpirationDate?.TimeOfDay;
}
else
{
var defaultType = _canAccessPremium && _emailVerified ? SendType.File : SendType.Text;
Send = new SendView
{
Type = Type.GetValueOrDefault(defaultType),
};
_deletionDate = DateTimeNow().AddDays(7);
_deletionTime = DeletionDate.TimeOfDay;
DeletionDateTypeSelectedIndex = 4;
ExpirationDateTypeSelectedIndex = 0;
}
MaxAccessCount = Send.MaxAccessCount;
_isOverridingPickers = false;
}
DisableHideEmailControl = !SendEnabled ||
(!EditMode && DisableHideEmail) ||
(EditMode && DisableHideEmail && !Send.HideEmail);
return true;
}
public async Task ChooseFileAsync()
{
await _deviceActionService.SelectFileAsync();
}
public void ClearExpirationDate()
{
_isOverridingPickers = true;
ExpirationDate = null;
ExpirationTime = null;
_isOverridingPickers = false;
}
private void UpdateSendData()
{
// filename
if (Send.File != null && FileName != null)
{
Send.File.FileName = FileName;
}
// deletion date
if (ShowDeletionCustomPickers)
{
Send.DeletionDate = DeletionDate.Date.Add(DeletionTime).ToUniversalTime();
}
else
{
Send.DeletionDate = _simpleDeletionDateTime.ToUniversalTime();
}
// expiration date
if (ShowExpirationCustomPickers && ExpirationDate.HasValue && ExpirationTime.HasValue)
{
Send.ExpirationDate = ExpirationDate.Value.Date.Add(ExpirationTime.Value).ToUniversalTime();
}
else if (_simpleExpirationDateTime.HasValue)
{
Send.ExpirationDate = _simpleExpirationDateTime.Value.ToUniversalTime();
}
else
{
Send.ExpirationDate = null;
}
}
public async Task<bool> SubmitAsync()
{
if (Send == null || !SendEnabled)
{
return false;
}
if (Connectivity.NetworkAccess == NetworkAccess.None)
{
await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage,
AppResources.InternetConnectionRequiredTitle);
return false;
}
if (string.IsNullOrWhiteSpace(Send.Name))
{
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
string.Format(AppResources.ValidationFieldRequired, AppResources.Name),
AppResources.Ok);
return false;
}
if (IsFile)
{
if (!_canAccessPremium)
{
await _platformUtilsService.ShowDialogAsync(AppResources.SendFilePremiumRequired);
return false;
}
if (!_emailVerified)
{
await _platformUtilsService.ShowDialogAsync(AppResources.SendFileEmailVerificationRequired);
return false;
}
if (!EditMode)
{
if (FileData == null)
{
await _platformUtilsService.ShowDialogAsync(
string.Format(AppResources.ValidationFieldRequired, AppResources.File),
AppResources.AnErrorHasOccurred);
return false;
}
if (FileData.Length > 104857600) // 100 MB
{
await _platformUtilsService.ShowDialogAsync(AppResources.MaxFileSize,
AppResources.AnErrorHasOccurred);
return false;
}
}
}
UpdateSendData();
2022-04-26 17:21:17 +02:00
if (string.IsNullOrWhiteSpace(NewPassword))
{
NewPassword = null;
}
var (send, encryptedFileData) = await _sendService.EncryptAsync(Send, FileData, NewPassword);
if (send == null)
{
return false;
}
try
{
await _deviceActionService.ShowLoadingAsync(AppResources.Saving);
var sendId = await _sendService.SaveWithServerAsync(send, encryptedFileData);
await _deviceActionService.HideLoadingAsync();
if (Device.RuntimePlatform == Device.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
// it doesn't get called again when the model is dismissed, so the list isn't updated.
_messagingService.Send("sendUpdated");
}
if (!ShareOnSave)
{
_platformUtilsService.ShowToast("success", null,
EditMode ? AppResources.SendUpdated : AppResources.NewSendCreated);
}
if (!CopyInsteadOfShareAfterSaving)
{
await CloseAsync();
}
if (ShareOnSave)
{
var savedSend = await _sendService.GetAsync(sendId);
if (savedSend != null)
{
var savedSendView = await savedSend.DecryptAsync();
if (CopyInsteadOfShareAfterSaving)
{
await AppHelpers.CopySendUrlAsync(savedSendView);
// wait so that the user sees the message before the view gets dismissed
await Task.Delay(1300);
}
else
{
await AppHelpers.ShareSendUrlAsync(savedSendView);
}
}
}
if (CopyInsteadOfShareAfterSaving)
{
await CloseAsync();
}
return true;
}
catch (ApiException e)
{
await _deviceActionService.HideLoadingAsync();
if (e?.Error != null)
{
await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(),
AppResources.AnErrorHasOccurred);
}
}
catch (Exception ex)
{
await _deviceActionService.HideLoadingAsync();
_logger.Exception(ex);
await _platformUtilsService.ShowDialogAsync(AppResources.AnErrorHasOccurred);
}
return false;
}
private async Task CloseAsync()
{
if (IsAddFromShare)
{
if (Device.RuntimePlatform == Device.Android)
{
_deviceActionService.CloseMainApp();
return;
}
if (Page is SendAddEditPage sendPage && sendPage.OnClose != null)
{
sendPage.OnClose();
return;
}
}
await Page.Navigation.PopModalAsync();
}
public async Task<bool> RemovePasswordAsync()
{
return await AppHelpers.RemoveSendPasswordAsync(SendId);
}
public async Task CopyLinkAsync()
{
await AppHelpers.CopySendUrlAsync(Send);
}
public async Task ShareLinkAsync()
{
await AppHelpers.ShareSendUrlAsync(Send);
}
public async Task<bool> DeleteAsync()
{
return await AppHelpers.DeleteSendAsync(SendId);
}
public async Task TypeChangedAsync(SendType type)
{
if (!SendEnabled)
{
await _platformUtilsService.ShowDialogAsync(AppResources.SendDisabledWarning);
await CloseAsync();
return;
}
if (Send != null)
{
if (!EditMode && type == SendType.File && (!_canAccessPremium || !_emailVerified))
{
if (!_canAccessPremium)
{
await _platformUtilsService.ShowDialogAsync(AppResources.SendFilePremiumRequired);
}
else if (!_emailVerified)
{
await _platformUtilsService.ShowDialogAsync(AppResources.SendFileEmailVerificationRequired);
}
2022-04-26 17:21:17 +02:00
if (IsAddFromShare && Device.RuntimePlatform == Device.Android)
{
_deviceActionService.CloseMainApp();
return;
}
type = SendType.Text;
}
Send.Type = type;
TriggerPropertyChanged(nameof(Send), _additionalSendProperties);
}
}
2021-02-17 16:46:04 +01:00
2021-02-16 21:24:51 +01:00
public void ToggleOptions()
{
ShowOptions = !ShowOptions;
}
private void DeletionTypeChanged()
{
if (Send != null && DeletionDateTypeSelectedIndex > -1)
{
_isOverridingPickers = true;
switch (DeletionDateTypeSelectedIndex)
{
case 0:
_simpleDeletionDateTime = DateTimeNow().AddHours(1);
break;
case 1:
_simpleDeletionDateTime = DateTimeNow().AddDays(1);
break;
case 2:
_simpleDeletionDateTime = DateTimeNow().AddDays(2);
break;
case 3:
_simpleDeletionDateTime = DateTimeNow().AddDays(3);
break;
case 4:
_simpleDeletionDateTime = DateTimeNow().AddDays(7);
break;
case 5:
_simpleDeletionDateTime = DateTimeNow().AddDays(30);
break;
case 6:
// custom option, initial values already set elsewhere
break;
}
_isOverridingPickers = false;
TriggerPropertyChanged(nameof(ShowDeletionCustomPickers));
}
}
private void ExpirationTypeChanged()
{
if (Send != null && ExpirationDateTypeSelectedIndex > -1)
{
_isOverridingPickers = true;
switch (ExpirationDateTypeSelectedIndex)
{
case 0:
_simpleExpirationDateTime = null;
break;
case 1:
_simpleExpirationDateTime = DateTimeNow().AddHours(1);
break;
case 2:
_simpleExpirationDateTime = DateTimeNow().AddDays(1);
break;
case 3:
_simpleExpirationDateTime = DateTimeNow().AddDays(2);
break;
case 4:
_simpleExpirationDateTime = DateTimeNow().AddDays(3);
break;
case 5:
_simpleExpirationDateTime = DateTimeNow().AddDays(7);
break;
case 6:
_simpleExpirationDateTime = DateTimeNow().AddDays(30);
break;
case 7:
// custom option, clear all expiration values
_simpleExpirationDateTime = null;
ClearExpirationDate();
break;
}
_isOverridingPickers = false;
TriggerPropertyChanged(nameof(ShowExpirationCustomPickers));
}
}
private void ExpirationDateChanged()
{
if (!_isOverridingPickers && !ExpirationTime.HasValue)
{
// auto-set time to current time upon setting date
ExpirationTime = DateTimeNow().TimeOfDay;
}
}
private void ExpirationTimeChanged()
{
if (!_isOverridingPickers && !ExpirationDate.HasValue)
{
// auto-set date to current date upon setting time
ExpirationDate = DateTime.Today;
}
}
private void MaxAccessCountChanged()
{
Send.MaxAccessCount = _maxAccessCount;
}
private void TogglePassword()
{
ShowPassword = !ShowPassword;
}
private DateTime DateTimeNow()
{
var dtn = DateTime.Now;
return new DateTime(
dtn.Year,
dtn.Month,
dtn.Day,
dtn.Hour,
dtn.Minute,
0,
DateTimeKind.Local
);
}
}
}