OfficeDev / TeamsFx

Developer tools for building Teams apps
Other
427 stars 164 forks source link

Unable to simulate clicks via javascript #8616

Open wrharper-AASP opened 1 year ago

wrharper-AASP commented 1 year ago

Describe the bug Normally, you can simulate clicks in javascript, but this does not appear to be possible in Blazor/TeamsFx.

To Reproduce Go to any Teams app with a browser and do F12 dev tools. Go to the console and type: document.getElementsByClassName('btn')[1].click(); This will click the 2nd tab.

If you try to do this same thing in the TeamsFx toolkit Blazor via a Javascript call you get the error: TypeError: Cannot read properties of undefined (reading 'click')

Javascript in _Host.cshtml:

<script>
function navToSettings(){
            document.getElementsByClassName('btn')[1].click();
        }
</script>

Call anywhere in the program:

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await base.OnAfterRenderAsync(firstRender);

        if (firstRender)
        {
await JsRuntime.InvokeVoidAsync("navToSettings");
        }
    }

Screenshots image

Visual Studio 2022 TeamsFx 2.0

ghost commented 1 year ago

Thank you for contacting us! Any issue or feedback from you is quite important to us. We will do our best to fully respond to your issue as soon as possible. Sometimes additional investigations may be needed, we will usually get back to you within 2 days by adding comments to this issue. Please stay tuned.

JerryYangKai commented 1 year ago

Hi @wrharper-AASP, I have followed your repro step and got a same result. I tried a lot and found that when we trigger it in console, it regards the whole Teams as a page, so browser can find the button then click it. When we trigger it in code level, it will only focus on the elements in tab page itself, so there is no button in it, it will output click undefined.

wrharper-AASP commented 1 year ago

Hi @wrharper-AASP, I have followed your repro step and got a same result. I tried a lot and found that when we trigger it in console, it regards the whole Teams as a page, so browser can find the button then click it. When we trigger it in code level, it will only focus on the elements in tab page itself, so there is no button in it, it will output click undefined.

That is correct, how can this be fixed?

JerryYangKai commented 1 year ago

I think in Teams, by design in a tab can not control the experience out of this tab.

wrharper-AASP commented 1 year ago

I think in Teams, by design in a tab cannot control the experience out of this tab.

I can do navigation to the other tab within the current tab, but that causes new issues.

  1. It confuses users because they are in another tab by looking at the page but on the top when they are not.
  2. Users have to click on another tab and click back to refresh that tab.

This is needed due to an error check on authorization so they can change a setting that is on another tab requiring the fix and go back.

wrharper-AASP commented 1 year ago

in TeamsJsBlazorInterop.js i found:

export function navigateToTab(tabInstance) {
    return microsoftTeams.pages.tabs.navigateToTab(tabInstance);
}

so I tried to call it: Console.WriteLine(await JsRuntime.InvokeAsync<string>("navigateToTab", "index2"));

but get error: Microsoft.JSInterop.JSException: Could not find 'navigateToTab' ('navigateToTab' was undefined).

I tried creating a javascript that you would use normally:

function getQuery()
        {
                const querystring = window.location.search;
                const urlParams = new URLSearchParams(queryString);
                return urlParams.get('redirect');
        }

you get an error saying querystring is empty even though i used a param: NavigationManager.NavigateTo("/tab3?redirect=tab1", true);

if you use top.location.search(which would work), you get this error: Blocked a frame with origin from accessing a cross-origin frame

Every way that would normally work isn't possible. How can this be done? The closest direction to the right answer I would think is TeamsFx blazor interop from the first error. This should be found, something is missing...

JerryYangKai commented 1 year ago

The first error I think you may try to add this function in \Interop\TeamsSDK\MicrosoftTeams.cs Then include it '@inject MicrosoftTeams MicrosoftTeams;' in Welcome.razor

wrharper-AASP commented 1 year ago

I created:

public Task<string> NavigateToTab(string tabInstance)
    {
        return InvokeAsync<string>("navigateToTab", tabInstance);
    }

Tried to use it: Console.WriteLine(MicrosoftTeams.NavigateToTab("index2")); Error: Microsoft.JSInterop.JSException: The library has not yet been initialized

Interesting since I was able to call IsInTeams before this?

wrharper-AASP commented 1 year ago

At this point you can easily see how crazy it is just to do something that should be very easy to do. This has been 5 or so different attempts to go around the problem and none of them work so far. It shouldn't be this complicated.

hund030 commented 12 months ago

@wrharper-AASP The error message Microsoft.JSInterop.JSException: The library has not yet been initialized indicated that you had not initialize Teams JS SDK before invoke navigateToTab function. Following may work.

protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await base.OnAfterRenderAsync(firstRender);

        if (firstRender)
        {
            await MicrosoftTeams.InitializeAsync();
            await MicrosoftTeams.NavigateToTab(tabInstance);
        }
    }

You can find the TabInstance interface definition here.

While as far as I know, navigateToTab only works for channel tab or group tab. If you want to navigate between personal tab, you may want to use microsoftTeams.pages.navigateToApp();. Reference.

Here's an example: https://github.com/hund030/BlazorNavigateToTab/blob/main/MyTeamsApp5/wwwroot/js/TeamsJsBlazorInterop.js#L33 https://github.com/hund030/BlazorNavigateToTab/blob/main/MyTeamsApp5/Interop/TeamsSDK/MicrosoftTeams.cs#L38 https://github.com/hund030/BlazorNavigateToTab/blob/main/MyTeamsApp5/Pages/Tab.razor#LL22C50-L22C50

wrharper-AASP commented 12 months ago

This looks like it may work. I'll test it when I get a chance, thanks.

EDIT: where does the app ID hide in the files?

wrharper-AASP commented 12 months ago

I would expect microsoftTeams.app.id to exist but it doesn't appear to be possible to get this anywhere? It should be possible to get its own id? In the manifest it's "{{state.fx-resource-appstudio.teamsAppId}}", how can this be called outside of the manifest?

wrharper-AASP commented 12 months ago

I fixed another problem. I noticed TeamsContext exists but it always comes back with a bunch of null info. Here is the correct way to capture it's info:

public class Context
    {
        public App app { get; set; }
        public Page page { get; set; }
        public User user { get; set; }
        public Sharepointsite sharePointSite { get; set; }

        public class App
        {
            public string locale { get; set; }
            public string sessionId { get; set; }
            public string theme { get; set; }
            public int iconPositionVertical { get; set; }
            public string parentMessageId { get; set; }
            public long userClickTime { get; set; }
            public string userFileOpenPreference { get; set; }
            public Host host { get; set; }
            public string appLaunchId { get; set; }
        }

        public class Host
        {
            public string name { get; set; }
            public string clientType { get; set; }
            public string sessionId { get; set; }
            public string ringId { get; set; }
        }

        public class Page
        {
            public string id { get; set; }
            public string frameContext { get; set; }
            public string subPageId { get; set; }
            public bool isFullScreen { get; set; }
            public bool isMultiWindow { get; set; }
            public string sourceOrigin { get; set; }
        }

        public class User
        {
            public string id { get; set; }
            public string licenseType { get; set; }
            public string loginHint { get; set; }
            public string userPrincipalName { get; set; }
            public Tenant tenant { get; set; }
        }

        public class Tenant
        {
            public string id { get; set; }
            public string teamsSku { get; set; }
        }

        public class Sharepointsite
        {
            public string teamSiteUrl { get; set; }
            public string teamSiteDomain { get; set; }
            public string teamSitePath { get; set; }
            public string mySitePath { get; set; }
            public string mySiteDomain { get; set; }
        }
    }
wrharper-AASP commented 12 months ago

is there a reason why this wouldn't work? this should bypass the appid problem:

export function navigateToPage(pageId) {
    microsoftTeams.pages.currentApp.navigateTo(pageId)
}

However, when i integrate and run it, no errors happen and nothing changes.

The reason why navigateToApp won't work is because the published id is also different than the local. so, it's not a good way to handle it and it can change with custom publishes as well. there needs to be a way to automatically get the appid from within the app.

hund030 commented 12 months ago

where does the app ID hide in the files?

You can find the TeamsAppId from .fx/states/state.{envName}.json. state.local.json is for local and by default state.dev.json is for published, if you are using Teams Toolkit to provision the remote resources. You can set the value to your environment variables and reference it from your code. I think .NET supports multiple environments by appsettings.json file. You can set your local TeamsAppId in appsettings.Development.json and set published TeasmAppId in your Azure App Service's appsettings.

Here's an example of using Teams Toolkit to automatically update Azure App Service's appsettings: Update .fx/configs/azure.parameter.dev.json as following:

"parameters": {
        "provisionParameters": {
            "value": {
                ...
              "teamsAppId": "{{state.fx-resource-appstudio.teamsAppId}}" 
            }
        }
    }

Update bicep files:

@secure()
param provisionParameters object // Teams Toolkit pass the provisionParameters from `azure.parameter.dev.json` to Bicep engine.
var teamsAppId= provisionParameters['teamsAppId'] //

// You can find the webApp resources in your bicep file, add teamsAppId into its appSettings
resource webApp 'Microsoft.Web/sites@2021-02-01' = {
  kind: 'app'
  location: resourceGroup().location
  name: webAppName
  properties: {
    serverFarmId: serverfarm.id
    siteConfig: {
      appSettings: [
        .......
        {
          name: 'TEAMS_APP_ID'
          value: teamsAppId
        }
      ]
    }
  }
}

After that, re-run the provision command to apply the bicep changes.

is there a reason why this(navigateToPage) wouldn't work?

According to the document microsoft-teams-js-pages-tabs-navigatetotab, navigateToPage accept a tabInstance object. I didn't try but I assume you may want to write code like:

microsoftTeams.app.initialize();
var pageInfo = microsoftTeams.pages.tabs.getTabInstances(); // get tab instance of current channel or group.
microsoftTeams.pages.tabs.navigateToTab(pageInfo.teamTabs[1]);

To invoke these functions in Blazor, you may need to create a PageInfomation class and a TabInstance class for type annotation. Just as you have done for Context class.

wrharper-AASP commented 12 months ago

for some reason, getTabInstances comes back empty.

wrharper-AASP commented 12 months ago

What if i publish to the store? would it then be another id? this seems like the wrong direction to go.

According to the docs, currentApp should exist and this would be the right path to do this: https://learn.microsoft.com/en-us/javascript/api/@microsoft/teams-js/pages.currentapp?view=msteams-client-js-latest Can we get this fixed instead of trying to find ways around it?

So far, all attempts don't work or end up creating a chain of events of more problems.

The 2 possible solutions to get this fixed: 1, fix getTabInstances so it returns what it should per docs.

  1. fix currentApp so you can use navigateTo per docs.

These features need to be fixed because it really puts limits on possibilities for everyone.

wrharper-AASP commented 11 months ago

any update on currentApp and getTabInstances objects not working? We need a proper fix instead of this: image

hund030 commented 11 months ago

@wrharper-AASP Sorry for delay response.

  1. Publish to store will not change the teams app id. It's hard code in your manifest.{envName}.json and Teams Toolkit make sure the value in your env file is consistent with the manifest. So I think this is an acceptable solution.
  2. While I agree that if we can get teams app id from teams-js SDK, it would be a better way. I think you may want to file another GitHub issue for the SDK team for the issue of currentApp and getTabInstances. Thanks for your patience.