Nativescript - Android TabView back button custom navigation - nativescript

In an Nativescript application I need to implement a custom navigation scenario for Android when user click on material/soft back button.
For simplicity, starting with login-tabs template (https://github.com/ADjenkov/login-tabs-ng) and I want implement a navigation like Instagram, Facebook, WhatsApp, Pinterest, and many more ...
That's with the example of login-tabs template, when I navigate from the Players tab to the Teams tab and then I tap the back button I want to return to the Players tab (on the page I left in this tab).
Today as the navigation history of the Teams tab outlet is empty and the navigation history of the root outlet is empty, the application is closes. I wish it was close if I tap on the back button after returning to the Players tab and if navigation history of Players tab is empty.
I hope it's clear, tell me if it's not the case.
Is there a way to implement this behavior?

Finally I implemented a solution that's inspired by the response of #Manoj.
I listen to the activityBackPressed event and set args.cancel = true for prevent default behavior.
At each Tab change I save the Tab previously visited. Then at every activityBackPressed event I check if the current outlet can go back or not with this.routerExtension.canGoBack({ outlets: [this.tabVisibleName], relativeTo: this.activeRoute }).
If not I return to the previous tab programmatically if the list of tabs visited is not empty. If the list of tabs visited is empty I set args.cancel = false for exit the app.
If this.routerExtension.canGoBack({ outlets: [this.tabVisibleName], relativeTo: this.activeRoute }) return true I simply go back : this.routerExtension.back({ outlets: [this.tabVisibleName], relativeTo: this.activeRoute });
Note : you must remove listener when application is going to background, otherwise you will have several listeners (one by resume) :
application.on(application.exitEvent, (args) => {
if (args.android) {
application.android.off(application.AndroidApplication.activityBackPressedEvent);
}
});
Thanks for your help

You need to save selected Tab in your data.service and when user go back to tabs.component.html you can use the selectedIndex. You can skip to listen to activityBackPressed as well in that case.
in your tabs.component.html
<TabView (selectedIndexChanged)="onSelectedIndexChanged($event)" [(ngModel)]="selectedTabIndex" width="100%">
and in your tabs.component.ts
constructor(
private routerExtension: RouterExtensions,
private activeRoute: ActivatedRoute,
private _dataService: DataService ) {
this.selectedTabIndex = this._dataService.selectedTabIndex;
}
and
onSelectedIndexChanged(args: SelectedIndexChangedEventData) {
const tabView = <TabView>args.object;
const selectedTabViewItem = tabView.items[args.newIndex];
this._dataService.selectedTabIndex = args.newIndex;
}

Related

Pressed state of trumbowyg toolbar buttons not being read out by NVDA but are being read out by VoiceOver

I would like the pressed state of the trumbowyg toolbar buttons (bold/italic etc) to be read out by NVDA screen reader. I have implemented the aria-pressed solution, which works fine for VoiceOver; it reads out select/deselect when the buttons have been selected and deselected, however not for NVDA:
function addValuesToTextEditorButtons() {
const toggleButton = element => {
// Check to see if the button is pressed
var pressed = (element.getAttribute("aria-pressed") === "true");
// Change aria-pressed to the opposite state
element.setAttribute("aria-pressed", !pressed);
}
const handleBtnKeyDown = event => {
// Prevent the default action to stop scrolling when space is pressed
event.preventDefault();
toggleButton(event.target);
}
var buttons = $('.trumbowyg-button-pane .trumbowyg-button-group button[type="button"]');
buttons.each(function (index, element) {
let title = element.title.split(' ')[0]
element.value = title
element.setAttribute('aria-label', title)
element.setAttribute('aria-pressed', false)
element.setAttribute('role', 'button')
element.addEventListener('click', event => {
handleBtnKeyDown(event)
})
element.removeAttribute('tabindex')
});
}
First off, verify that the element you're setting aria-pressed on is a real button (or role='button'). It looks like that's true from your code snippet but would be the first thing to verify. ARIA attributes are only valid on certain elements. (See https://www.w3.org/TR/html53/dom.html#allowed-aria-roles-states-and-properties)
Some screen readers might still announce the value of the attribute even if it's not valid so sometimes that explains why one SR works (such as VO) whereas another does not (NVDA).
I've used aria-pressed successfully with all screen readers without a problem. For NVDA, it will announce the element as a "toggle button" and will say "pressed" or "not pressed" depending on the value.

How to Unmount a screen when moving to another in React Native

I'm developing a React Native app using React Navigation v4, React Hooks and ES6.
I have 2 bottom tabs (Movies, Shows) and 4 screens with the following Stack structure:
**Movies**
-- MovieList
-- MovieDetail
**Shows**
-- ShowList
-- ShowDetail
My scenario
1) Moving from Movie list to an individual movie page
MovieList contains a list of movies, when I click on one of them, I first fetch some API data then move to the MovieDetail screen like this
dispatch(apiFetchActions.fetchMovies(movieId)).then((response) => {
props.navigation.navigate({
routeName: "MovieDetail",
params: {
assetId: movieId,
assetName: movieTitle,
},
});
MovieDetail is now on top of the Movies stack and MovieList at the bottom
2) Moving to a different tab (navigation stack)
I then click on Shows (2nd Tab) which takes me to the ShowList using props.navigation.navigate('ShowList')
3) The problem
If I click on the Movies Tab, I expect to be moved back to MovieList but since MovieDetail was never unmounted, it is still at the top of the Movies stack meaning that I see an old screen. I have to click twice to go back to the MovieList screen.
I've read quite a few suggestions on how to use onFocus/onBlur subscription however I could not found a solution using React Hooks.
My ideal solution would be to find a way to listen to the onBlur status in MovieDetail screen possibly using useEffect hook and somehow unmount it before leaving.
I found a way to make it easier to always move to the initial top of the stack when you click on any bottom tab icons.
You simply need to add the on Press and the screen reference like this
Stars: {
screen: StarsNavigator,
navigationOptions: ({ navigation }) => ({
tabBarIcon: (tabInfo) => {
return (
<Ionicons name="ios-people" size={22} color={tabInfo.tintColor} />
);
},
tabBarLabel: Platform.OS === "android" ? <Text>Stars</Text> : "Stars",
tabBarOnPress: () => {
navigation.navigate("StarsList");
},
}),
},
Star is one of my screens in BottomTabNavigator and using navigation.navigate("You Screen") does the trick. So regardless in which level of the stack you find yourself, every time you click on the Star tab you always end up to the original top level.

How to get the current tab's history in a Web Extension in Firefox?

Is there an API that makes it possible to get the current tab's history in a Web Extension in Firefox? Just like when clicking and holding on the Back button, a dropdown will appear to show the current tab's history.
No. You cannot ask for the list for a certain tab by default.
You can, however, listen for the tab events onUpdated, onCreated etc. Using the tabId which stays the same, you can keep a list of URLs in a background script (background.js) which is always running if the addon is enabled.
You would do it like this:
let arr=[]; // At the top of background.js
browser.tabs.onCreated.addListener(handleCreated); // Somewhere in background.js
function handleCreated(tab) {
let tabId = tab.id;
if(arr[tabId]==null) arr[tabId] = [];
arr[tabId].push(url);
}
function getHistoryForCurrentTab(){
function currentTabs(tabs) {
// browser.tabs.query returns an array, lets assume the first one (it's safe to assume)
let tab = tabs[0];
// tab.url requires the `tabs` permission (manifest.json)
// We will now log the tab history to the console.
for(let url of arr[tab.id]){
console.log(url);
}
}
function onError(error) {
console.log(`This should not happen: ${error}`);
}
browser.tabs.query({currentWindow: true, active: true}).then(currentTabs, onError);
}
The above code is a proof of concept. Some improvements you will need to consider: implement onClosed which resets the tab history for that id (arr[tabId] = null), implement onUpdated (will be needed for sure, same logic as in handleCreated).
Links:
https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/tabs

How to get handle of popup window (WebdriverIO)

I am very very new to automated testing and I am currently completely stuck with the following issue:
I have a webpage open(first window)
In the same test I call a .newWindow(second window) and do some stuff in that window. The last action opens new popup window(popup window).
What I need, is to set the focus on a popup window.
According to WebdriverIO API I can use .switchTab http://webdriver.io/api/window/switchTab.html
But to be able to switch to a popup window I have to indicate handle, but I don't understand how to get the handle of a popup window :(
That s my piece of code:
//this is the part where I have already second window open
it('should open email letter', function(done) {
client
.pause(2000)
.clickAndWait('[title="Password restore"]', 4000)
.clickAndWait('[title="Restore password"]', 7000) //this is the part where popup window opens
.pause(2000)
.windowHandles(function(err,res){
console.log(res, handles)
}) // I have got three handles but i dont know how to use them now
.........
There is a lot of examples in java, but i didnt find anything that would fit mine language.
Please, excuse me my dumbness, I am really a very beginner and I will appreciate if somebody could explain that to me.
Thanks a lot in advance!
we not use getCurrentTabId to remember the handle of the currently open window?
For example:
var main, popup; //your window handles
client
.getCurrentTabId(function (err, handle) {
main = handle;
})
.newWindow('http://localhost:9001/') //you are on popup window
.getCurrentTabId(function (err, handle) {
popup = handle;
})
.switchTab(main) //you are back to main window
.switchTab(popup) //you are on popup again
.close(popup) //extra bonus!
I notice you stated "The last action opens new popup window(popup window). What I need, is to set the focus on a popup window."
I had this issue. But the new window was opened when clicking on login with facebook. This caused an issue on finding the handle for the new window because I could not use .newWindow('http://localhost:9001/'). The API keys and all sorts are added as parameters when using a social login. So one has little control
To handle this I registered each window ID as its opened.
The first background step in my feature is Given I open the url "/a.html"
In the step you can set an empty array as the variable windowID with let let windowID = []
So my step file would look like this
const url = 'http://localhost:8080'
let windowID = []
this.Given(/^I open the url "([^"]*)"$/, (path) => {
browser
.url(url + path)
console.log(`# Navigating to ${url + path}`)
expect(browser.getUrl()).toEqual(url + path)
windowID.main = browser.getTabIds()
});
During the step after clicking the Facebook button you can then check all the open window ID's and drop the one that matches windowID.main
this.When(/^I authenticate with facebook$/, function (arg1) {
// log the ID of the main window
console.log('Main Window ID' + windowID.main)
browser
.pause(2000)
.getTabIds().forEach(function (value) {
if (value === windowID.main) {
// we do not need to do anything with windowID.main as it's already set
return
}
// if the value does not match windowID.main then we know its the new facebook login window
windowID.facebook = value
})
// log both of these
console.log('Main Window ID: ' + windowID.main)
console.log('Facebook Window ID: ' + windowID.facebook)
// Do the login
browser
.switchTab(windowID.facebook)
.setValue('input[name="email"]', process.env.FACEBOOK_EMAIL)
.setValue('input[name="pass"]', process.env.FACEBOOK_PASSWORD)
.submitForm('form')
});
note that I add credentials as an environment variable. This is a good idea, you don't want to commit your personal credentials to the code base. One may think well obviously, but you may not, who knows.
You had your question answered years ago, but I found this post first when trying to find a solution so it seems a sensible place to put this addition.

jQuery click event behaves differently with live function in Firefox

Using the event click with live function leads to strange behavior when using Firefox*.
With live in Firefox, click is triggered when right-clicking also! The same does not happen in Internet Explorer 7 neither in Google Chrome.
Example:
Without live, go to demo and try right clicking
the paragraphs. A dialog menu should
appear.
With live, go to demo and try right
clicking "Click me!". Now both dialog
menu and "Another paragraph" appear.
*tested with firefox 3.5.3
As far as I know, that is a known issue (bug?). You can easily work around it by testing which button was clicked as follows:
$('a.foo').live("click", function(e) {
if (e.button == 0) { // 0 = left, 1 = middle, 2 = right
//left button was clicked
} else {
//other button was clicked (do nothing?)
//return false or e.preventDefault()
}
});
you might prefer using a switch depending on your specific requirements, but generally you would probably just want to do nothing (or or simply return) if any button other than the left button is clicked, as above:
$('a.foo').live("click", function(e) {
switch(e.button) {
case 0 : alert('Left button was clicked');break;
default: return false;
}
});
I think it's a known "bug", you could potentially query the event object after attaching the click handler ( which gets attached to the document ) and see if its a right click, otherwise manually attach the click handler after you manipulate the DOM.
After looking it up, e.button is the property you want to query:
.live('click', function(e){
if ( e.button == 2 ) return false; // exit if right clicking
// normal action
});
See my answer here: if you don't mind changing the jQuery source a bit, adding a single line in the liveHandler() works around the problem entirely.

Resources