mirror of
https://github.com/bitwarden/mobile
synced 2025-12-05 23:53:33 +00:00
Compare commits
1 Commits
v2022.11.0
...
bugfix/SG-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f75cc95cce |
@@ -7,12 +7,6 @@
|
||||
"commands": [
|
||||
"dotnet-format"
|
||||
]
|
||||
},
|
||||
"cake.tool": {
|
||||
"version": "2.2.0",
|
||||
"commands": [
|
||||
"dotnet-cake"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
73
.github/workflows/build.yml
vendored
73
.github/workflows/build.yml
vendored
@@ -59,10 +59,6 @@ jobs:
|
||||
name: Android
|
||||
runs-on: windows-2022
|
||||
needs: setup
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
variant: ['prod', 'qa']
|
||||
steps:
|
||||
- name: Setup NuGet
|
||||
uses: nuget/setup-nuget@b2bc17b761a1d88cab755a776c7922eb26eefbfa # v1.0.6
|
||||
@@ -71,7 +67,7 @@ jobs:
|
||||
|
||||
- name: Set up MSBuild
|
||||
uses: microsoft/setup-msbuild@ab534842b4bdf384b8aaf93765dc6f721d9f5fab
|
||||
|
||||
|
||||
- name: Work Around for broken Windows 2022 Runner Image
|
||||
run: |
|
||||
Set-Location "C:\Program Files (x86)\Microsoft Visual Studio\Installer\"
|
||||
@@ -91,6 +87,7 @@ jobs:
|
||||
Write-Host "components were not installed"
|
||||
exit 1
|
||||
}
|
||||
|
||||
- name: Print environment
|
||||
run: |
|
||||
nuget help | grep Version
|
||||
@@ -101,8 +98,7 @@ jobs:
|
||||
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Decrypt secrets
|
||||
env:
|
||||
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
|
||||
@@ -113,17 +109,12 @@ jobs:
|
||||
--output ./src/Android/app_play-keystore.jks ./.github/secrets/app_play-keystore.jks.gpg
|
||||
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||
--output ./src/Android/app_upload-keystore.jks ./.github/secrets/app_upload-keystore.jks.gpg
|
||||
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||
--output ./src/Android/google-services.json ./.github/secrets/google-services.json.gpg
|
||||
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||
--output $HOME/secrets/play_creds.json ./.github/secrets/play_creds.json.gpg
|
||||
shell: bash
|
||||
- name: Decrypt secrets - Google Services
|
||||
if: ${{ matrix.variant == 'prod' }}
|
||||
env:
|
||||
DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
|
||||
run: |
|
||||
gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
|
||||
--output ./src/Android/google-services.json ./.github/secrets/google-services.json.gpg
|
||||
shell: bash
|
||||
|
||||
- name: Increment version
|
||||
run: |
|
||||
BUILD_NUMBER=$((3000 + $GITHUB_RUN_NUMBER))
|
||||
@@ -151,35 +142,26 @@ jobs:
|
||||
run: dotnet test test/Core.Test/Core.Test.csproj
|
||||
|
||||
- name: Build Play Store publisher
|
||||
if: ${{ matrix.variant == 'prod' }}
|
||||
run: dotnet build ./store/google/Publisher/Publisher.csproj -p:Configuration=Release
|
||||
|
||||
- name: Setup Android build (${{ matrix.variant }})
|
||||
run: dotnet cake build.cake --target Android --variant ${{ matrix.variant }}
|
||||
|
||||
- name: Build Android
|
||||
- name: Build for Play Store
|
||||
run: |
|
||||
$configuration = "Release";
|
||||
|
||||
Write-Output "########################################"
|
||||
Write-Output "##### Build $configuration Configuration"
|
||||
Write-Output "########################################"
|
||||
msbuild "$($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj")" "/p:Configuration=$configuration"
|
||||
|
||||
msbuild "$($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj")" "/p:Configuration=$configuration"
|
||||
shell: pwsh
|
||||
|
||||
- name: Sign Android Build
|
||||
- name: Sign for Play Store
|
||||
env:
|
||||
PLAY_KEYSTORE_PASSWORD: ${{ secrets.PLAY_KEYSTORE_PASSWORD }}
|
||||
UPLOAD_KEYSTORE_PASSWORD: ${{ secrets.UPLOAD_KEYSTORE_PASSWORD }}
|
||||
run: |
|
||||
$androidPath = $($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj");
|
||||
$packageName = "com.x8bit.bitwarden";
|
||||
|
||||
if ("${{ matrix.variant }}" -ne "prod")
|
||||
{
|
||||
$packageName = "com.x8bit.bitwarden.${{ matrix.variant }}";
|
||||
}
|
||||
|
||||
Write-Output "########################################"
|
||||
Write-Output "##### Sign Google Play Bundle Release Configuration"
|
||||
Write-Output "########################################"
|
||||
@@ -193,8 +175,9 @@ jobs:
|
||||
Write-Output "##### Copy Google Play Bundle to project root"
|
||||
Write-Output "########################################"
|
||||
|
||||
$signedAabPath = $($env:GITHUB_WORKSPACE + "/src/Android/bin/Release/$($packageName)-Signed.aab");
|
||||
$signedAabDestPath = $($env:GITHUB_WORKSPACE + "/$($packageName).aab");
|
||||
$signedAabPath = $($env:GITHUB_WORKSPACE + "/src/Android/bin/Release/com.x8bit.bitwarden-Signed.aab");
|
||||
$signedAabDestPath = $($env:GITHUB_WORKSPACE + "/com.x8bit.bitwarden.aab");
|
||||
|
||||
Copy-Item $signedAabPath $signedAabDestPath
|
||||
|
||||
Write-Output "########################################"
|
||||
@@ -210,41 +193,33 @@ jobs:
|
||||
Write-Output "##### Copy Release APK to project root"
|
||||
Write-Output "########################################"
|
||||
|
||||
$signedApkPath = $($env:GITHUB_WORKSPACE + "/src/Android/bin/Release/$($packageName)-Signed.apk");
|
||||
$signedApkDestPath = $($env:GITHUB_WORKSPACE + "/$($packageName).apk");
|
||||
$signedApkPath = $($env:GITHUB_WORKSPACE + "/src/Android/bin/Release/com.x8bit.bitwarden-Signed.apk");
|
||||
$signedApkDestPath = $($env:GITHUB_WORKSPACE + "/com.x8bit.bitwarden.apk");
|
||||
|
||||
Copy-Item $signedApkPath $signedApkDestPath
|
||||
shell: pwsh
|
||||
- name: Upload Prod .aab artifact
|
||||
if: ${{ matrix.variant == 'prod' }}
|
||||
|
||||
- name: Upload Play Store .aab artifact
|
||||
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
|
||||
with:
|
||||
name: com.x8bit.bitwarden.aab
|
||||
path: ./com.x8bit.bitwarden.aab
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload Prod .apk artifact
|
||||
if: ${{ matrix.variant == 'prod' }}
|
||||
- name: Upload Play Store .apk artifact
|
||||
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
|
||||
with:
|
||||
name: com.x8bit.bitwarden.apk
|
||||
path: ./com.x8bit.bitwarden.apk
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Upload Other .apk artifact
|
||||
if: ${{ matrix.variant != 'prod' }}
|
||||
uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535
|
||||
with:
|
||||
name: com.x8bit.bitwarden.${{ matrix.variant }}.apk
|
||||
path: ./com.x8bit.bitwarden.${{ matrix.variant }}.apk
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Deploy to Play Store
|
||||
if: ${{ matrix.variant == 'prod' && (( github.ref == 'refs/heads/master'
|
||||
&& needs.setup.outputs.rc_branch_exists == 0
|
||||
&& needs.setup.outputs.hotfix_branch_exists == 0)
|
||||
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|
||||
|| github.ref == 'refs/heads/hotfix-rc' ) }}
|
||||
if: |
|
||||
(github.ref == 'refs/heads/master'
|
||||
&& needs.setup.outputs.rc_branch_exists == 0
|
||||
&& needs.setup.outputs.hotfix_branch_exists == 0)
|
||||
|| (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
|
||||
|| github.ref == 'refs/heads/hotfix-rc'
|
||||
run: |
|
||||
PUBLISHER_PATH="$GITHUB_WORKSPACE/store/google/Publisher/bin/Release/netcoreapp3.1/Publisher.dll"
|
||||
CREDS_PATH="$HOME/secrets/play_creds.json"
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -208,5 +208,4 @@ FakesAssemblies/
|
||||
# Other
|
||||
project.lock.json
|
||||
.DS_Store
|
||||
src/App/Css
|
||||
tools
|
||||
src/App/Css
|
||||
345
build.cake
345
build.cake
@@ -1,345 +0,0 @@
|
||||
#addin nuget:?package=Cake.FileHelpers&version=5.0.0
|
||||
#addin nuget:?package=Cake.AndroidAppManifest&version=1.1.2
|
||||
#addin nuget:?package=Cake.Plist&version=0.7.0
|
||||
#addin nuget:?package=Cake.Incubator&version=7.0.0
|
||||
#tool dotnet:?package=GitVersion.Tool&version=5.10.3
|
||||
using Path = System.IO.Path;
|
||||
|
||||
var debugScript = Argument<bool>("debugScript", false);
|
||||
var target = Argument("target", "Default");
|
||||
var configuration = Argument("configuration", "Release");
|
||||
var variant = Argument("variant", "dev");
|
||||
|
||||
abstract record VariantConfig(
|
||||
string AppName,
|
||||
string AndroidPackageName,
|
||||
string iOSBundleId,
|
||||
string ApsEnvironment
|
||||
);
|
||||
|
||||
const string BASE_BUNDLE_ID_DROID = "com.x8bit.bitwarden";
|
||||
const string BASE_BUNDLE_ID_IOS = "com.8bit.bitwarden";
|
||||
|
||||
record Dev(): VariantConfig("Bitwarden Dev", $"{BASE_BUNDLE_ID_DROID}.dev", $"{BASE_BUNDLE_ID_IOS}.dev", "development");
|
||||
record QA(): VariantConfig("Bitwarden QA", $"{BASE_BUNDLE_ID_DROID}.qa", $"{BASE_BUNDLE_ID_IOS}.qa", "development");
|
||||
record Beta(): VariantConfig("Bitwarden Beta", $"{BASE_BUNDLE_ID_DROID}.beta", $"{BASE_BUNDLE_ID_IOS}.beta", "production");
|
||||
record Prod(): VariantConfig("Bitwarden", $"{BASE_BUNDLE_ID_DROID}", $"{BASE_BUNDLE_ID_IOS}", "production");
|
||||
|
||||
VariantConfig GetVariant() => variant.ToLower() switch{
|
||||
"qa" => new QA(),
|
||||
"beta" => new Beta(),
|
||||
"prod" => new Prod(),
|
||||
_ => new Dev()
|
||||
};
|
||||
|
||||
GitVersion _gitVersion; //will be set by GetGitInfo task
|
||||
var _slnPath = Path.Combine(""); //base path used to access files. If build.cake file is moved, just update this
|
||||
string _androidPackageName = string.Empty; //will be set by UpdateAndroidManifest task
|
||||
string CreateFeatureBranch(string prevVersionName, GitVersion git) => $"{prevVersionName}-{git.BranchName.Replace("/","-")}";
|
||||
string GetVersionName(string prevVersionName, VariantConfig buildVariant, GitVersion git) => buildVariant is Prod? prevVersionName : CreateFeatureBranch(prevVersionName, git);
|
||||
int CreateBuildNumber(int previousNumber) => ++previousNumber;
|
||||
|
||||
Task("GetGitInfo")
|
||||
.Does(()=> {
|
||||
_gitVersion = GitVersion(new GitVersionSettings());
|
||||
|
||||
if(debugScript)
|
||||
{
|
||||
Information($"GitVersion Dump:\n{_gitVersion.Dump()}");
|
||||
}
|
||||
|
||||
Information("Git data Load successfully.");
|
||||
});
|
||||
|
||||
#region Android
|
||||
Task("UpdateAndroidAppIcon")
|
||||
.Does(()=>{
|
||||
//TODO we'll implement variant icons later
|
||||
//manifest.ApplicationIcon = "@mipmap/ic_launcher";
|
||||
Information($"Updated Androix App Icon with success");
|
||||
});
|
||||
|
||||
|
||||
Task("UpdateAndroidManifest")
|
||||
.IsDependentOn("GetGitInfo")
|
||||
.Does(()=>
|
||||
{
|
||||
var buildVariant = GetVariant();
|
||||
var manifestPath = Path.Combine(_slnPath, "src", "Android", "Properties", "AndroidManifest.xml");
|
||||
|
||||
// Cake.AndroidAppManifest doesn't currently enable us to access nested items so, quick (not ideal) fix:
|
||||
var manifestText = FileReadText(manifestPath);
|
||||
manifestText = manifestText.Replace("com.x8bit.bitwarden.", buildVariant.AndroidPackageName + ".");
|
||||
manifestText = manifestText.Replace("android:label=\"Bitwarden\"", $"android:label=\"{buildVariant.AppName}\"");
|
||||
FileWriteText(manifestPath, manifestText);
|
||||
|
||||
var manifest = DeserializeAppManifest(manifestPath);
|
||||
|
||||
var prevVersionCode = manifest.VersionCode;
|
||||
var prevVersionName = manifest.VersionName;
|
||||
_androidPackageName = manifest.PackageName;
|
||||
|
||||
//manifest.VersionCode = CreateBuildNumber(prevVersionCode);
|
||||
manifest.VersionName = GetVersionName(prevVersionName, buildVariant, _gitVersion);
|
||||
manifest.PackageName = buildVariant.AndroidPackageName;
|
||||
manifest.ApplicationLabel = buildVariant.AppName;
|
||||
|
||||
//Information($"AndroidManigest.xml VersionCode from {prevVersionCode} to {manifest.VersionCode}");
|
||||
Information($"AndroidManigest.xml VersionName from {prevVersionName} to {manifest.VersionName}");
|
||||
Information($"AndroidManigest.xml PackageName from {_androidPackageName} to {buildVariant.AndroidPackageName}");
|
||||
Information($"AndroidManigest.xml ApplicationLabel to {buildVariant.AppName}");
|
||||
|
||||
SerializeAppManifest(manifestPath, manifest);
|
||||
|
||||
Information("AndroidManifest updated with success!");
|
||||
});
|
||||
|
||||
void ReplaceInFile(string filePath, string oldtext, string newtext)
|
||||
{
|
||||
var fileText = FileReadText(filePath);
|
||||
|
||||
if(string.IsNullOrEmpty(fileText) || !fileText.Contains(oldtext))
|
||||
{
|
||||
throw new Exception($"Couldn't find {filePath} or it didn't contain: {oldtext}");
|
||||
}
|
||||
|
||||
fileText = fileText.Replace(oldtext, newtext);
|
||||
|
||||
FileWriteText(filePath, fileText);
|
||||
Information($"{filePath} modified successfully.");
|
||||
}
|
||||
|
||||
Task("UpdateAndroidCodeFiles")
|
||||
.IsDependentOn("UpdateAndroidManifest")
|
||||
.Does(()=> {
|
||||
var buildVariant = GetVariant();
|
||||
|
||||
//We're not using _androidPackageName here because the codefile is currently slightly different string than the one in AndroidManifest.xml
|
||||
var keyName = "com.8bit.bitwarden";
|
||||
var fixedPackageName = buildVariant.AndroidPackageName.Replace("x8bit", "8bit");
|
||||
var filePath = Path.Combine(_slnPath, "src", "Android", "Services", "BiometricService.cs");
|
||||
ReplaceInFile(filePath, keyName, fixedPackageName);
|
||||
|
||||
var packageFileList = new string[] {
|
||||
Path.Combine(_slnPath, "src", "Android", "MainActivity.cs"),
|
||||
Path.Combine(_slnPath, "src", "Android", "MainApplication.cs"),
|
||||
Path.Combine(_slnPath, "src", "Android", "Constants.cs"),
|
||||
Path.Combine(_slnPath, "src", "Android", "Accessibility", "AccessibilityService.cs"),
|
||||
Path.Combine(_slnPath, "src", "Android", "Autofill", "AutofillHelpers.cs"),
|
||||
Path.Combine(_slnPath, "src", "Android", "Autofill", "AutofillService.cs"),
|
||||
Path.Combine(_slnPath, "src", "Android", "Receivers", "ClearClipboardAlarmReceiver.cs"),
|
||||
Path.Combine(_slnPath, "src", "Android", "Receivers", "EventUploadReceiver.cs"),
|
||||
Path.Combine(_slnPath, "src", "Android", "Receivers", "PackageReplacedReceiver.cs"),
|
||||
Path.Combine(_slnPath, "src", "Android", "Receivers", "RestrictionsChangedReceiver.cs"),
|
||||
Path.Combine(_slnPath, "src", "Android", "Services", "DeviceActionService.cs"),
|
||||
Path.Combine(_slnPath, "src", "Android", "Tiles", "AutofillTileService.cs"),
|
||||
Path.Combine(_slnPath, "src", "Android", "Tiles", "GeneratorTileService.cs"),
|
||||
Path.Combine(_slnPath, "src", "Android", "Tiles", "MyVaultTileService.cs"),
|
||||
Path.Combine(_slnPath, "src", "Android", "google-services.json"),
|
||||
Path.Combine(_slnPath, "store", "google", "Publisher", "Program.cs"),
|
||||
};
|
||||
|
||||
foreach(string path in packageFileList)
|
||||
{
|
||||
ReplaceInFile(path, "com.x8bit.bitwarden", buildVariant.AndroidPackageName);
|
||||
}
|
||||
|
||||
var labelFileList = new string[] {
|
||||
Path.Combine(_slnPath, "src", "Android", "Autofill", "AutofillService.cs"),
|
||||
};
|
||||
|
||||
foreach(string path in labelFileList)
|
||||
{
|
||||
ReplaceInFile(path, "Bitwarden\"", $"{buildVariant.AppName}\"");
|
||||
}
|
||||
});
|
||||
#endregion Android
|
||||
|
||||
#region iOS
|
||||
enum iOSProjectType
|
||||
{
|
||||
Null,
|
||||
MainApp,
|
||||
Autofill,
|
||||
Extension,
|
||||
ShareExtension
|
||||
}
|
||||
|
||||
string GetiOSBundleId(VariantConfig buildVariant, iOSProjectType projectType) => projectType switch
|
||||
{
|
||||
iOSProjectType.Autofill => $"{buildVariant.iOSBundleId}.autofill",
|
||||
iOSProjectType.Extension => $"{buildVariant.iOSBundleId}.find-login-action-extension",
|
||||
iOSProjectType.ShareExtension => $"{buildVariant.iOSBundleId}.share-extension",
|
||||
_ => buildVariant.iOSBundleId
|
||||
};
|
||||
|
||||
string GetiOSBundleName(VariantConfig buildVariant, iOSProjectType projectType) => projectType switch
|
||||
{
|
||||
iOSProjectType.Autofill => $"{buildVariant.AppName} Autofill",
|
||||
iOSProjectType.Extension => $"{buildVariant.AppName} Extension",
|
||||
iOSProjectType.ShareExtension => $"{buildVariant.AppName} Share Extension",
|
||||
_ => buildVariant.AppName
|
||||
};
|
||||
|
||||
private void UpdateiOSInfoPlist(string plistPath, VariantConfig buildVariant, GitVersion git, iOSProjectType projectType = iOSProjectType.MainApp)
|
||||
{
|
||||
var plistFile = File(plistPath);
|
||||
dynamic plist = DeserializePlist(plistFile);
|
||||
|
||||
var prevVersionName = plist["CFBundleShortVersionString"];
|
||||
var prevVersionString = plist["CFBundleVersion"];
|
||||
var prevVersion = int.Parse(plist["CFBundleVersion"]);
|
||||
var prevBundleId = plist["CFBundleIdentifier"];
|
||||
var prevBundleName = plist["CFBundleName"];
|
||||
//var newVersion = CreateBuildNumber(prevVersion).ToString();
|
||||
var newVersionName = GetVersionName(prevVersionName, buildVariant, git);
|
||||
var newBundleId = GetiOSBundleId(buildVariant, projectType);
|
||||
var newBundleName = GetiOSBundleName(buildVariant, projectType);
|
||||
|
||||
plist["CFBundleName"] = newBundleName;
|
||||
plist["CFBundleDisplayName"] = newBundleName;
|
||||
//plist["CFBundleVersion"] = newVersion;
|
||||
plist["CFBundleShortVersionString"] = newVersionName;
|
||||
plist["CFBundleIdentifier"] = newBundleId;
|
||||
|
||||
if(projectType == iOSProjectType.MainApp)
|
||||
{
|
||||
plist["CFBundleURLTypes"][0]["CFBundleURLName"] = $"{buildVariant.iOSBundleId}.url";
|
||||
}
|
||||
|
||||
if(projectType == iOSProjectType.Extension)
|
||||
{
|
||||
var keyText = plist["NSExtension"]["NSExtensionAttributes"]["NSExtensionActivationRule"];
|
||||
plist["NSExtension"]["NSExtensionAttributes"]["NSExtensionActivationRule"] = keyText.Replace("com.8bit.bitwarden", buildVariant.iOSBundleId);
|
||||
}
|
||||
|
||||
SerializePlist(plistFile, plist);
|
||||
|
||||
Information($"Changed app name from {prevBundleName} to {newBundleName}");
|
||||
//Information($"Changed Bundle Version from {prevVersion} to {newVersion}");
|
||||
Information($"Changed Bundle Short Version name from {prevVersionName} to {newVersionName}");
|
||||
Information($"Changed Bundle Identifier from {prevBundleId} to {newBundleId}");
|
||||
Information($"{plistPath} updated with success!");
|
||||
}
|
||||
|
||||
private void UpdateiOSEntitlementsPlist(string entitlementsPath, VariantConfig buildVariant)
|
||||
{
|
||||
var EntitlementlistFile = File(entitlementsPath);
|
||||
dynamic Entitlements = DeserializePlist(EntitlementlistFile);
|
||||
|
||||
Entitlements["aps-environment"] = buildVariant.ApsEnvironment;
|
||||
Entitlements["keychain-access-groups"] = new List<string>() { "$(AppIdentifierPrefix)" + buildVariant.iOSBundleId };
|
||||
Entitlements["com.apple.security.application-groups"] = new List<string>() { $"group.{buildVariant.iOSBundleId}" };;
|
||||
|
||||
Information($"Changed ApsEnvironment name to {buildVariant.ApsEnvironment}");
|
||||
Information($"Changed keychain-access-groups bundleID to {buildVariant.iOSBundleId}");
|
||||
|
||||
SerializePlist(EntitlementlistFile, Entitlements);
|
||||
|
||||
Information($"{entitlementsPath} updated with success!");
|
||||
}
|
||||
|
||||
Task("UpdateiOSIcon")
|
||||
.Does(()=>{
|
||||
//TODO we'll implement variant icons later
|
||||
Information($"Updating IOS App Icon");
|
||||
});
|
||||
|
||||
Task("UpdateiOSPlist")
|
||||
.IsDependentOn("GetGitInfo")
|
||||
.Does(()=> {
|
||||
var buildVariant = GetVariant();
|
||||
var infoPath = Path.Combine(_slnPath, "src", "iOS", "Info.plist");
|
||||
var entitlementsPath = Path.Combine(_slnPath, "src", "iOS", "Entitlements.plist");
|
||||
UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.MainApp);
|
||||
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant);
|
||||
});
|
||||
|
||||
Task("UpdateiOSAutofillPlist")
|
||||
.IsDependentOn("GetGitInfo")
|
||||
.IsDependentOn("UpdateiOSPlist")
|
||||
.Does(()=> {
|
||||
var buildVariant = GetVariant();
|
||||
var infoPath = Path.Combine(_slnPath, "src", "iOS.Autofill", "Info.plist");
|
||||
var entitlementsPath = Path.Combine(_slnPath, "src", "iOS.Autofill", "Entitlements.plist");
|
||||
UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.Autofill);
|
||||
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant);
|
||||
});
|
||||
|
||||
Task("UpdateiOSExtensionPlist")
|
||||
.IsDependentOn("GetGitInfo")
|
||||
.IsDependentOn("UpdateiOSPlist")
|
||||
.Does(()=> {
|
||||
var buildVariant = GetVariant();
|
||||
var infoPath = Path.Combine(_slnPath, "src", "iOS.Extension", "Info.plist");
|
||||
var entitlementsPath = Path.Combine(_slnPath, "src", "iOS.Extension", "Entitlements.plist");
|
||||
UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.Extension);
|
||||
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant);
|
||||
});
|
||||
|
||||
Task("UpdateiOSShareExtensionPlist")
|
||||
.IsDependentOn("GetGitInfo")
|
||||
.IsDependentOn("UpdateiOSPlist")
|
||||
.Does(()=> {
|
||||
var buildVariant = GetVariant();
|
||||
var infoPath = Path.Combine(_slnPath, "src", "iOS.ShareExtension", "Info.plist");
|
||||
var entitlementsPath = Path.Combine(_slnPath, "src", "iOS.ShareExtension", "Entitlements.plist");
|
||||
UpdateiOSInfoPlist(infoPath, buildVariant, _gitVersion, iOSProjectType.ShareExtension);
|
||||
UpdateiOSEntitlementsPlist(entitlementsPath, buildVariant);
|
||||
});
|
||||
|
||||
Task("UpdateiOSCodeFiles")
|
||||
.IsDependentOn("UpdateiOSPlist")
|
||||
.Does(()=> {
|
||||
var buildVariant = GetVariant();
|
||||
var fileList = new string[] {
|
||||
Path.Combine(_slnPath, "src", "iOS.Core", "Utilities", "iOSCoreHelpers.cs"),
|
||||
Path.Combine(_slnPath, "src", "iOS.Core", "Constants.cs"),
|
||||
Path.Combine(".github", "resources", "export-options-ad-hoc.plist"),
|
||||
Path.Combine(".github", "resources", "export-options-app-store.plist"),
|
||||
};
|
||||
|
||||
foreach(string path in fileList)
|
||||
{
|
||||
ReplaceInFile(path, "com.8bit.bitwarden", buildVariant.iOSBundleId);
|
||||
}
|
||||
});
|
||||
#endregion iOS
|
||||
|
||||
#region Main Tasks
|
||||
Task("Android")
|
||||
//.IsDependentOn("UpdateAndroidAppIcon")
|
||||
.IsDependentOn("UpdateAndroidManifest")
|
||||
.IsDependentOn("UpdateAndroidCodeFiles")
|
||||
.Does(()=>
|
||||
{
|
||||
Information("Android app updated");
|
||||
});
|
||||
|
||||
Task("iOS")
|
||||
//.IsDependentOn("UpdateiOSIcon")
|
||||
.IsDependentOn("UpdateiOSPlist")
|
||||
.IsDependentOn("UpdateiOSAutofillPlist")
|
||||
.IsDependentOn("UpdateiOSExtensionPlist")
|
||||
.IsDependentOn("UpdateiOSShareExtensionPlist")
|
||||
.IsDependentOn("UpdateiOSCodeFiles")
|
||||
.Does(()=>
|
||||
{
|
||||
Information("iOS app updated");
|
||||
});
|
||||
|
||||
Task("Default")
|
||||
.Does(() => {
|
||||
var usage = @"Missing target.
|
||||
|
||||
Usage:
|
||||
dotnet cake build.cake --target (Android | iOS) --variant (dev | qa | beta | prod)
|
||||
|
||||
Options:
|
||||
--debugScript=<bool> Script debug mode.
|
||||
";
|
||||
Information(usage);
|
||||
});
|
||||
#endregion Main Tasks
|
||||
|
||||
RunTarget(target);
|
||||
@@ -156,7 +156,6 @@
|
||||
<Compile Include="Services\FileService.cs" />
|
||||
<Compile Include="Services\AutofillHandler.cs" />
|
||||
<Compile Include="Constants.cs" />
|
||||
<Compile Include="Effects\RemoveFontPaddingEffect.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidAsset Include="Assets\bwi-font.ttf" />
|
||||
|
||||
Binary file not shown.
@@ -1,23 +0,0 @@
|
||||
using Android.Widget;
|
||||
using Bit.Droid.Effects;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Platform.Android;
|
||||
|
||||
[assembly: ExportEffect(typeof(RemoveFontPaddingEffect), nameof(RemoveFontPaddingEffect))]
|
||||
namespace Bit.Droid.Effects
|
||||
{
|
||||
public class RemoveFontPaddingEffect : PlatformEffect
|
||||
{
|
||||
protected override void OnAttached()
|
||||
{
|
||||
if (Control is TextView textView)
|
||||
{
|
||||
textView.SetIncludeFontPadding(false);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDetached()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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="2022.11.0" 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="2022.10.1" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="32" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.NFC" />
|
||||
|
||||
@@ -9,6 +9,5 @@ namespace Bit.App.Abstractions
|
||||
void Init(Func<AppOptions> getOptionsFunc, IAccountsManagerHost accountsManagerHost);
|
||||
Task NavigateOnAccountChangeAsync(bool? isAuthed = null);
|
||||
Task LogOutAsync(string userId, bool userInitiated, bool expired);
|
||||
Task PromptToSwitchToExistingAccountAsync(string userId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,7 +139,6 @@
|
||||
<Folder Include="Controls\AccountSwitchingOverlay\" />
|
||||
<Folder Include="Utilities\AccountManagement\" />
|
||||
<Folder Include="Controls\DateTime\" />
|
||||
<Folder Include="Controls\IconLabelButton\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -431,6 +430,5 @@
|
||||
<None Remove="Controls\AccountSwitchingOverlay\" />
|
||||
<None Remove="Utilities\AccountManagement\" />
|
||||
<None Remove="Controls\DateTime\" />
|
||||
<None Remove="Controls\IconLabelButton\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Models;
|
||||
@@ -34,9 +33,8 @@ namespace Bit.App
|
||||
private readonly IAccountsManager _accountsManager;
|
||||
private readonly IPushNotificationService _pushNotificationService;
|
||||
private static bool _isResumed;
|
||||
// these variables are static because the app is launching new activities on notification click, creating new instances of App.
|
||||
// this variable is static because the app is launching new activities on notification click, creating new instances of App.
|
||||
private static bool _pendingCheckPasswordlessLoginRequests;
|
||||
private static object _processingLoginRequestLock = new object();
|
||||
|
||||
public App(AppOptions appOptions)
|
||||
{
|
||||
@@ -145,15 +143,9 @@ namespace Bit.App
|
||||
new NavigationPage(new RemoveMasterPasswordPage()));
|
||||
});
|
||||
}
|
||||
else if (message.Command == Constants.PasswordlessLoginRequestKey
|
||||
|| message.Command == "unlocked"
|
||||
|| message.Command == AccountsManagerMessageCommands.ACCOUNT_SWITCH_COMPLETED)
|
||||
else if (message.Command == "passwordlessLoginRequest" || message.Command == "unlocked" || message.Command == AccountsManagerMessageCommands.ACCOUNT_SWITCH_COMPLETED)
|
||||
{
|
||||
lock (_processingLoginRequestLock)
|
||||
{
|
||||
// lock doesn't allow for async execution
|
||||
CheckPasswordlessLoginRequestsAsync().Wait();
|
||||
}
|
||||
CheckPasswordlessLoginRequestsAsync().FireAndForget();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -170,6 +162,7 @@ namespace Bit.App
|
||||
_pendingCheckPasswordlessLoginRequests = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_pendingCheckPasswordlessLoginRequests = false;
|
||||
if (await _vaultTimeoutService.IsLockedAsync())
|
||||
{
|
||||
@@ -189,11 +182,6 @@ namespace Bit.App
|
||||
|
||||
// Delay to wait for the vault page to appear
|
||||
await Task.Delay(2000);
|
||||
// if there is a request modal opened ignore all incoming requests
|
||||
if (App.Current.MainPage.Navigation.ModalStack.Any(p => p is NavigationPage navPage && navPage.CurrentPage is LoginPasswordlessPage))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var loginRequestData = await _authService.GetPasswordlessLoginRequestByIdAsync(notification.Id);
|
||||
var page = new LoginPasswordlessPage(new LoginPasswordlessDetails()
|
||||
{
|
||||
@@ -208,7 +196,7 @@ namespace Bit.App
|
||||
});
|
||||
await _stateService.SetPasswordlessLoginNotificationAsync(null);
|
||||
_pushNotificationService.DismissLocalNotification(Constants.PasswordlessNotificationId);
|
||||
if (!loginRequestData.IsExpired)
|
||||
if (loginRequestData.CreationDate.ToUniversalTime().AddMinutes(Constants.PasswordlessNotificationTimeoutInMinutes) > DateTime.UtcNow)
|
||||
{
|
||||
await Device.InvokeOnMainThreadAsync(() => Application.Current.MainPage.Navigation.PushModalAsync(new NavigationPage(page)));
|
||||
}
|
||||
@@ -223,20 +211,13 @@ namespace Bit.App
|
||||
}
|
||||
|
||||
var notificationUserEmail = await _stateService.GetEmailAsync(notification.UserId);
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
await Device.InvokeOnMainThreadAsync(async () =>
|
||||
{
|
||||
try
|
||||
var result = await _deviceActionService.DisplayAlertAsync(AppResources.LogInRequested, string.Format(AppResources.LoginAttemptFromXDoYouWantToSwitchToThisAccount, notificationUserEmail), AppResources.Cancel, AppResources.Ok);
|
||||
if (result == AppResources.Ok)
|
||||
{
|
||||
var result = await _deviceActionService.DisplayAlertAsync(AppResources.LogInRequested, string.Format(AppResources.LoginAttemptFromXDoYouWantToSwitchToThisAccount, notificationUserEmail), AppResources.Cancel, AppResources.Ok);
|
||||
if (result == AppResources.Ok)
|
||||
{
|
||||
await _stateService.SetActiveUserAsync(notification.UserId);
|
||||
_messagingService.Send(AccountsManagerMessageCommands.SWITCHED_ACCOUNT);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
await _stateService.SetActiveUserAsync(notification.UserId);
|
||||
_messagingService.Send(AccountsManagerMessageCommands.SWITCHED_ACCOUNT);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
@@ -261,7 +242,7 @@ namespace Bit.App
|
||||
}
|
||||
if (_pendingCheckPasswordlessLoginRequests)
|
||||
{
|
||||
_messagingService.Send(Constants.PasswordlessLoginRequestKey);
|
||||
CheckPasswordlessLoginRequestsAsync().FireAndForget();
|
||||
}
|
||||
if (Device.RuntimePlatform == Device.Android)
|
||||
{
|
||||
@@ -297,7 +278,7 @@ namespace Bit.App
|
||||
_isResumed = true;
|
||||
if (_pendingCheckPasswordlessLoginRequests)
|
||||
{
|
||||
_messagingService.Send(Constants.PasswordlessLoginRequestKey);
|
||||
CheckPasswordlessLoginRequestsAsync().FireAndForget();
|
||||
}
|
||||
if (Device.RuntimePlatform == Device.Android)
|
||||
{
|
||||
@@ -459,14 +440,7 @@ namespace Bit.App
|
||||
switch (navTarget)
|
||||
{
|
||||
case NavigationTarget.HomeLogin:
|
||||
if (navParams is HomeNavigationParams homeParams)
|
||||
{
|
||||
Current.MainPage = new NavigationPage(new HomePage(Options, homeParams.ShouldCheckRememberEmail));
|
||||
}
|
||||
else
|
||||
{
|
||||
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||
}
|
||||
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||
break;
|
||||
case NavigationTarget.Login:
|
||||
if (navParams is LoginNavigationParams loginParams)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Bit.App.Effects;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Controls
|
||||
{
|
||||
@@ -17,8 +16,6 @@ namespace Bit.App.Controls
|
||||
FontFamily = "bwi-font.ttf#bwi-font";
|
||||
break;
|
||||
}
|
||||
|
||||
Effects.Add(new RemoveFontPaddingEffect());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Bit.App.Effects;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Controls
|
||||
{
|
||||
@@ -18,8 +17,6 @@ namespace Bit.App.Controls
|
||||
FontFamily = "bwi-font.ttf#bwi-font";
|
||||
break;
|
||||
}
|
||||
|
||||
Effects.Add(new RemoveFontPaddingEffect());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Frame xmlns="http://xamarin.com/schemas/2014/forms"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="Bit.App.Controls.IconLabelButton"
|
||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||
x:Name="_iconLabelButton"
|
||||
HeightRequest="45"
|
||||
Padding="1"
|
||||
StyleClass="btn-icon-secondary"
|
||||
BackgroundColor="{Binding IconLabelBorderColor, Source={x:Reference _iconLabelButton}}"
|
||||
HasShadow="False">
|
||||
<Frame.GestureRecognizers>
|
||||
<TapGestureRecognizer Command="{Binding ButtonCommand, Source={x:Reference _iconLabelButton}}" />
|
||||
</Frame.GestureRecognizers>
|
||||
<Frame
|
||||
Margin="0"
|
||||
Padding="0"
|
||||
CornerRadius="{Binding CornerRadius, Source={x:Reference _iconLabelButton}}"
|
||||
BackgroundColor="{Binding IconLabelBackgroundColor, Source={x:Reference _iconLabelButton}}"
|
||||
IsClippedToBounds="True"
|
||||
HasShadow="False">
|
||||
<StackLayout
|
||||
Orientation="Horizontal"
|
||||
HorizontalOptions="Center">
|
||||
<controls:IconLabel
|
||||
VerticalOptions="Center"
|
||||
HorizontalTextAlignment="Center"
|
||||
FontSize="Large"
|
||||
TextColor="{Binding IconLabelColor, Source={x:Reference _iconLabelButton}}"
|
||||
Text="{Binding Icon, Source={x:Reference _iconLabelButton}}">
|
||||
</controls:IconLabel>
|
||||
<Label
|
||||
VerticalOptions="Center"
|
||||
HorizontalTextAlignment="Center"
|
||||
TextColor="{Binding IconLabelColor, Source={x:Reference _iconLabelButton}}"
|
||||
FontSize="Medium"
|
||||
Text="{Binding Label, Source={x:Reference _iconLabelButton}}"/>
|
||||
</StackLayout>
|
||||
</Frame>
|
||||
</Frame>
|
||||
@@ -1,75 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Xamarin.Forms;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace Bit.App.Controls
|
||||
{
|
||||
public partial class IconLabelButton : Frame
|
||||
{
|
||||
public static readonly BindableProperty IconProperty = BindableProperty.Create(
|
||||
nameof(Icon), typeof(string), typeof(IconLabelButton));
|
||||
|
||||
public static readonly BindableProperty LabelProperty = BindableProperty.Create(
|
||||
nameof(Label), typeof(string), typeof(IconLabelButton));
|
||||
|
||||
public static readonly BindableProperty ButtonCommandProperty = BindableProperty.Create(
|
||||
nameof(ButtonCommand), typeof(Command), typeof(IconLabelButton));
|
||||
|
||||
public static readonly BindableProperty IconLabelColorProperty = BindableProperty.Create(
|
||||
nameof(IconLabelColor), typeof(Color), typeof(IconLabelButton), Color.White);
|
||||
|
||||
public static readonly BindableProperty IconLabelBackgroundColorProperty = BindableProperty.Create(
|
||||
nameof(IconLabelBackgroundColor), typeof(Color), typeof(IconLabelButton), Color.White);
|
||||
|
||||
public static readonly BindableProperty IconLabelBorderColorProperty = BindableProperty.Create(
|
||||
nameof(IconLabelBorderColor), typeof(Color), typeof(IconLabelButton), Color.White);
|
||||
|
||||
public IconLabelButton()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public string Icon
|
||||
{
|
||||
get => GetValue(IconProperty) as string;
|
||||
set => SetValue(IconProperty, value);
|
||||
}
|
||||
|
||||
public string Label
|
||||
{
|
||||
get => GetValue(LabelProperty) as string;
|
||||
set => SetValue(LabelProperty, value);
|
||||
}
|
||||
|
||||
public ICommand ButtonCommand
|
||||
{
|
||||
get => GetValue(ButtonCommandProperty) as ICommand;
|
||||
set => SetValue(ButtonCommandProperty, value);
|
||||
}
|
||||
|
||||
public Color IconLabelColor
|
||||
{
|
||||
get { return (Color)GetValue(IconLabelColorProperty); }
|
||||
set { SetValue(IconLabelColorProperty, value); }
|
||||
}
|
||||
|
||||
public Color IconLabelBackgroundColor
|
||||
{
|
||||
get { return (Color)GetValue(IconLabelBackgroundColorProperty); }
|
||||
set { SetValue(IconLabelBackgroundColorProperty, value); }
|
||||
}
|
||||
|
||||
public Color IconLabelBorderColor
|
||||
{
|
||||
get { return (Color)GetValue(IconLabelBorderColorProperty); }
|
||||
set { SetValue(IconLabelBorderColorProperty, value); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
using System;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Effects
|
||||
{
|
||||
public class RemoveFontPaddingEffect : RoutingEffect
|
||||
{
|
||||
public RemoveFontPaddingEffect()
|
||||
: base("Bitwarden.RemoveFontPaddingEffect")
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,11 @@ namespace Bit.App.Pages
|
||||
{
|
||||
private HintPageViewModel _vm;
|
||||
|
||||
public HintPage(string email = null)
|
||||
public HintPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
_vm = BindingContext as HintPageViewModel;
|
||||
_vm.Page = this;
|
||||
_vm.Email = email;
|
||||
if (Device.RuntimePlatform == Device.Android)
|
||||
{
|
||||
ToolbarItems.RemoveAt(0);
|
||||
|
||||
@@ -15,7 +15,6 @@ namespace Bit.App.Pages
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IApiService _apiService;
|
||||
private readonly ILogger _logger;
|
||||
private string _email;
|
||||
|
||||
public HintPageViewModel()
|
||||
{
|
||||
@@ -35,12 +34,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
|
||||
public ICommand SubmitCommand { get; }
|
||||
|
||||
public string Email
|
||||
{
|
||||
get => _email;
|
||||
set => SetProperty(ref _email, value);
|
||||
}
|
||||
public string Email { get; set; }
|
||||
|
||||
public async Task SubmitAsync()
|
||||
{
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
UseOriginalImage="True"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n Account}" />
|
||||
<ToolbarItem x:Name="_closeButton" Text="{u:I18n Close}" Command="{Binding CloseCommand}" Order="Primary" Priority="-1"/>
|
||||
<ToolbarItem
|
||||
Icon="cog_environment.png" Clicked="Environment_Clicked" Order="Primary"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
@@ -33,66 +32,30 @@
|
||||
|
||||
<ContentPage.Resources>
|
||||
<ResourceDictionary>
|
||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||
<StackLayout x:Name="_mainLayout" x:Key="mainLayout" Spacing="30" Padding="20, 50, 20, 0">
|
||||
<Image
|
||||
x:Name="_logo"
|
||||
Source="logo.png"
|
||||
VerticalOptions="Center" />
|
||||
<Label Text="{u:I18n LoginOrCreateNewAccount}"
|
||||
StyleClass="text-lg"
|
||||
HorizontalTextAlignment="Center"/>
|
||||
<StackLayout
|
||||
StyleClass="box-row">
|
||||
<Label
|
||||
Text="{u:I18n EmailAddress}"
|
||||
StyleClass="box-label" />
|
||||
<Entry
|
||||
x:Name="_email"
|
||||
Text="{Binding Email}"
|
||||
Keyboard="Email"
|
||||
StyleClass="box-value">
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Property="TextColor" Value="{DynamicResource MutedColor}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Entry>
|
||||
<StackLayout
|
||||
Orientation="Horizontal"
|
||||
Margin="0, 16, 0 ,0">
|
||||
<StackLayout.GestureRecognizers>
|
||||
<TapGestureRecognizer
|
||||
Command="{Binding RememberEmailCommand}" />
|
||||
</StackLayout.GestureRecognizers>
|
||||
<Label
|
||||
Text="{u:I18n RememberMe}"
|
||||
StyleClass="text-sm"
|
||||
HorizontalOptions="FillAndExpand"
|
||||
VerticalOptions="Center"
|
||||
VerticalTextAlignment="Center"/>
|
||||
<Switch
|
||||
Scale="0.8"
|
||||
IsToggled="{Binding RememberEmail}"
|
||||
VerticalOptions="Center"/>
|
||||
<StackLayout x:Name="_mainLayout" x:Key="mainLayout" Spacing="0" Padding="10, 5">
|
||||
<StackLayout VerticalOptions="CenterAndExpand" Spacing="20">
|
||||
<Image
|
||||
x:Name="_logo"
|
||||
Source="logo.png"
|
||||
VerticalOptions="Center" />
|
||||
<Label Text="{u:I18n LoginOrCreateNewAccount}"
|
||||
StyleClass="text-lg"
|
||||
HorizontalTextAlignment="Center">
|
||||
</Label>
|
||||
<StackLayout Spacing="5">
|
||||
<Button Text="{u:I18n LogIn}"
|
||||
StyleClass="btn-primary"
|
||||
Clicked="LogIn_Clicked" />
|
||||
<Button Text="{u:I18n CreateAccount}"
|
||||
Clicked="Register_Clicked" />
|
||||
<Button Text="{u:I18n LogInSso}"
|
||||
Clicked="LogInSso_Clicked" />
|
||||
<Button Text="{u:I18n Cancel}"
|
||||
IsVisible="{Binding ShowCancelButton}"
|
||||
Margin="0,10,0,0"
|
||||
Clicked="Cancel_Clicked" />
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
<Button Text="{u:I18n Continue}"
|
||||
StyleClass="btn-primary"
|
||||
IsEnabled="{Binding CanContinue}"
|
||||
Command="{Binding ContinueCommand}" />
|
||||
|
||||
<Label FormattedText="{Binding CreateAccountText}"
|
||||
Margin="0, 10"
|
||||
StyleClass="box-footer-label">
|
||||
<Label.GestureRecognizers>
|
||||
<TapGestureRecognizer Command="{Binding CreateAccountCommand}" />
|
||||
</Label.GestureRecognizers>
|
||||
</Label>
|
||||
</StackLayout>
|
||||
</ResourceDictionary>
|
||||
</ContentPage.Resources>
|
||||
|
||||
@@ -10,35 +10,28 @@ namespace Bit.App.Pages
|
||||
{
|
||||
public partial class HomePage : BaseContentPage
|
||||
{
|
||||
private bool _checkRememberedEmail;
|
||||
private readonly HomeViewModel _vm;
|
||||
private readonly AppOptions _appOptions;
|
||||
private IBroadcasterService _broadcasterService;
|
||||
|
||||
public HomePage(AppOptions appOptions = null, bool shouldCheckRememberEmail = true)
|
||||
public HomePage(AppOptions appOptions = null)
|
||||
{
|
||||
_broadcasterService = ServiceContainer.Resolve<IBroadcasterService>("broadcasterService");
|
||||
_appOptions = appOptions;
|
||||
InitializeComponent();
|
||||
_vm = BindingContext as HomeViewModel;
|
||||
_vm.Page = this;
|
||||
_vm.ShouldCheckRememberEmail = shouldCheckRememberEmail;
|
||||
_vm.ShowCancelButton = _appOptions?.IosExtension ?? false;
|
||||
_vm.StartLoginAction = async () => await StartLoginAsync();
|
||||
_vm.StartLoginAction = () => Device.BeginInvokeOnMainThread(async () => await StartLoginAsync());
|
||||
_vm.StartRegisterAction = () => Device.BeginInvokeOnMainThread(async () => await StartRegisterAsync());
|
||||
_vm.StartSsoLoginAction = () => Device.BeginInvokeOnMainThread(async () => await StartSsoLoginAsync());
|
||||
_vm.StartEnvironmentAction = () => Device.BeginInvokeOnMainThread(async () => await StartEnvironmentAsync());
|
||||
_vm.CloseAction = async () =>
|
||||
{
|
||||
await _accountListOverlay.HideAsync();
|
||||
await Navigation.PopModalAsync();
|
||||
};
|
||||
UpdateLogo();
|
||||
|
||||
if (!_vm.ShowCancelButton)
|
||||
if (_appOptions?.IosExtension ?? false)
|
||||
{
|
||||
ToolbarItems.Remove(_closeButton);
|
||||
_vm.ShowCancelButton = true;
|
||||
}
|
||||
|
||||
if (_appOptions?.HideAccountSwitcher ?? false)
|
||||
{
|
||||
ToolbarItems.Remove(_accountAvatar);
|
||||
@@ -59,7 +52,7 @@ namespace Bit.App.Pages
|
||||
|
||||
if (!_appOptions?.HideAccountSwitcher ?? false)
|
||||
{
|
||||
_vm.AvatarImageSource = await GetAvatarImageSourceAsync(false);
|
||||
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
|
||||
}
|
||||
_broadcasterService.Subscribe(nameof(HomePage), (message) =>
|
||||
{
|
||||
@@ -71,8 +64,6 @@ namespace Bit.App.Pages
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
_vm.CheckNavigateLoginStep();
|
||||
}
|
||||
|
||||
protected override bool OnBackButtonPressed()
|
||||
@@ -105,12 +96,28 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
|
||||
private void LogIn_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
if (DoOnce())
|
||||
{
|
||||
_vm.StartLoginAction();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task StartLoginAsync()
|
||||
{
|
||||
var page = new LoginPage(_vm.Email, _appOptions);
|
||||
var page = new LoginPage(null, _appOptions);
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
|
||||
private void Register_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
if (DoOnce())
|
||||
{
|
||||
_vm.StartRegisterAction();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task StartRegisterAsync()
|
||||
{
|
||||
var page = new RegisterPage(this);
|
||||
|
||||
@@ -1,15 +1,8 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Controls;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.CommunityToolkit.ObjectModel;
|
||||
using Xamarin.Essentials;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
{
|
||||
@@ -19,37 +12,19 @@ namespace Bit.App.Pages
|
||||
private readonly IMessagingService _messagingService;
|
||||
|
||||
private bool _showCancelButton;
|
||||
private bool _rememberEmail;
|
||||
private string _email;
|
||||
private bool _isEmailEnabled;
|
||||
private bool _canLogin;
|
||||
private IPlatformUtilsService _platformUtilsService;
|
||||
private ILogger _logger;
|
||||
private IEnvironmentService _environmentService;
|
||||
private IAccountsManager _accountManager;
|
||||
|
||||
public HomeViewModel()
|
||||
{
|
||||
_stateService = ServiceContainer.Resolve<IStateService>();
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>();
|
||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>();
|
||||
_logger = ServiceContainer.Resolve<ILogger>();
|
||||
_environmentService = ServiceContainer.Resolve<IEnvironmentService>();
|
||||
_accountManager = ServiceContainer.Resolve<IAccountsManager>();
|
||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
var logger = ServiceContainer.Resolve<ILogger>("logger");
|
||||
|
||||
PageTitle = AppResources.Bitwarden;
|
||||
|
||||
AccountSwitchingOverlayViewModel = new AccountSwitchingOverlayViewModel(_stateService, _messagingService, _logger)
|
||||
AccountSwitchingOverlayViewModel = new AccountSwitchingOverlayViewModel(_stateService, _messagingService, logger)
|
||||
{
|
||||
AllowActiveAccountSelection = true
|
||||
};
|
||||
RememberEmailCommand = new Command(() => RememberEmail = !RememberEmail);
|
||||
ContinueCommand = new AsyncCommand(ContinueToLoginStepAsync, allowsMultipleExecutions: false);
|
||||
CreateAccountCommand = new AsyncCommand(async () => await Device.InvokeOnMainThreadAsync(StartRegisterAction),
|
||||
onException: _logger.Exception, allowsMultipleExecutions: false);
|
||||
CloseCommand = new AsyncCommand(async () => await Device.InvokeOnMainThreadAsync(CloseAction),
|
||||
onException: _logger.Exception, allowsMultipleExecutions: false);
|
||||
InitAsync().FireAndForget();
|
||||
}
|
||||
|
||||
public bool ShowCancelButton
|
||||
@@ -58,102 +33,11 @@ namespace Bit.App.Pages
|
||||
set => SetProperty(ref _showCancelButton, value);
|
||||
}
|
||||
|
||||
public bool RememberEmail
|
||||
{
|
||||
get => _rememberEmail;
|
||||
set => SetProperty(ref _rememberEmail, value);
|
||||
}
|
||||
|
||||
public string Email
|
||||
{
|
||||
get => _email;
|
||||
set => SetProperty(ref _email, value,
|
||||
additionalPropertyNames: new[] { nameof(CanContinue) });
|
||||
}
|
||||
|
||||
public bool CanContinue => !string.IsNullOrEmpty(Email);
|
||||
|
||||
public bool ShouldCheckRememberEmail { get; set; }
|
||||
|
||||
public FormattedString CreateAccountText
|
||||
{
|
||||
get
|
||||
{
|
||||
var fs = new FormattedString();
|
||||
fs.Spans.Add(new Span
|
||||
{
|
||||
Text = $"{AppResources.NewAroundHere} "
|
||||
});
|
||||
fs.Spans.Add(new Span
|
||||
{
|
||||
Text = AppResources.CreateAccount,
|
||||
TextColor = ThemeManager.GetResourceColor("PrimaryColor")
|
||||
});
|
||||
return fs;
|
||||
}
|
||||
}
|
||||
|
||||
public AccountSwitchingOverlayViewModel AccountSwitchingOverlayViewModel { get; }
|
||||
public Action StartLoginAction { get; set; }
|
||||
public Action StartRegisterAction { get; set; }
|
||||
public Action StartSsoLoginAction { get; set; }
|
||||
public Action StartEnvironmentAction { get; set; }
|
||||
public Action CloseAction { get; set; }
|
||||
public Command RememberEmailCommand { get; set; }
|
||||
public AsyncCommand ContinueCommand { get; }
|
||||
public AsyncCommand CloseCommand { get; }
|
||||
public AsyncCommand CreateAccountCommand { get; }
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
Email = await _stateService.GetRememberedEmailAsync();
|
||||
RememberEmail = !string.IsNullOrEmpty(Email);
|
||||
}
|
||||
|
||||
public void CheckNavigateLoginStep()
|
||||
{
|
||||
if (ShouldCheckRememberEmail && RememberEmail)
|
||||
{
|
||||
StartLoginAction();
|
||||
}
|
||||
ShouldCheckRememberEmail = false;
|
||||
}
|
||||
|
||||
public async Task ContinueToLoginStepAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Email))
|
||||
{
|
||||
await _platformUtilsService.ShowDialogAsync(
|
||||
string.Format(AppResources.ValidationFieldRequired, AppResources.EmailAddress),
|
||||
AppResources.AnErrorHasOccurred, AppResources.Ok);
|
||||
return;
|
||||
}
|
||||
if (!Email.Contains("@"))
|
||||
{
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.InvalidEmail, AppResources.AnErrorHasOccurred,
|
||||
AppResources.Ok);
|
||||
return;
|
||||
}
|
||||
await _stateService.SetRememberedEmailAsync(RememberEmail ? Email : null);
|
||||
var userId = await _stateService.GetUserIdAsync(Email);
|
||||
if (!string.IsNullOrWhiteSpace(userId))
|
||||
{
|
||||
var userEnvUrls = await _stateService.GetEnvironmentUrlsAsync(userId);
|
||||
if (userEnvUrls?.Base == _environmentService.BaseUrl)
|
||||
{
|
||||
await _accountManager.PromptToSwitchToExistingAccountAsync(userId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
StartLoginAction();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Exception(ex);
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.GenericErrorMessage, AppResources.AnErrorHasOccurred, AppResources.Ok);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="Bit.App.Pages.LoginPage"
|
||||
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||
xmlns:core="clr-namespace:Bit.Core;assembly=BitwardenCore"
|
||||
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||
x:DataType="pages:LoginPageViewModel"
|
||||
@@ -47,13 +46,33 @@
|
||||
Order="Secondary" />
|
||||
|
||||
<ScrollView x:Name="_mainLayout" x:Key="mainLayout">
|
||||
<StackLayout Spacing="0">
|
||||
<StackLayout Spacing="20">
|
||||
<StackLayout StyleClass="box">
|
||||
<StackLayout StyleClass="box-row">
|
||||
<Label
|
||||
Text="{u:I18n EmailAddress}"
|
||||
StyleClass="box-label" />
|
||||
<Entry
|
||||
x:Name="_email"
|
||||
Text="{Binding Email}"
|
||||
IsEnabled="{Binding IsEmailEnabled}"
|
||||
Keyboard="Email"
|
||||
StyleClass="box-value">
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Property="TextColor" Value="{DynamicResource MutedColor}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Entry>
|
||||
</StackLayout>
|
||||
<Grid StyleClass="box-row">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
@@ -62,7 +81,6 @@
|
||||
<Label
|
||||
Text="{u:I18n MasterPassword}"
|
||||
StyleClass="box-label"
|
||||
Padding="0, 10, 0, 0"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0" />
|
||||
<controls:MonoEntry
|
||||
@@ -80,60 +98,21 @@
|
||||
StyleClass="box-row-button, box-row-button-platform"
|
||||
Text="{Binding ShowPasswordIcon}"
|
||||
Command="{Binding TogglePasswordCommand}"
|
||||
Grid.Row="1"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Grid.RowSpan="1"
|
||||
Grid.RowSpan="2"
|
||||
AutomationProperties.IsInAccessibleTree="True"
|
||||
AutomationProperties.Name="{u:I18n ToggleVisibility}"
|
||||
AutomationProperties.HelpText="{Binding PasswordVisibilityAccessibilityText}"/>
|
||||
<Label
|
||||
Text="{u:I18n GetMasterPasswordwordHint}"
|
||||
StyleClass="box-footer-label"
|
||||
TextColor="{DynamicResource HyperlinkColor}"
|
||||
Padding="0,5,0,0"
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="2">
|
||||
<Label.GestureRecognizers>
|
||||
<TapGestureRecognizer Tapped="Hint_Clicked" />
|
||||
</Label.GestureRecognizers>
|
||||
</Label>
|
||||
</Grid>
|
||||
</StackLayout>
|
||||
<StackLayout Padding="10, 10">
|
||||
<Button x:Name="_loginWithMasterPassword"
|
||||
Text="{u:I18n LogInWithMasterPassword}"
|
||||
<StackLayout Padding="10, 0">
|
||||
<Button Text="{u:I18n LogIn}"
|
||||
StyleClass="btn-primary"
|
||||
Clicked="LogIn_Clicked" />
|
||||
<controls:IconLabelButton
|
||||
HorizontalOptions="Fill"
|
||||
VerticalOptions="CenterAndExpand"
|
||||
Icon="{Binding Source={x:Static core:BitwardenIcons.Device}}"
|
||||
Label="{u:I18n LogInWithAnotherDevice}"
|
||||
ButtonCommand="{Binding LogInCommand}"
|
||||
IsVisible="False"/>
|
||||
<controls:IconLabelButton
|
||||
HorizontalOptions="Fill"
|
||||
VerticalOptions="CenterAndExpand"
|
||||
Icon="{Binding Source={x:Static core:BitwardenIcons.Suitcase}}"
|
||||
Label="{u:I18n LogInSso}">
|
||||
<controls:IconLabelButton.GestureRecognizers>
|
||||
<TapGestureRecognizer Tapped="LogInSSO_Clicked" />
|
||||
</controls:IconLabelButton.GestureRecognizers>
|
||||
</controls:IconLabelButton>
|
||||
<Label
|
||||
Text="{Binding LoggingInAsText}"
|
||||
StyleClass="text-sm"
|
||||
Margin="0,40,0,0"/>
|
||||
<Label
|
||||
Text="{u:I18n NotYou}"
|
||||
StyleClass="text-md"
|
||||
HorizontalOptions="Start"
|
||||
TextColor="{DynamicResource HyperlinkColor}">
|
||||
<Label.GestureRecognizers>
|
||||
<TapGestureRecognizer Tapped="Cancel_Clicked" />
|
||||
</Label.GestureRecognizers>
|
||||
</Label>
|
||||
<Button Text="{u:I18n Cancel}"
|
||||
IsVisible="{Binding ShowCancelButton}"
|
||||
Clicked="Cancel_Clicked" />
|
||||
</StackLayout>
|
||||
</StackLayout>
|
||||
</ScrollView>
|
||||
|
||||
@@ -3,9 +3,7 @@ using System.Threading.Tasks;
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.CommunityToolkit.ObjectModel;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Pages
|
||||
@@ -27,7 +25,6 @@ namespace Bit.App.Pages
|
||||
_vm.Page = this;
|
||||
_vm.StartTwoFactorAction = () => Device.BeginInvokeOnMainThread(async () => await StartTwoFactorAsync());
|
||||
_vm.LogInSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await LogInSuccessAsync());
|
||||
_vm.StartSsoLoginAction = () => Device.BeginInvokeOnMainThread(async () => await StartSsoLoginAsync());
|
||||
_vm.UpdateTempPasswordAction =
|
||||
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
||||
_vm.CloseAction = async () =>
|
||||
@@ -45,6 +42,9 @@ namespace Bit.App.Pages
|
||||
_vm.Email = email;
|
||||
MasterPasswordEntry = _masterPassword;
|
||||
|
||||
_email.ReturnType = ReturnType.Next;
|
||||
_email.ReturnCommand = new Command(() => _masterPassword.Focus());
|
||||
|
||||
if (Device.RuntimePlatform == Device.iOS)
|
||||
{
|
||||
ToolbarItems.Add(_moreItem);
|
||||
@@ -54,6 +54,11 @@ namespace Bit.App.Pages
|
||||
ToolbarItems.Add(_getPasswordHint);
|
||||
}
|
||||
|
||||
if (Device.RuntimePlatform == Device.Android && !_vm.IsEmailEnabled)
|
||||
{
|
||||
ToolbarItems.Add(_removeAccount);
|
||||
}
|
||||
|
||||
if (_appOptions?.IosExtension ?? false)
|
||||
{
|
||||
_vm.ShowCancelButton = true;
|
||||
@@ -73,20 +78,16 @@ namespace Bit.App.Pages
|
||||
_mainContent.Content = _mainLayout;
|
||||
_accountAvatar?.OnAppearing();
|
||||
|
||||
await _vm.InitAsync();
|
||||
if (!_appOptions?.HideAccountSwitcher ?? false)
|
||||
{
|
||||
_vm.AvatarImageSource = await GetAvatarImageSourceAsync(_vm.EmailIsInSavedAccounts);
|
||||
_vm.AvatarImageSource = await GetAvatarImageSourceAsync();
|
||||
}
|
||||
await _vm.InitAsync();
|
||||
if (!_inputFocused)
|
||||
{
|
||||
RequestFocus(_masterPassword);
|
||||
RequestFocus(string.IsNullOrWhiteSpace(_vm.Email) ? _email : _masterPassword);
|
||||
_inputFocused = true;
|
||||
}
|
||||
if (Device.RuntimePlatform == Device.Android && !_vm.CanRemoveAccount)
|
||||
{
|
||||
ToolbarItems.Add(_removeAccount);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool OnBackButtonPressed()
|
||||
@@ -114,25 +115,11 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
|
||||
private void LogInSSO_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
if (DoOnce())
|
||||
{
|
||||
_vm.StartSsoLoginAction();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task StartSsoLoginAsync()
|
||||
{
|
||||
var page = new LoginSsoPage(_appOptions);
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
|
||||
private void Hint_Clicked(object sender, EventArgs e)
|
||||
{
|
||||
if (DoOnce())
|
||||
{
|
||||
_vm.ShowMasterPasswordHintAsync().FireAndForget();
|
||||
Navigation.PushModalAsync(new NavigationPage(new HintPage()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Input;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Controls;
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Resources;
|
||||
using Bit.App.Utilities;
|
||||
using Bit.App.Utilities.AccountManagement;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.View;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.CommunityToolkit.ObjectModel;
|
||||
using Xamarin.Forms;
|
||||
@@ -30,15 +25,12 @@ namespace Bit.App.Pages
|
||||
private readonly II18nService _i18nService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IApiService _apiService;
|
||||
private readonly IAppIdService _appIdService;
|
||||
private readonly IAccountsManager _accountManager;
|
||||
|
||||
private bool _showPassword;
|
||||
private bool _showCancelButton;
|
||||
private string _email;
|
||||
private string _masterPassword;
|
||||
private bool _isEmailEnabled;
|
||||
private bool _isKnownDevice;
|
||||
|
||||
public LoginPageViewModel()
|
||||
{
|
||||
@@ -51,9 +43,6 @@ namespace Bit.App.Pages
|
||||
_i18nService = ServiceContainer.Resolve<II18nService>("i18nService");
|
||||
_messagingService = ServiceContainer.Resolve<IMessagingService>("messagingService");
|
||||
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
||||
_apiService = ServiceContainer.Resolve<IApiService>();
|
||||
_appIdService = ServiceContainer.Resolve<IAppIdService>();
|
||||
_accountManager = ServiceContainer.Resolve<IAccountsManager>();
|
||||
|
||||
PageTitle = AppResources.Bitwarden;
|
||||
TogglePasswordCommand = new Command(TogglePassword);
|
||||
@@ -87,11 +76,7 @@ namespace Bit.App.Pages
|
||||
public string Email
|
||||
{
|
||||
get => _email;
|
||||
set => SetProperty(ref _email, value,
|
||||
additionalPropertyNames: new string[]
|
||||
{
|
||||
nameof(LoggingInAsText)
|
||||
});
|
||||
set => SetProperty(ref _email, value);
|
||||
}
|
||||
|
||||
public string MasterPassword
|
||||
@@ -106,29 +91,20 @@ namespace Bit.App.Pages
|
||||
set => SetProperty(ref _isEmailEnabled, value);
|
||||
}
|
||||
|
||||
public bool IsKnownDevice
|
||||
{
|
||||
get => _isKnownDevice;
|
||||
set => SetProperty(ref _isKnownDevice, value);
|
||||
}
|
||||
public bool IsIosExtension { get; set; }
|
||||
|
||||
public AccountSwitchingOverlayViewModel AccountSwitchingOverlayViewModel { get; }
|
||||
|
||||
public Command LogInCommand { get; }
|
||||
public Command TogglePasswordCommand { get; }
|
||||
public ICommand MoreCommand { get; internal set; }
|
||||
public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye;
|
||||
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
|
||||
public string LoggingInAsText => string.Format(AppResources.LoggingInAsX, Email);
|
||||
public bool IsIosExtension { get; set; }
|
||||
public bool CanRemoveAccount { get; set; }
|
||||
public Action StartTwoFactorAction { get; set; }
|
||||
public Action LogInSuccessAction { get; set; }
|
||||
public Action UpdateTempPasswordAction { get; set; }
|
||||
public Action StartSsoLoginAction { get; set; }
|
||||
public Action CloseAction { get; set; }
|
||||
|
||||
public bool EmailIsInSavedAccounts => _stateService.AccountViews != null && _stateService.AccountViews.Any(e => e.Email == Email);
|
||||
|
||||
protected override II18nService i18nService => _i18nService;
|
||||
protected override IEnvironmentService environmentService => _environmentService;
|
||||
protected override IDeviceActionService deviceActionService => _deviceActionService;
|
||||
@@ -136,22 +112,9 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task InitAsync()
|
||||
{
|
||||
try
|
||||
if (string.IsNullOrWhiteSpace(Email))
|
||||
{
|
||||
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
|
||||
await AccountSwitchingOverlayViewModel.RefreshAccountViewsAsync();
|
||||
if (string.IsNullOrWhiteSpace(Email))
|
||||
{
|
||||
Email = await _stateService.GetRememberedEmailAsync();
|
||||
}
|
||||
var deviceIdentifier = await _appIdService.GetAppIdAsync();
|
||||
IsKnownDevice = await _apiService.GetKnownDeviceAsync(Email, deviceIdentifier);
|
||||
CanRemoveAccount = await _stateService.GetActiveUserEmailAsync() != Email;
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HandleException(ex);
|
||||
Email = await _stateService.GetRememberedEmailAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +158,7 @@ namespace Bit.App.Pages
|
||||
var userEnvUrls = await _stateService.GetEnvironmentUrlsAsync(userId);
|
||||
if (userEnvUrls?.Base == _environmentService.BaseUrl)
|
||||
{
|
||||
await _accountManager.PromptToSwitchToExistingAccountAsync(userId);
|
||||
await PromptToSwitchToExistingAccountAsync(userId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -207,6 +170,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
|
||||
var response = await _authService.LogInAsync(Email, MasterPassword, _captchaToken);
|
||||
await _stateService.SetRememberedEmailAsync(Email);
|
||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||
|
||||
if (response.CaptchaNeeded)
|
||||
@@ -252,14 +216,19 @@ namespace Bit.App.Pages
|
||||
|
||||
private async Task MoreAsync()
|
||||
{
|
||||
var buttons = IsEmailEnabled || CanRemoveAccount
|
||||
var buttons = IsEmailEnabled
|
||||
? new[] { AppResources.GetPasswordHint }
|
||||
: new[] { AppResources.GetPasswordHint, AppResources.RemoveAccount };
|
||||
var selection = await _deviceActionService.DisplayActionSheetAsync(AppResources.Options, AppResources.Cancel, null, buttons);
|
||||
|
||||
if (selection == AppResources.GetPasswordHint)
|
||||
{
|
||||
await ShowMasterPasswordHintAsync();
|
||||
var hintNavigationPage = new NavigationPage(new HintPage());
|
||||
if (IsIosExtension)
|
||||
{
|
||||
ThemeManager.ApplyResourcesTo(hintNavigationPage);
|
||||
}
|
||||
await Page.Navigation.PushModalAsync(hintNavigationPage);
|
||||
}
|
||||
else if (selection == AppResources.RemoveAccount)
|
||||
{
|
||||
@@ -267,16 +236,6 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ShowMasterPasswordHintAsync()
|
||||
{
|
||||
var hintNavigationPage = new NavigationPage(new HintPage(Email));
|
||||
if (IsIosExtension)
|
||||
{
|
||||
ThemeManager.ApplyResourcesTo(hintNavigationPage);
|
||||
}
|
||||
await Page.Navigation.PushModalAsync(hintNavigationPage);
|
||||
}
|
||||
|
||||
public void TogglePassword()
|
||||
{
|
||||
ShowPassword = !ShowPassword;
|
||||
@@ -302,14 +261,16 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleException(Exception ex)
|
||||
private async Task PromptToSwitchToExistingAccountAsync(string userId)
|
||||
{
|
||||
Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(async () =>
|
||||
var switchToAccount = await _platformUtilsService.ShowDialogAsync(
|
||||
AppResources.SwitchToAlreadyAddedAccountConfirmation,
|
||||
AppResources.AccountAlreadyAdded, AppResources.Yes, AppResources.Cancel);
|
||||
if (switchToAccount)
|
||||
{
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.GenericErrorMessage);
|
||||
}).FireAndForget();
|
||||
_logger.Exception(ex);
|
||||
await _stateService.SetActiveUserAsync(userId);
|
||||
_messagingService.Send("switchedAccount");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace Bit.App.Pages
|
||||
private async Task UpdateRequestTime()
|
||||
{
|
||||
TriggerPropertyChanged(nameof(TimeOfRequestText));
|
||||
if (LoginRequest?.IsExpired ?? false)
|
||||
if (DateTime.UtcNow > LoginRequest?.RequestDate.ToUniversalTime().AddMinutes(Constants.PasswordlessNotificationTimeoutInMinutes))
|
||||
{
|
||||
StopRequestTimeUpdater();
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.LoginRequestHasAlreadyExpired);
|
||||
@@ -110,7 +110,7 @@ namespace Bit.App.Pages
|
||||
|
||||
private async Task PasswordlessLoginAsync(bool approveRequest)
|
||||
{
|
||||
if (LoginRequest.IsExpired)
|
||||
if (LoginRequest.RequestDate.ToUniversalTime().AddMinutes(Constants.PasswordlessNotificationTimeoutInMinutes) <= DateTime.UtcNow)
|
||||
{
|
||||
await _platformUtilsService.ShowDialogAsync(AppResources.LoginRequestHasAlreadyExpired);
|
||||
await Page.Navigation.PopModalAsync();
|
||||
@@ -171,7 +171,5 @@ namespace Bit.App.Pages
|
||||
public string DeviceType { get; set; }
|
||||
|
||||
public string IpAddress { get; set; }
|
||||
|
||||
public bool IsExpired => RequestDate.ToUniversalTime().AddMinutes(Constants.PasswordlessNotificationTimeoutInMinutes) < DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,6 @@ namespace Bit.App.Pages
|
||||
try
|
||||
{
|
||||
await _deviceActionService.ShowLoadingAsync(AppResources.Syncing);
|
||||
await _syncService.SyncPasswordlessLoginRequestsAsync();
|
||||
var success = await _syncService.FullSyncAsync(true);
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
if (success)
|
||||
|
||||
@@ -189,7 +189,6 @@ namespace Bit.App.Pages
|
||||
if (await _stateService.GetSyncOnRefreshAsync() && Refreshing && !SyncRefreshing)
|
||||
{
|
||||
SyncRefreshing = true;
|
||||
await _syncService.SyncPasswordlessLoginRequestsAsync();
|
||||
await _syncService.FullSyncAsync(false);
|
||||
return;
|
||||
}
|
||||
|
||||
61
src/App/Resources/AppResources.Designer.cs
generated
61
src/App/Resources/AppResources.Designer.cs
generated
@@ -2893,15 +2893,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Get master password hint.
|
||||
/// </summary>
|
||||
public static string GetMasterPasswordwordHint {
|
||||
get {
|
||||
return ResourceManager.GetString("GetMasterPasswordwordHint", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Get your master password hint.
|
||||
/// </summary>
|
||||
@@ -3415,15 +3406,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Logging in as {0}.
|
||||
/// </summary>
|
||||
public static string LoggingInAsX {
|
||||
get {
|
||||
return ResourceManager.GetString("LoggingInAsX", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Log In.
|
||||
/// </summary>
|
||||
@@ -3452,11 +3434,10 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Login attempt from:
|
||||
///{0}
|
||||
///Do you want to switch to this account?.
|
||||
/// Looks up a localized string similar to Login attempt from {0}. Do you want to switch to this account?.
|
||||
/// </summary>
|
||||
public static string LoginAttemptFromXDoYouWantToSwitchToThisAccount {
|
||||
public static string LoginAttemptFromXDoYouWantToSwitchToThisAccount
|
||||
{
|
||||
get {
|
||||
return ResourceManager.GetString("LoginAttemptFromXDoYouWantToSwitchToThisAccount", resourceCulture);
|
||||
}
|
||||
@@ -3561,24 +3542,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Log In with another device.
|
||||
/// </summary>
|
||||
public static string LogInWithAnotherDevice {
|
||||
get {
|
||||
return ResourceManager.GetString("LogInWithAnotherDevice", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Log In with master password.
|
||||
/// </summary>
|
||||
public static string LogInWithMasterPassword {
|
||||
get {
|
||||
return ResourceManager.GetString("LogInWithMasterPassword", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Log out.
|
||||
/// </summary>
|
||||
@@ -3984,15 +3947,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to New around here?.
|
||||
/// </summary>
|
||||
public static string NewAroundHere {
|
||||
get {
|
||||
return ResourceManager.GetString("NewAroundHere", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to New custom field.
|
||||
/// </summary>
|
||||
@@ -4227,15 +4181,6 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Not you?.
|
||||
/// </summary>
|
||||
public static string NotYou {
|
||||
get {
|
||||
return ResourceManager.GetString("NotYou", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to This login does not have a username or password configured..
|
||||
/// </summary>
|
||||
|
||||
@@ -1762,7 +1762,7 @@ Scanning will happen automatically.</value>
|
||||
<value>Syncing vault with pull down gesture.</value>
|
||||
</data>
|
||||
<data name="LogInSso" xml:space="preserve">
|
||||
<value>Enterprise single sign-on</value>
|
||||
<value>Enterprise Single Sign-On</value>
|
||||
</data>
|
||||
<data name="LogInSsoSummary" xml:space="preserve">
|
||||
<value>Quickly log in using your organization's single sign-on portal. Please enter your organization's identifier to begin.</value>
|
||||
@@ -2473,22 +2473,4 @@ select Add TOTP to store the key safely</value>
|
||||
{0}
|
||||
Do you want to switch to this account?</value>
|
||||
</data>
|
||||
<data name="NewAroundHere" xml:space="preserve">
|
||||
<value>New around here?</value>
|
||||
</data>
|
||||
<data name="GetMasterPasswordwordHint" xml:space="preserve">
|
||||
<value>Get master password hint</value>
|
||||
</data>
|
||||
<data name="LoggingInAsX" xml:space="preserve">
|
||||
<value>Logging in as {0}</value>
|
||||
</data>
|
||||
<data name="NotYou" xml:space="preserve">
|
||||
<value>Not you?</value>
|
||||
</data>
|
||||
<data name="LogInWithMasterPassword" xml:space="preserve">
|
||||
<value>Log in with master password</value>
|
||||
</data>
|
||||
<data name="LogInWithAnotherDevice" xml:space="preserve">
|
||||
<value>Log In with another device</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -157,7 +157,7 @@ namespace Bit.App.Services
|
||||
};
|
||||
|
||||
_pushNotificationService.Value.SendLocalNotification(AppResources.LogInRequested, String.Format(AppResources.ConfimLogInAttempForX, userEmail), notificationData);
|
||||
_messagingService.Value.Send(Constants.PasswordlessLoginRequestKey, passwordlessLoginMessage);
|
||||
_messagingService.Value.Send("passwordlessLoginRequest", passwordlessLoginMessage);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -228,9 +228,7 @@ namespace Bit.App.Services
|
||||
if (data is PasswordlessNotificationData passwordlessNotificationData)
|
||||
{
|
||||
var notificationUserId = await _stateService.Value.GetUserIdAsync(passwordlessNotificationData.UserEmail);
|
||||
var activeUserEmail = await _stateService.Value.GetActiveUserEmailAsync();
|
||||
var notificationSaved = await _stateService.Value.GetPasswordlessLoginNotificationAsync();
|
||||
if (activeUserEmail != passwordlessNotificationData.UserEmail && notificationUserId != null && notificationSaved != null)
|
||||
if (notificationUserId != null)
|
||||
{
|
||||
await _stateService.Value.SetActiveUserAsync(notificationUserId);
|
||||
_messagingService.Value.Send(AccountsManagerMessageCommands.SWITCHED_ACCOUNT);
|
||||
|
||||
@@ -80,7 +80,6 @@
|
||||
Value="{DynamicResource StepperForegroundColor}" />
|
||||
</Style>
|
||||
<Style TargetType="Frame"
|
||||
ApplyToDerivedTypes="True"
|
||||
Class="btn-icon-row">
|
||||
<Setter Property="BackgroundColor"
|
||||
Value="{DynamicResource ButtonBackgroundColor}" />
|
||||
|
||||
@@ -130,36 +130,7 @@
|
||||
<Setter Property="BackgroundColor"
|
||||
Value="{DynamicResource FabColor}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="controls:IconLabelButton"
|
||||
ApplyToDerivedTypes="True"
|
||||
Class="btn-icon-secondary">
|
||||
<Setter Property="IconLabelColor"
|
||||
Value="{DynamicResource ButtonTextColorOpacity}" />
|
||||
<Setter Property="IconLabelBorderColor"
|
||||
Value="{DynamicResource ButtonBorderColor}" />
|
||||
<Setter Property="IconLabelBackgroundColor"
|
||||
Value="{DynamicResource BackgroundColor}" />
|
||||
<Setter Property="CornerRadius"
|
||||
Value="5" />
|
||||
<Setter Property="VisualStateManager.VisualStateGroups">
|
||||
<VisualStateGroupList>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Property="IconLabelColor"
|
||||
Value="{DynamicResource ButtonTextColorDisabled}" />
|
||||
<Setter Property="IconLabelBackgroundColor"
|
||||
Value="{DynamicResource ButtonBackgroundColorDisabled}" />
|
||||
<Setter Property="IconLabelBorderColor"
|
||||
Value="{DynamicResource ButtonBackgroundColorDisabled}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateGroupList>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- Title -->
|
||||
<Style TargetType="Button"
|
||||
Class="btn-title"
|
||||
@@ -547,19 +518,4 @@
|
||||
<Setter Property="Radius"
|
||||
Value="15" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Entry" Class="entry-visual-disabled">
|
||||
<Setter Property="VisualStateManager.VisualStateGroups">
|
||||
<VisualStateGroupList>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Property="TextColor"
|
||||
Value="{DynamicResource MutedColor}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateGroupList>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
|
||||
@@ -93,7 +93,6 @@
|
||||
Value="{DynamicResource StepperForegroundColor}" />
|
||||
</Style>
|
||||
<Style TargetType="Frame"
|
||||
ApplyToDerivedTypes="True"
|
||||
Class="btn-icon-row">
|
||||
<Setter Property="BackgroundColor"
|
||||
Value="{DynamicResource ButtonBackgroundColor}" />
|
||||
|
||||
@@ -7,7 +7,6 @@ using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Utilities;
|
||||
using Xamarin.Essentials;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Utilities.AccountManagement
|
||||
@@ -104,12 +103,11 @@ namespace Bit.App.Utilities.AccountManagement
|
||||
// var orgIdentifier = await _stateService.GetOrgIdentifierAsync();
|
||||
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
await _stateService.SetRememberedEmailAsync(email);
|
||||
_accountsManagerHost.Navigate(NavigationTarget.HomeLogin, new HomeNavigationParams(true));
|
||||
_accountsManagerHost.Navigate(NavigationTarget.Login, new LoginNavigationParams(email));
|
||||
}
|
||||
else
|
||||
{
|
||||
_accountsManagerHost.Navigate(NavigationTarget.HomeLogin, new HomeNavigationParams(false));
|
||||
_accountsManagerHost.Navigate(NavigationTarget.HomeLogin);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -185,7 +183,7 @@ namespace Bit.App.Utilities.AccountManagement
|
||||
await Device.InvokeOnMainThreadAsync(() =>
|
||||
{
|
||||
Options.HideAccountSwitcher = false;
|
||||
_accountsManagerHost.Navigate(NavigationTarget.HomeLogin, new HomeNavigationParams(false));
|
||||
_accountsManagerHost.Navigate(NavigationTarget.HomeLogin);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -220,17 +218,5 @@ namespace Bit.App.Utilities.AccountManagement
|
||||
_messagingService.Send(AccountsManagerMessageCommands.ACCOUNT_SWITCH_COMPLETED);
|
||||
});
|
||||
}
|
||||
|
||||
public async Task PromptToSwitchToExistingAccountAsync(string userId)
|
||||
{
|
||||
var switchToAccount = await _platformUtilsService.ShowDialogAsync(
|
||||
AppResources.SwitchToAlreadyAddedAccountConfirmation,
|
||||
AppResources.AccountAlreadyAdded, AppResources.Yes, AppResources.Cancel);
|
||||
if (switchToAccount)
|
||||
{
|
||||
await _stateService.SetActiveUserAsync(userId);
|
||||
_messagingService.Send("switchedAccount");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
using Bit.App.Abstractions;
|
||||
|
||||
namespace Bit.App.Utilities.AccountManagement
|
||||
{
|
||||
public class HomeNavigationParams : INavigationParams
|
||||
{
|
||||
public HomeNavigationParams(bool shouldCheckRememberEmail)
|
||||
{
|
||||
ShouldCheckRememberEmail = shouldCheckRememberEmail;
|
||||
}
|
||||
|
||||
public bool ShouldCheckRememberEmail { get; }
|
||||
}
|
||||
}
|
||||
@@ -83,10 +83,8 @@ namespace Bit.Core.Abstractions
|
||||
Task<SendResponse> PutSendAsync(string id, SendRequest request);
|
||||
Task<SendResponse> PutSendRemovePasswordAsync(string id);
|
||||
Task DeleteSendAsync(string id);
|
||||
Task<List<PasswordlessLoginResponse>> GetAuthRequestAsync();
|
||||
Task<PasswordlessLoginResponse> GetAuthRequestAsync(string id);
|
||||
Task<PasswordlessLoginResponse> PutAuthRequestAsync(string id, string key, string masterPasswordHash, string deviceIdentifier, bool requestApproved);
|
||||
Task<string> GetUsernameFromAsync(ForwardedEmailServiceType service, UsernameGeneratorConfig config);
|
||||
Task<bool> GetKnownDeviceAsync(string email, string deviceIdentifier);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ namespace Bit.Core.Abstractions
|
||||
Task<AuthResult> LogInCompleteAsync(string email, string masterPassword, TwoFactorProviderType twoFactorProvider, string twoFactorToken, bool? remember = null);
|
||||
Task<AuthResult> LogInTwoFactorAsync(TwoFactorProviderType twoFactorProvider, string twoFactorToken, string captchaToken, bool? remember = null);
|
||||
|
||||
Task<List<PasswordlessLoginResponse>> GetPasswordlessLoginRequestsAsync();
|
||||
Task<PasswordlessLoginResponse> GetPasswordlessLoginRequestByIdAsync(string id);
|
||||
Task<PasswordlessLoginResponse> PasswordlessLoginAsync(string id, string pubKey, bool requestApproved);
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ namespace Bit.Core.Abstractions
|
||||
{
|
||||
List<AccountView> AccountViews { get; }
|
||||
Task<string> GetActiveUserIdAsync();
|
||||
Task<string> GetActiveUserEmailAsync();
|
||||
Task<bool> IsActiveAccountAsync(string userId = null);
|
||||
Task SetActiveUserAsync(string userId);
|
||||
Task CheckExtensionActiveUserAndSwitchIfNeededAsync();
|
||||
|
||||
@@ -14,7 +14,5 @@ namespace Bit.Core.Abstractions
|
||||
Task<bool> SyncDeleteFolderAsync(SyncFolderNotification notification);
|
||||
Task<bool> SyncUpsertCipherAsync(SyncCipherNotification notification, bool isEdit);
|
||||
Task<bool> SyncUpsertFolderAsync(SyncFolderNotification notification, bool isEdit);
|
||||
// Passwordless code will be moved to an independent service in future techdept
|
||||
Task SyncPasswordlessLoginRequestsAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +112,5 @@
|
||||
public const string File = "\xe96e";
|
||||
public const string Paste = "\xe96f";
|
||||
public const string ViewCellMenu = "\xe5d3";
|
||||
public const string Device = "\xe986";
|
||||
public const string Suitcase = "\xe98c";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@
|
||||
public const string iOSNotificationClearActionId = "Clear";
|
||||
public const string NotificationData = "notificationData";
|
||||
public const string NotificationDataType = "Type";
|
||||
public const string PasswordlessLoginRequestKey = "passwordlessLoginRequest";
|
||||
public const int SelectFileRequestCode = 42;
|
||||
public const int SelectFilePermissionRequestCode = 43;
|
||||
public const int SaveFileRequestCode = 44;
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
|
||||
namespace Bit.Core.Models.Response
|
||||
{
|
||||
@@ -15,19 +13,7 @@ namespace Bit.Core.Models.Response
|
||||
public string Key { get; set; }
|
||||
public string MasterPasswordHash { get; set; }
|
||||
public DateTime CreationDate { get; set; }
|
||||
public DateTime? ResponseDate { get; set; }
|
||||
public bool? RequestApproved { get; set; }
|
||||
public bool RequestApproved { get; set; }
|
||||
public string Origin { get; set; }
|
||||
public string RequestAccessCode { get; set; }
|
||||
public Tuple<byte[], byte[]> RequestKeyPair { get; set; }
|
||||
|
||||
public bool IsAnswered => RequestApproved != null && ResponseDate != null;
|
||||
|
||||
public bool IsExpired => CreationDate.ToUniversalTime().AddMinutes(Constants.PasswordlessNotificationTimeoutInMinutes) < DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public class PasswordlessLoginsResponse
|
||||
{
|
||||
public List<PasswordlessLoginResponse> Data { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -536,12 +536,6 @@ namespace Bit.Core.Services
|
||||
|
||||
#region PasswordlessLogin
|
||||
|
||||
public async Task<List<PasswordlessLoginResponse>> GetAuthRequestAsync()
|
||||
{
|
||||
var response = await SendAsync<object, PasswordlessLoginsResponse>(HttpMethod.Get, $"/auth-requests/", null, true, true);
|
||||
return response.Data;
|
||||
}
|
||||
|
||||
public Task<PasswordlessLoginResponse> GetAuthRequestAsync(string id)
|
||||
{
|
||||
return SendAsync<object, PasswordlessLoginResponse>(HttpMethod.Get, $"/auth-requests/{id}", null, true, true);
|
||||
@@ -553,11 +547,6 @@ namespace Bit.Core.Services
|
||||
return SendAsync<object, PasswordlessLoginResponse>(HttpMethod.Put, $"/auth-requests/{id}", request, true, true);
|
||||
}
|
||||
|
||||
public Task<bool> GetKnownDeviceAsync(string email, string deviceIdentifier)
|
||||
{
|
||||
return SendAsync<object, bool>(HttpMethod.Get, $"/devices/knowndevice/{email}/{deviceIdentifier}", null, false, true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
@@ -801,6 +790,8 @@ namespace Bit.Core.Services
|
||||
if (authed
|
||||
&&
|
||||
(
|
||||
(tokenError && response.StatusCode == HttpStatusCode.BadRequest)
|
||||
||
|
||||
(logoutOnUnauthorized && response.StatusCode == HttpStatusCode.Unauthorized)
|
||||
||
|
||||
response.StatusCode == HttpStatusCode.Forbidden
|
||||
@@ -817,17 +808,6 @@ namespace Bit.Core.Services
|
||||
var responseJsonString = await response.Content.ReadAsStringAsync();
|
||||
responseJObject = JObject.Parse(responseJsonString);
|
||||
}
|
||||
|
||||
if (authed && tokenError
|
||||
&&
|
||||
response.StatusCode == HttpStatusCode.BadRequest
|
||||
&&
|
||||
responseJObject?["error"]?.ToString() == "invalid_grant")
|
||||
{
|
||||
await _logoutCallbackAsync(new Tuple<string, bool, bool>(null, false, true));
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ErrorResponse(responseJObject, response.StatusCode, tokenError);
|
||||
}
|
||||
catch
|
||||
|
||||
@@ -471,11 +471,6 @@ namespace Bit.Core.Services
|
||||
SelectedTwoFactorProviderType = null;
|
||||
}
|
||||
|
||||
public async Task<List<PasswordlessLoginResponse>> GetPasswordlessLoginRequestsAsync()
|
||||
{
|
||||
return await _apiService.GetAuthRequestAsync();
|
||||
}
|
||||
|
||||
public async Task<PasswordlessLoginResponse> GetPasswordlessLoginRequestByIdAsync(string id)
|
||||
{
|
||||
return await _apiService.GetAuthRequestAsync(id);
|
||||
|
||||
@@ -46,12 +46,6 @@ namespace Bit.Core.Services
|
||||
return activeUserId;
|
||||
}
|
||||
|
||||
public async Task<string> GetActiveUserEmailAsync()
|
||||
{
|
||||
var activeUserId = await GetActiveUserIdAsync();
|
||||
return await GetEmailAsync(activeUserId);
|
||||
}
|
||||
|
||||
public async Task<bool> IsActiveAccountAsync(string userId = null)
|
||||
{
|
||||
if (userId == null)
|
||||
@@ -74,6 +68,7 @@ namespace Bit.Core.Services
|
||||
_state.ActiveUserId = userId;
|
||||
|
||||
// Update pre-auth settings based on now-active user
|
||||
await SetRememberedEmailAsync(await GetEmailAsync());
|
||||
await SetRememberedOrgIdentifierAsync(await GetRememberedOrgIdentifierAsync());
|
||||
await SetPreAuthEnvironmentUrlsAsync(await GetEnvironmentUrlsAsync());
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ namespace Bit.Core.Services
|
||||
private readonly IPolicyService _policyService;
|
||||
private readonly ISendService _sendService;
|
||||
private readonly IKeyConnectorService _keyConnectorService;
|
||||
private readonly ILogger _logger;
|
||||
private readonly Func<Tuple<string, bool, bool>, Task> _logoutCallbackAsync;
|
||||
|
||||
public SyncService(
|
||||
@@ -40,7 +39,6 @@ namespace Bit.Core.Services
|
||||
IPolicyService policyService,
|
||||
ISendService sendService,
|
||||
IKeyConnectorService keyConnectorService,
|
||||
ILogger logger,
|
||||
Func<Tuple<string, bool, bool>, Task> logoutCallbackAsync)
|
||||
{
|
||||
_stateService = stateService;
|
||||
@@ -55,7 +53,6 @@ namespace Bit.Core.Services
|
||||
_policyService = policyService;
|
||||
_sendService = sendService;
|
||||
_keyConnectorService = keyConnectorService;
|
||||
_logger = logger;
|
||||
_logoutCallbackAsync = logoutCallbackAsync;
|
||||
}
|
||||
|
||||
@@ -385,45 +382,5 @@ namespace Bit.Core.Services
|
||||
new Dictionary<string, SendData>();
|
||||
await _sendService.ReplaceAsync(sends);
|
||||
}
|
||||
|
||||
public async Task SyncPasswordlessLoginRequestsAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var userId = await _stateService.GetActiveUserIdAsync();
|
||||
// if the user has not enabled passwordless logins ignore requests
|
||||
if (!await _stateService.GetApprovePasswordlessLoginsAsync(userId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var loginRequests = await _apiService.GetAuthRequestAsync();
|
||||
if (loginRequests == null || !loginRequests.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var validLoginRequest = loginRequests.Where(l => !l.IsAnswered && !l.IsExpired)
|
||||
.OrderByDescending(x => x.CreationDate)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (validLoginRequest is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await _stateService.SetPasswordlessLoginNotificationAsync(new PasswordlessRequestNotification()
|
||||
{
|
||||
Id = validLoginRequest.Id,
|
||||
UserId = userId
|
||||
});
|
||||
|
||||
_messagingService.Send(Constants.PasswordlessLoginRequestKey);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Exception(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,8 +29,6 @@ namespace Bit.Core.Utilities
|
||||
var messagingService = Resolve<IMessagingService>("messagingService");
|
||||
var cryptoFunctionService = Resolve<ICryptoFunctionService>("cryptoFunctionService");
|
||||
var cryptoService = Resolve<ICryptoService>("cryptoService");
|
||||
var logger = Resolve<ILogger>();
|
||||
|
||||
SearchService searchService = null;
|
||||
|
||||
var tokenService = new TokenService(stateService);
|
||||
@@ -69,7 +67,7 @@ namespace Bit.Core.Utilities
|
||||
});
|
||||
var syncService = new SyncService(stateService, apiService, settingsService, folderService, cipherService,
|
||||
cryptoService, collectionService, organizationService, messagingService, policyService, sendService,
|
||||
keyConnectorService, logger, (extras) =>
|
||||
keyConnectorService, (extras) =>
|
||||
{
|
||||
messagingService.Send("logout", extras);
|
||||
return Task.CompletedTask;
|
||||
|
||||
@@ -425,16 +425,15 @@ namespace Bit.iOS.Autofill
|
||||
}
|
||||
}
|
||||
|
||||
private void LaunchHomePage(bool shouldCheckRememberEmail = true)
|
||||
private void LaunchHomePage()
|
||||
{
|
||||
var appOptions = new AppOptions { IosExtension = true };
|
||||
var homePage = new HomePage(appOptions, shouldCheckRememberEmail);
|
||||
var app = new App.App(appOptions);
|
||||
var homePage = new HomePage();
|
||||
var app = new App.App(new AppOptions { IosExtension = true });
|
||||
ThemeManager.SetTheme(app.Resources);
|
||||
ThemeManager.ApplyResourcesTo(homePage);
|
||||
if (homePage.BindingContext is HomeViewModel vm)
|
||||
{
|
||||
vm.StartLoginAction = () => DismissViewController(false, () => LaunchLoginFlow(vm.Email));
|
||||
vm.StartLoginAction = () => DismissViewController(false, () => LaunchLoginFlow());
|
||||
vm.StartRegisterAction = () => DismissViewController(false, () => LaunchRegisterFlow());
|
||||
vm.StartSsoLoginAction = () => DismissViewController(false, () => LaunchLoginSsoFlow());
|
||||
vm.StartEnvironmentAction = () => DismissViewController(false, () => LaunchEnvironmentFlow());
|
||||
@@ -457,8 +456,8 @@ namespace Bit.iOS.Autofill
|
||||
ThemeManager.ApplyResourcesTo(environmentPage);
|
||||
if (environmentPage.BindingContext is EnvironmentPageViewModel vm)
|
||||
{
|
||||
vm.SubmitSuccessAction = () => DismissViewController(false, () => LaunchHomePage(shouldCheckRememberEmail: false));
|
||||
vm.CloseAction = () => DismissViewController(false, () => LaunchHomePage(shouldCheckRememberEmail: false));
|
||||
vm.SubmitSuccessAction = () => DismissViewController(false, () => LaunchHomePage());
|
||||
vm.CloseAction = () => DismissViewController(false, () => LaunchHomePage());
|
||||
}
|
||||
|
||||
var navigationPage = new NavigationPage(environmentPage);
|
||||
@@ -476,7 +475,7 @@ namespace Bit.iOS.Autofill
|
||||
if (registerPage.BindingContext is RegisterPageViewModel vm)
|
||||
{
|
||||
vm.RegistrationSuccess = () => DismissViewController(false, () => LaunchLoginFlow(vm.Email));
|
||||
vm.CloseAction = () => DismissViewController(false, () => LaunchHomePage(shouldCheckRememberEmail: false));
|
||||
vm.CloseAction = () => DismissViewController(false, () => LaunchHomePage());
|
||||
}
|
||||
|
||||
var navigationPage = new NavigationPage(registerPage);
|
||||
@@ -496,9 +495,8 @@ namespace Bit.iOS.Autofill
|
||||
{
|
||||
vm.StartTwoFactorAction = () => DismissViewController(false, () => LaunchTwoFactorFlow(false));
|
||||
vm.UpdateTempPasswordAction = () => DismissViewController(false, () => LaunchUpdateTempPasswordFlow());
|
||||
vm.StartSsoLoginAction = () => DismissViewController(false, () => LaunchLoginSsoFlow());
|
||||
vm.LogInSuccessAction = () => DismissLockAndContinue();
|
||||
vm.CloseAction = () => DismissViewController(false, () => LaunchHomePage(shouldCheckRememberEmail: false));
|
||||
vm.CloseAction = () => DismissViewController(false, () => LaunchHomePage());
|
||||
}
|
||||
|
||||
var navigationPage = new NavigationPage(loginPage);
|
||||
@@ -604,14 +602,7 @@ namespace Bit.iOS.Autofill
|
||||
switch (navTarget)
|
||||
{
|
||||
case NavigationTarget.HomeLogin:
|
||||
if (navParams is HomeNavigationParams homeParams)
|
||||
{
|
||||
DismissViewController(false, () => LaunchHomePage(homeParams.ShouldCheckRememberEmail));
|
||||
}
|
||||
else
|
||||
{
|
||||
DismissViewController(false, () => LaunchHomePage());
|
||||
}
|
||||
DismissViewController(false, () => LaunchHomePage());
|
||||
break;
|
||||
case NavigationTarget.Login:
|
||||
if (navParams is LoginNavigationParams loginParams)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.8bit.bitwarden.autofill</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2022.11.0</string>
|
||||
<string>2022.10.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>CFBundleLocalizations</key>
|
||||
|
||||
Binary file not shown.
@@ -11,7 +11,7 @@
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.8bit.bitwarden.find-login-action-extension</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2022.11.0</string>
|
||||
<string>2022.10.1</string>
|
||||
<key>CFBundleLocalizations</key>
|
||||
<array>
|
||||
<string>en</string>
|
||||
|
||||
@@ -446,16 +446,15 @@ namespace Bit.iOS.Extension
|
||||
});
|
||||
}
|
||||
|
||||
private void LaunchHomePage(bool shouldCheckRememberEmail = true)
|
||||
private void LaunchHomePage()
|
||||
{
|
||||
var appOptions = new AppOptions { IosExtension = true };
|
||||
var homePage = new HomePage(appOptions, shouldCheckRememberEmail);
|
||||
var app = new App.App(appOptions);
|
||||
var homePage = new HomePage();
|
||||
var app = new App.App(new AppOptions { IosExtension = true });
|
||||
ThemeManager.SetTheme(app.Resources);
|
||||
ThemeManager.ApplyResourcesTo(homePage);
|
||||
if (homePage.BindingContext is HomeViewModel vm)
|
||||
{
|
||||
vm.StartLoginAction = () => DismissViewController(false, () => LaunchLoginFlow(vm.Email));
|
||||
vm.StartLoginAction = () => DismissViewController(false, () => LaunchLoginFlow());
|
||||
vm.StartRegisterAction = () => DismissViewController(false, () => LaunchRegisterFlow());
|
||||
vm.StartSsoLoginAction = () => DismissViewController(false, () => LaunchLoginSsoFlow());
|
||||
vm.StartEnvironmentAction = () => DismissViewController(false, () => LaunchEnvironmentFlow());
|
||||
@@ -478,8 +477,8 @@ namespace Bit.iOS.Extension
|
||||
ThemeManager.ApplyResourcesTo(environmentPage);
|
||||
if (environmentPage.BindingContext is EnvironmentPageViewModel vm)
|
||||
{
|
||||
vm.SubmitSuccessAction = () => DismissViewController(false, () => LaunchHomePage(shouldCheckRememberEmail: false));
|
||||
vm.CloseAction = () => DismissViewController(false, () => LaunchHomePage(shouldCheckRememberEmail: false));
|
||||
vm.SubmitSuccessAction = () => DismissViewController(false, () => LaunchHomePage());
|
||||
vm.CloseAction = () => DismissViewController(false, () => LaunchHomePage());
|
||||
}
|
||||
|
||||
var navigationPage = new NavigationPage(environmentPage);
|
||||
@@ -497,7 +496,7 @@ namespace Bit.iOS.Extension
|
||||
if (registerPage.BindingContext is RegisterPageViewModel vm)
|
||||
{
|
||||
vm.RegistrationSuccess = () => DismissViewController(false, () => LaunchLoginFlow(vm.Email));
|
||||
vm.CloseAction = () => DismissViewController(false, () => LaunchHomePage(shouldCheckRememberEmail: false));
|
||||
vm.CloseAction = () => DismissViewController(false, () => LaunchHomePage());
|
||||
}
|
||||
|
||||
var navigationPage = new NavigationPage(registerPage);
|
||||
@@ -517,9 +516,8 @@ namespace Bit.iOS.Extension
|
||||
{
|
||||
vm.StartTwoFactorAction = () => DismissViewController(false, () => LaunchTwoFactorFlow(false));
|
||||
vm.UpdateTempPasswordAction = () => DismissViewController(false, () => LaunchUpdateTempPasswordFlow());
|
||||
vm.StartSsoLoginAction = () => DismissViewController(false, () => LaunchLoginSsoFlow());
|
||||
vm.LogInSuccessAction = () => DismissLockAndContinue();
|
||||
vm.CloseAction = () => DismissViewController(false, () => LaunchHomePage(shouldCheckRememberEmail: false));
|
||||
vm.CloseAction = () => CompleteRequest(null, null);
|
||||
}
|
||||
|
||||
var navigationPage = new NavigationPage(loginPage);
|
||||
|
||||
Binary file not shown.
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2022.11.0</string>
|
||||
<string>2022.10.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
|
||||
@@ -287,13 +287,13 @@ namespace Bit.iOS.ShareExtension
|
||||
return _app;
|
||||
}
|
||||
|
||||
private void LaunchHomePage(bool shouldCheckRememberEmail = true)
|
||||
private void LaunchHomePage()
|
||||
{
|
||||
var homePage = new HomePage(_appOptions.Value, shouldCheckRememberEmail);
|
||||
var homePage = new HomePage();
|
||||
SetupAppAndApplyResources(homePage);
|
||||
if (homePage.BindingContext is HomeViewModel vm)
|
||||
{
|
||||
vm.StartLoginAction = () => DismissAndLaunch(() => LaunchLoginFlow(vm.Email));
|
||||
vm.StartLoginAction = () => DismissAndLaunch(() => LaunchLoginFlow());
|
||||
vm.StartRegisterAction = () => DismissAndLaunch(() => LaunchRegisterFlow());
|
||||
vm.StartSsoLoginAction = () => DismissAndLaunch(() => LaunchLoginSsoFlow());
|
||||
vm.StartEnvironmentAction = () => DismissAndLaunch(() => LaunchEnvironmentFlow());
|
||||
@@ -311,8 +311,8 @@ namespace Bit.iOS.ShareExtension
|
||||
ThemeManager.ApplyResourcesTo(environmentPage);
|
||||
if (environmentPage.BindingContext is EnvironmentPageViewModel vm)
|
||||
{
|
||||
vm.SubmitSuccessAction = () => DismissAndLaunch(() => LaunchHomePage(shouldCheckRememberEmail: false));
|
||||
vm.CloseAction = () => DismissAndLaunch(() => LaunchHomePage(shouldCheckRememberEmail: false));
|
||||
vm.SubmitSuccessAction = () => DismissAndLaunch(() => LaunchHomePage());
|
||||
vm.CloseAction = () => DismissAndLaunch(() => LaunchHomePage());
|
||||
}
|
||||
|
||||
NavigateToPage(environmentPage);
|
||||
@@ -325,7 +325,7 @@ namespace Bit.iOS.ShareExtension
|
||||
if (registerPage.BindingContext is RegisterPageViewModel vm)
|
||||
{
|
||||
vm.RegistrationSuccess = () => DismissAndLaunch(() => LaunchLoginFlow(vm.Email));
|
||||
vm.CloseAction = () => DismissAndLaunch(() => LaunchHomePage(shouldCheckRememberEmail: false));
|
||||
vm.CloseAction = () => DismissAndLaunch(() => LaunchHomePage());
|
||||
}
|
||||
NavigateToPage(registerPage);
|
||||
}
|
||||
@@ -338,9 +338,11 @@ namespace Bit.iOS.ShareExtension
|
||||
{
|
||||
vm.StartTwoFactorAction = () => DismissAndLaunch(() => LaunchTwoFactorFlow(false));
|
||||
vm.UpdateTempPasswordAction = () => DismissAndLaunch(() => LaunchUpdateTempPasswordFlow());
|
||||
vm.StartSsoLoginAction = () => DismissAndLaunch(() => LaunchLoginSsoFlow());
|
||||
vm.LogInSuccessAction = () => { DismissLockAndContinue(); };
|
||||
vm.CloseAction = () => DismissAndLaunch(() => LaunchHomePage(shouldCheckRememberEmail: false));
|
||||
vm.LogInSuccessAction = () =>
|
||||
{
|
||||
DismissLockAndContinue();
|
||||
};
|
||||
vm.CloseAction = () => CompleteRequest();
|
||||
}
|
||||
NavigateToPage(loginPage);
|
||||
|
||||
@@ -424,14 +426,7 @@ namespace Bit.iOS.ShareExtension
|
||||
switch (navTarget)
|
||||
{
|
||||
case NavigationTarget.HomeLogin:
|
||||
if (navParams is HomeNavigationParams homeParams)
|
||||
{
|
||||
ExecuteLaunch(() => LaunchHomePage(homeParams.ShouldCheckRememberEmail));
|
||||
}
|
||||
else
|
||||
{
|
||||
ExecuteLaunch(() => LaunchHomePage());
|
||||
}
|
||||
ExecuteLaunch(LaunchHomePage);
|
||||
break;
|
||||
case NavigationTarget.Login:
|
||||
if (navParams is LoginNavigationParams loginParams)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.8bit.bitwarden</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2022.11.0</string>
|
||||
<string>2022.10.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>CFBundleIconName</key>
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user