diff --git a/src/AzureExtension/Strings/en-US/Resources.resw b/src/AzureExtension/Strings/en-US/Resources.resw
index c0001dd9..f00b1228 100644
--- a/src/AzureExtension/Strings/en-US/Resources.resw
+++ b/src/AzureExtension/Strings/en-US/Resources.resw
@@ -138,7 +138,7 @@
Shown in Widget, Tooltip text
- You must sign in to view this widget's content.
+ You must sign in to view this widget's content. Go to the Account Settings page to sign in.
Shown in Widget when user is not logged in.
diff --git a/src/AzureExtension/Widgets/AzurePullRequestsWidget.cs b/src/AzureExtension/Widgets/AzurePullRequestsWidget.cs
index c8576daa..875ecb79 100644
--- a/src/AzureExtension/Widgets/AzurePullRequestsWidget.cs
+++ b/src/AzureExtension/Widgets/AzurePullRequestsWidget.cs
@@ -22,7 +22,6 @@ internal class AzurePullRequestsWidget : AzureWidget
// Widget Data
private string widgetTitle = string.Empty;
- private string selectedDevId = string.Empty;
private string selectedRepositoryUrl = string.Empty;
private string selectedRepositoryName = string.Empty;
private string selectedView = DefaultSelectedView;
@@ -91,19 +90,19 @@ protected override void HandleSubmit(WidgetActionInvokedArgs args)
if (dataObject != null && dataObject["account"] != null && dataObject["query"] != null)
{
widgetTitle = dataObject["widgetTitle"]?.GetValue() ?? string.Empty;
- selectedDevId = dataObject["account"]?.GetValue() ?? string.Empty;
+ DeveloperLoginId = dataObject["account"]?.GetValue() ?? string.Empty;
selectedRepositoryUrl = dataObject["query"]?.GetValue() ?? string.Empty;
selectedView = dataObject["view"]?.GetValue() ?? string.Empty;
- SetDefaultDeveloperId();
- if (selectedDevId != dataObject["account"]?.GetValue())
+ SetDefaultDeveloperLoginId();
+ if (DeveloperLoginId != dataObject["account"]?.GetValue())
{
- dataObject["account"] = selectedDevId;
+ dataObject["account"] = DeveloperLoginId;
data = dataObject.ToJsonString();
}
ConfigurationData = data;
- var developerId = GetDevId(selectedDevId);
+ var developerId = GetDevId(DeveloperLoginId);
if (developerId == null)
{
message = Resources.GetResource(@"Widget_Template/DevIDError");
@@ -135,29 +134,21 @@ public override void OnCustomizationRequested(WidgetCustomizationRequestedArgs c
SetConfigure();
}
- // This method will attempt to select a DeveloperId if one is not already selected.
- // It uses the input url and tries to find the most likely DeveloperId that corresponds to the
- // url among the set of available DeveloperIds. If there is no best match it chooses the first
- // available developerId or none if there are no DeveloperIds.
- private void SetDefaultDeveloperId()
+ // Increase precision of SetDefaultDeveloperLoginId by matching the selectedRepositoryUrl's org
+ // with the first matching DeveloperId that contains that org.
+ protected override void SetDefaultDeveloperLoginId()
{
- if (!string.IsNullOrEmpty(selectedDevId))
- {
- return;
- }
-
- var devIds = DeveloperIdProvider.GetInstance().GetLoggedInDeveloperIds().DeveloperIds;
- if (devIds is null)
- {
- return;
- }
-
- // Set as the first DevId found, unless we find a better match from the Url.
- selectedDevId = devIds.FirstOrDefault()?.LoginId ?? string.Empty;
+ base.SetDefaultDeveloperLoginId();
var azureOrg = new AzureUri(selectedRepositoryUrl).Organization;
if (!string.IsNullOrEmpty(azureOrg))
{
- selectedDevId = devIds.Where(i => i.LoginId.Contains(azureOrg, StringComparison.OrdinalIgnoreCase)).FirstOrDefault()?.LoginId ?? selectedDevId;
+ var devIds = DeveloperIdProvider.GetInstance().GetLoggedInDeveloperIds().DeveloperIds;
+ if (devIds is null)
+ {
+ return;
+ }
+
+ DeveloperLoginId = devIds.Where(i => i.LoginId.Contains(azureOrg, StringComparison.OrdinalIgnoreCase)).FirstOrDefault()?.LoginId ?? DeveloperLoginId;
}
}
@@ -189,7 +180,7 @@ public override void HandleDataManagerUpdate(object? source, DataManagerUpdateEv
public override void RequestContentData()
{
- var developerId = GetDevId(selectedDevId);
+ var developerId = GetDevId(DeveloperLoginId);
if (developerId == null)
{
// Should not happen
@@ -219,11 +210,11 @@ private void ResetDataFromState(string data)
}
widgetTitle = dataObject["widgetTitle"]?.GetValue() ?? string.Empty;
- selectedDevId = dataObject["account"]?.GetValue() ?? string.Empty;
+ DeveloperLoginId = dataObject["account"]?.GetValue() ?? string.Empty;
selectedRepositoryUrl = dataObject["query"]?.GetValue() ?? string.Empty;
selectedView = dataObject["view"]?.GetValue() ?? string.Empty;
- var developerId = GetDevId(selectedDevId);
+ var developerId = GetDevId(DeveloperLoginId);
if (developerId == null)
{
return;
@@ -251,7 +242,7 @@ public override string GetConfiguration(string data)
configurationData.Add("accounts", developerIdsData);
- configurationData.Add("selectedDevId", selectedDevId);
+ configurationData.Add("selectedDevId", DeveloperLoginId);
configurationData.Add("url", selectedRepositoryUrl);
configurationData.Add("selectedView", selectedView);
configurationData.Add("message", message);
@@ -268,7 +259,7 @@ public override void LoadContentData()
{
try
{
- var developerId = GetDevId(selectedDevId);
+ var developerId = GetDevId(DeveloperLoginId);
if (developerId == null)
{
// Should not happen
diff --git a/src/AzureExtension/Widgets/AzureQueryListWidget.cs b/src/AzureExtension/Widgets/AzureQueryListWidget.cs
index df21b6f9..f83e4dfd 100644
--- a/src/AzureExtension/Widgets/AzureQueryListWidget.cs
+++ b/src/AzureExtension/Widgets/AzureQueryListWidget.cs
@@ -4,7 +4,6 @@
using System.Text.Json.Nodes;
using DevHomeAzureExtension.Client;
using DevHomeAzureExtension.DataManager;
-using DevHomeAzureExtension.DataModel;
using DevHomeAzureExtension.DeveloperId;
using DevHomeAzureExtension.Helpers;
using Microsoft.Windows.Widgets.Providers;
@@ -20,7 +19,6 @@ internal class AzureQueryListWidget : AzureWidget
// Widget Data
private string widgetTitle = string.Empty;
- private string selectedDevId = string.Empty;
private string selectedQueryUrl = string.Empty;
private string selectedQueryId = string.Empty;
private string? message;
@@ -90,17 +88,17 @@ protected override void HandleSubmit(WidgetActionInvokedArgs args)
CanPin = false;
widgetTitle = dataObject["widgetTitle"]?.GetValue() ?? string.Empty;
selectedQueryUrl = dataObject["query"]?.GetValue() ?? string.Empty;
- selectedDevId = dataObject["account"]?.GetValue() ?? string.Empty;
- SetDefaultDeveloperId();
- if (selectedDevId != dataObject["account"]?.GetValue())
+ DeveloperLoginId = dataObject["account"]?.GetValue() ?? string.Empty;
+ SetDefaultDeveloperLoginId();
+ if (DeveloperLoginId != dataObject["account"]?.GetValue())
{
- dataObject["account"] = selectedDevId;
+ dataObject["account"] = DeveloperLoginId;
data = dataObject.ToJsonString();
}
ConfigurationData = data;
- var developerId = GetDevId(selectedDevId);
+ var developerId = GetDevId(DeveloperLoginId);
if (developerId == null)
{
message = Resources.GetResource(@"Widget_Template/DevIDError");
@@ -137,29 +135,22 @@ public override void OnCustomizationRequested(WidgetCustomizationRequestedArgs c
SetConfigure();
}
- // This method will attempt to select a DeveloperId if one is not already selected.
- // It uses the input url and tries to find the most likely DeveloperId that corresponds to the
- // url among the set of available DeveloperIds. If there is no best match it chooses the first
- // available developerId or none if there are no DeveloperIds.
- private void SetDefaultDeveloperId()
+ // Increase precision of SetDefaultDeveloperLoginId by matching the selectedQueryUrl's org
+ // with the first matching DeveloperId that contains that org.
+ protected override void SetDefaultDeveloperLoginId()
{
- if (!string.IsNullOrEmpty(selectedDevId))
- {
- return;
- }
+ base.SetDefaultDeveloperLoginId();
- var devIds = DeveloperIdProvider.GetInstance().GetLoggedInDeveloperIds().DeveloperIds;
- if (devIds is null)
- {
- return;
- }
-
- // Set as the first DevId found, unless we find a better match from the Url.
- selectedDevId = devIds.FirstOrDefault()?.LoginId ?? string.Empty;
var azureOrg = new AzureUri(selectedQueryUrl).Organization;
if (!string.IsNullOrEmpty(azureOrg))
{
- selectedDevId = devIds.Where(i => i.LoginId.Contains(azureOrg, StringComparison.OrdinalIgnoreCase)).FirstOrDefault()?.LoginId ?? selectedDevId;
+ var devIds = DeveloperIdProvider.GetInstance().GetLoggedInDeveloperIds().DeveloperIds;
+ if (devIds is null)
+ {
+ return;
+ }
+
+ DeveloperLoginId = devIds.Where(i => i.LoginId.Contains(azureOrg, StringComparison.OrdinalIgnoreCase)).FirstOrDefault()?.LoginId ?? DeveloperLoginId;
}
}
@@ -191,7 +182,7 @@ public override void HandleDataManagerUpdate(object? source, DataManagerUpdateEv
public override void RequestContentData()
{
- var developerId = GetDevId(selectedDevId);
+ var developerId = GetDevId(DeveloperLoginId);
if (developerId == null)
{
// Should not happen
@@ -222,10 +213,10 @@ private void ResetDataFromState(string data)
}
widgetTitle = dataObject["widgetTitle"]?.GetValue() ?? string.Empty;
- selectedDevId = dataObject["account"]?.GetValue() ?? string.Empty;
+ DeveloperLoginId = dataObject["account"]?.GetValue() ?? string.Empty;
selectedQueryUrl = dataObject["query"]?.GetValue() ?? string.Empty;
- var developerId = GetDevId(selectedDevId);
+ var developerId = GetDevId(DeveloperLoginId);
if (developerId == null)
{
return;
@@ -257,7 +248,7 @@ public override string GetConfiguration(string data)
configurationData.Add("accounts", developerIdsData);
- configurationData.Add("selectedDevId", selectedDevId);
+ configurationData.Add("selectedDevId", DeveloperLoginId);
configurationData.Add("url", selectedQueryUrl);
configurationData.Add("message", message);
configurationData.Add("widgetTitle", widgetTitle);
@@ -273,7 +264,7 @@ public override void LoadContentData()
{
try
{
- var developerId = GetDevId(selectedDevId);
+ var developerId = GetDevId(DeveloperLoginId);
if (developerId == null)
{
// Should not happen, but may be possible in situations where the app is removed and
@@ -293,7 +284,7 @@ public override void LoadContentData()
}
// This can throw if DataStore is not connected.
- Query? queryInfo = DataManager!.GetQuery(azureUri, developerId.LoginId);
+ var queryInfo = DataManager!.GetQuery(azureUri, developerId.LoginId);
var queryResults = queryInfo is null ? new Dictionary() : JsonConvert.DeserializeObject>(queryInfo.QueryResults);
diff --git a/src/AzureExtension/Widgets/AzureQueryTilesWidget.cs b/src/AzureExtension/Widgets/AzureQueryTilesWidget.cs
index 74e6d7ed..04874437 100644
--- a/src/AzureExtension/Widgets/AzureQueryTilesWidget.cs
+++ b/src/AzureExtension/Widgets/AzureQueryTilesWidget.cs
@@ -22,8 +22,6 @@ internal class AzureQueryTilesWidget : AzureWidget
protected static readonly new string Name = nameof(AzureQueryTilesWidget);
private readonly List tiles = new();
- private string selectedDevId = string.Empty;
-
// Creation and destruction methods
public AzureQueryTilesWidget()
: base()
@@ -185,7 +183,7 @@ public override void HandleDataManagerUpdate(object? source, DataManagerUpdateEv
public override void RequestContentData()
{
- var developerId = GetDevId(selectedDevId);
+ var developerId = GetDevId(DeveloperLoginId);
if (developerId == null)
{
// Should not happen
@@ -218,7 +216,7 @@ public override void LoadContentData()
var data = new JsonObject();
var linesArray = new JsonArray();
- var developerId = GetDevId(selectedDevId);
+ var developerId = GetDevId(DeveloperLoginId);
if (developerId == null)
{
// Should not happen
@@ -284,8 +282,8 @@ private void ValidateConfigurationData()
CanPin = false;
var pinIssueFound = false;
- SetDefaultDeveloperId();
- var developerId = GetDevId(selectedDevId);
+ SetDefaultDeveloperLoginId();
+ var developerId = GetDevId(DeveloperLoginId);
if (developerId == null)
{
return;
@@ -351,29 +349,21 @@ private void ResetNumberOfTilesFromData(string data)
}
}
- // This method will attempt to select a DeveloperId if one is not already selected.
- // It uses the url of the first valid tile and tries to find the most likely DeveloperId that
- // among the set of available DeveloperIds. If there is no best match it chooses the first
- // available developerId or none if there are no DeveloperIds.
- private void SetDefaultDeveloperId()
+ // Increase precision of SetDefaultDeveloperLoginId by matching the first valid org in the
+ // tiles list with the first matching DeveloperId that contains that org.
+ protected override void SetDefaultDeveloperLoginId()
{
- if (!string.IsNullOrEmpty(selectedDevId))
- {
- return;
- }
-
- var devIds = DeveloperIdProvider.GetInstance().GetLoggedInDeveloperIds().DeveloperIds;
- if (devIds is null)
- {
- return;
- }
-
- // Set as the first DevId found, unless we find a better match to the first Org found.
- selectedDevId = devIds.FirstOrDefault()?.LoginId ?? string.Empty;
+ base.SetDefaultDeveloperLoginId();
var azureOrg = tiles.Where(i => i.AzureUri.IsValid).FirstOrDefault().AzureUri?.Organization ?? string.Empty;
if (!string.IsNullOrEmpty(azureOrg))
{
- selectedDevId = devIds.Where(i => i.LoginId.Contains(azureOrg, StringComparison.OrdinalIgnoreCase)).FirstOrDefault()?.LoginId ?? selectedDevId;
+ var devIds = DeveloperIdProvider.GetInstance().GetLoggedInDeveloperIds().DeveloperIds;
+ if (devIds is null)
+ {
+ return;
+ }
+
+ DeveloperLoginId = devIds.Where(i => i.LoginId.Contains(azureOrg, StringComparison.OrdinalIgnoreCase)).FirstOrDefault()?.LoginId ?? DeveloperLoginId;
}
}
@@ -407,11 +397,11 @@ private void UpdateAllTiles(string data)
}
}
- selectedDevId = dataObject["account"]?.GetValue() ?? string.Empty;
- SetDefaultDeveloperId();
- if (selectedDevId != dataObject["account"]?.GetValue())
+ DeveloperLoginId = dataObject["account"]?.GetValue() ?? string.Empty;
+ SetDefaultDeveloperLoginId();
+ if (DeveloperLoginId != dataObject["account"]?.GetValue())
{
- dataObject["account"] = selectedDevId;
+ dataObject["account"] = DeveloperLoginId;
data = dataObject.ToJsonString();
}
@@ -450,7 +440,7 @@ public override string GetConfiguration(string data)
}
configurationData.Add("tiles", tilesArray);
- configurationData.Add("selectedDevId", selectedDevId);
+ configurationData.Add("selectedDevId", DeveloperLoginId);
configurationData.Add("configuring", !CanPin);
configurationData.Add("pinned", Pinned);
configurationData.Add("arrow", IconLoader.GetIconAsBase64("arrow.png"));
diff --git a/src/AzureExtension/Widgets/AzureWidget.cs b/src/AzureExtension/Widgets/AzureWidget.cs
index 5d7a7557..7f13afdc 100644
--- a/src/AzureExtension/Widgets/AzureWidget.cs
+++ b/src/AzureExtension/Widgets/AzureWidget.cs
@@ -35,6 +35,8 @@ public abstract class AzureWidget : WidgetImpl
protected string ContentData { get; set; } = EmptyJson;
+ protected string DeveloperLoginId { get; set; } = string.Empty;
+
protected bool CanPin
{
get; set;
@@ -197,10 +199,7 @@ protected async Task HandleSignIn()
{
Log.Logger()?.ReportInfo(Name, ShortId, $"WidgetAction invoked for user sign in");
- // Do Sign-in Flow
- // var authProvider = DeveloperIdProvider.GetInstance();
- UpdateActivityState();
- Log.Logger()?.ReportInfo(Name, ShortId, $"User sign in successful from WidgetAction invocation");
+ // We cannot do sign-in flow because it requires the main window of DevHome.
}
protected WidgetAction GetWidgetActionForVerb(string verb)
@@ -230,10 +229,30 @@ public string GetSignIn()
return signInData.ToString();
}
- public bool IsUserLoggedIn()
+ protected virtual bool IsUserLoggedIn()
{
+ // User is not logged in if either there are zero DeveloperIds logged in, or the selected
+ // DeveloperId for this widget is not logged in.
var authProvider = DeveloperIdProvider.GetInstance();
- return authProvider.GetLoggedInDeveloperIds().DeveloperIds.Any();
+ if (!authProvider.GetLoggedInDeveloperIds().DeveloperIds.Any())
+ {
+ return false;
+ }
+
+ if (string.IsNullOrEmpty(DeveloperLoginId))
+ {
+ // User has not yet chosen a DeveloperId, but there is at least one available, so the
+ // user has logged in and we are in a good state.
+ return true;
+ }
+
+ if (GetDevId(DeveloperLoginId) is not null)
+ {
+ // The selected DeveloperId is logged in so we are in a good state.
+ return true;
+ }
+
+ return false;
}
protected DeveloperId.DeveloperId? GetDevId(string login)
@@ -470,8 +489,45 @@ private async Task PeriodicUpdate()
lastUpdateRequest = DateTime.Now;
}
- private void HandleDeveloperIdChange(object? sender, IDeveloperId e)
+ // This method will attempt to select a DeveloperId if one is not already selected. By default
+ // this will simply select the first DeveloperId in the list of available DeveloperIds.
+ protected virtual void SetDefaultDeveloperLoginId()
{
+ if (!string.IsNullOrEmpty(DeveloperLoginId))
+ {
+ return;
+ }
+
+ var devIds = DeveloperIdProvider.GetInstance().GetLoggedInDeveloperIds().DeveloperIds;
+ if (devIds is null)
+ {
+ return;
+ }
+
+ // Set as the first DevId found, unless we find a better match from the Url.
+ DeveloperLoginId = devIds.FirstOrDefault()?.LoginId ?? string.Empty;
+ }
+
+ protected void HandleDeveloperIdChange(object? sender, IDeveloperId e)
+ {
+ if (e.LoginId == DeveloperLoginId)
+ {
+ try
+ {
+ var state = DeveloperIdProvider.GetInstance().GetDeveloperIdState(e);
+ if (state == AuthenticationState.LoggedIn)
+ {
+ // If the DevId we are set for logs in, refresh the data.
+ RequestContentData();
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.Logger()?.ReportError(Name, ShortId, $"Failed getting DeveloperId state.", ex);
+ }
+ }
+
+ // Update state regardless, since we could be waiting for any developer to sign in.
Log.Logger()?.ReportInfo(Name, ShortId, $"Change in Developer Id, Updating widget state.");
UpdateActivityState();
}
diff --git a/src/AzureExtension/Widgets/Templates/AzureSignInTemplate.json b/src/AzureExtension/Widgets/Templates/AzureSignInTemplate.json
index 5bed8ddc..c5293fca 100644
--- a/src/AzureExtension/Widgets/Templates/AzureSignInTemplate.json
+++ b/src/AzureExtension/Widgets/Templates/AzureSignInTemplate.json
@@ -14,38 +14,6 @@
"height": "stretch",
"size": "Default",
"weight": "Bolder"
- },
- {
- "type": "ColumnSet",
- "spacing": "ExtraLarge",
- "columns": [
- {
- "type": "Column",
- "width": "stretch"
- },
- {
- "type": "Column",
- "width": "auto",
- "items": [
- {
- "type": "ActionSet",
- "actions": [
- {
- "type": "Action.Execute",
- "title": "%Widget_Template_Button/SignIn%",
- "verb": "SignIn",
- "tooltip": "%Widget_Template_Tooltip/SignIn%"
- }
- ],
- "spacing": "ExtraLarge"
- }
- ]
- },
- {
- "type": "Column",
- "width": "stretch"
- }
- ]
}
],
"horizontalAlignment": "Center",