StackNavigation don't add screen to stack React Native - ios

I have the current flow of screens.
Home -> Info -> Log in -> Camera.
The issue is that I don't want to add Login to the stack because then when you go back from the camera you will go back to login, rather than information.
If I use this.props.navigate('Info') from the camera, then the issue is that back now goes to the camera rather than home from info. Note I also don't want to complete delete the stack at the login page as I've seen with the Reset function here (https://reactnavigation.org/docs/navigators/navigation-actions#Reset).
Ideally what I would like to do would be:
Home -> Info -> Login (Not added to stack) -> Camera.
This was I can just go back from Camera to Info and from Info to Home.
Has anyone found a nice way around this?
Thanks

If you want to retain the whole stack and params, only ditching the Login page, then you could use Reset Navigation Action either in the Camera page :
import { NavigationActions } from 'react-navigation'
const resetAction = NavigationActions.reset({
index: 1,
actions: [
NavigationActions.navigate({ routeName: 'Home', params: this.props.navigation.state.params })
NavigationActions.navigate({ routeName: 'Info', params: this.props.navigation.state.params })
]
})
this.props.navigation.dispatch(resetAction)
This way you will:
Navigate to Info page on top of Home page, meaning that Back button will lead to Home page.
Retain the params, if you need. If you don't just do not pass them.
This functionality could be added anywhere: inside the components / inside the Action Creators / inside the Back button.
Or add the same code for the Back button. In this case you need to add this to the navigationOptions of Camera route:
const backButton = ({ navigation }) => ({
headerLeft: <TouchableOpacity
onPress={() => {
// the code from above, except for import line and using navigation.whatever instead of this.props.navigation.whatever
}}
><Text>Back</Text> // this is a text of a back button, you could also use an icon
</TouchableOpacity>
});
Navigator:
const MyStack = StackNavigator({
home: { screen: Home },
info: { screen: Info },
login: { screen: Login },
camera: { screen: Camera }, navigationOptions: backButton });

With v6 of Navigation, CommonActions.reset might might work for you:
import { useNavigation,CommonActions } from "#react-navigation/native";
...
const navigation = useNavigation();
...
navigation.dispatch((state) => {
// We can keep all routes on the stack except that which has name "Login"
const routesWithoutLogin = state.routes.filter((r) => r.name !== "Login");
// We can then define the new route we want
const routeToAdd = {
name: "Info",
params: {},
};
const newRoutes = [...routesWithoutLogin, routeToAdd];
return CommonActions.reset({
...state,
routes: newRoutes,
index: newRoutes.length - 1,
});
});
Reference here: https://reactnavigation.org/docs/navigation-actions/#reset
When debugging, I find it quite useful to log my routes state to see what needs changing.
import { useNavigation,CommonActions } from "#react-navigation/native";
...
const navigation = useNavigation();
...
console.log(navigation.getState().routes);
It should look something like this
[{"key": "Root-YhPzpMumgvLs62ZwxjOJW", "name": "Root", "params": undefined}, {"key": "Chat-sDA1-4R0fImaxaj9maXGb", "name": "Chat", "params": {"conversation": [Object]}, "path": undefined}]
One more note, when I tried creating routes from scratch, I lost some of my redux state. It seems like a better idea to filter out routes you don't want than to make them from scratch.

Related

navigator.mediaSession.metadata not updating after page reload

I am currently tying our audio player with mediaSession.
Everything is working as it should, when I hit play and update navigator.mediaSession.metadata, it's properly displayed in the notification on desktop and mobile.
But after I reload the page and hit play, the notification has always default values (website URL as a title and link rel="icon" for the artwork). This only happens after I reload the website. If I close it and open again the notification is working properly again.
Here's how it's done:
//...
initialConfiguration: {
title: 'Initial Title',
artist: 'Initial Artist',
album: '',
artwork: [
{ src: "initial/artwork/url.jpg", sizes: "512x512", type: "image/jpg" },
]
},
currentMetadata: null,
setMediaSessionMetaData: function(){
let self = this;
if ('mediaSession' in navigator) {
if( !self.currentMetadata ){
self.currentMetadata = new MediaMetadata(self.initialConfiguration);
}else{
// Update existing metadata
self.currentMetadata.title = "New Title";
self.currentMetadata.artist = "New Artist";
self.currentMetadata.artwork = [
{ src: "new/artwork/url.jpg", sizes: '512x512', type: "image/jpg" },
];
}
navigator.mediaSession.metadata = self.currentMetadata;
}
},
//...
This function works perfectly fine on first page load, when I hit play for the first time it loads the initialConfiguration and if I call the function again the title and artwork gets updated. But after reload, the notification has always default values ignoring my configuration.
Is there a bug in mediaSession, I didn't find anything regarding this issue on mediaSession github page (https://github.com/w3c/mediasession/issues) and searching this issue gives me zero results.
After Searching for days and not finding anything....
I discovered a workaround. It has to do with the performance.navigation.type when you first open a page it is set to 0 and after a refresh it gets set to 1. I was curious if this had any effect on the mediasession so I figured out how to "Hard Refresh" the page by using window.open(window.location.href, "_self");, because apparently that generates a "Hard Refresh". After that the mediasession works again. so what I did was setup an onload function that checks to see if performance.navigation.type == 1 and if so then do a "Hard Refresh"
window.onload = function() {
if (performance.navigation.type != 0) {
window.open(window.location.href, "_self");
}
};
Now when the user refresh's it will do a "Hard Refresh".
Also make sure to move the window. Onload line to the end of the javascript file or after the initial function

flutter: SFSafariViewController doesn't open like a native iOS modal

In order to use SFSafariViewController in my Flutter app, I am using this package: flutter_inappwebview: ^3.3.0+3
While this works, there is a bug in the animation through which the SFSafariViewController appears.
Notice this:
Actual Behaviour
Expected Behaviour
Notice how in the second GIF, the app itself is pushed back, whereas in the first GIF, we can see a semi-transparent view created instead of the native push back animation for the app.
Here's the code that I am using:
final ChromeSafariBrowser browser =
MyChromeSafariBrowser(MyInAppBrowser());
await browser.open(
url: 'https://google.com',
options: ChromeSafariBrowserClassOptions(
ios: IOSSafariOptions(
barCollapsingEnabled: true,
presentationStyle: IOSUIModalPresentationStyle.POPOVER)));
Here's the Gist for the MyChromeSafariBrowser and MyInAppBrowser class.
Can you please help me with this?
You should submit an issue to flutter_inappwebview. The package uses a native modal presentations and it looks like it is not doing it correctly.
If you want to use the modal_bottom_sheet package as the gif you attached, then you can't use ChromeSafariBrowser, and you would need to use InAppWebView instead.
ChromeSafariBrowser is a native activity outside Flutter while InAppWebView is a native view wrapped inside Flutter.
InAppWebView(
initialUrl: "https://flutter.dev/",
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
debuggingEnabled: true,
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
setState(() {
this.url = url;
});
},
onLoadStop: (InAppWebViewController controller, String url) async {
setState(() {
this.url = url;
});
},
onProgressChanged: (InAppWebViewController controller, int progress) {
setState(() {
this.progress = progress / 100;
});
},
),
This was fixed in the release v4.0.0 and it is now possible to get the expected behaviour by setting
presentationStyle: IOSUIModalPresentationStyle.POPOVER
More information here: https://pub.dev/packages/flutter_inappwebview#-changelog-tab-

close current window by submenu item in ElectronJs

I have two windows opened as you see in this picture:
screenshot
I want to close smaller window by clicking on "close" submenu.
here's my code:
const addMenuTemplate = [
{
label:'file',
submenu:[
{
label:'close',
click(){
//i dont know what to put here
}
}
]
}
]
what should I do ?
I've already test electron.remote.getCurrentWindow().close()
If you want to close the current window, you don't have to write a custom click handler. The close role of MenuItem will handle it for you.
const template = [
// ...
{
label: 'my-sub-menu',
submenu: [
{ role: 'close' },
{ role: 'quit' },
]
}
// ...
]
See a live example Gist openable in Electron Fiddle.
If you have the window stored in a variable you can use win.close().
And it will close the window.

Ionic IOS & Firebase (White Screen of Death)

I've hit a very difficult bug so if you're up for a challenge, read on. :)
Steps to recreate what I'm seeing:
Deploy my app to a device. (this bug never happens on emulate, serve, or Xcode)
Leave the app running in the background for an undetermined amount of time (usually a day or so).
Sometimes when opening the app again you'll get a white screen of death.
I have a feeling that something is happening to the firebase connection and when the app opens it can't resolve $requireAuth() in the route.
Any help would be appreciated. Is there anyway issues like that can get logged somewhere?
Here is part of my route file:
$urlRouterProvider.otherwise(function ($injector) {
var $state = $injector.get("$state");
$state.go("app.dashboard");
});
$stateProvider.state('app', {
url: '/app',
abstract: true,
templateUrl: 'skinsaver/app/app.html',
controller: 'AppController as app',
resolve: {
"userAuth": ["AppAuth", resolveUser]
},
});
$stateProvider.state('app.dashboard', {
url: '/dashboard',
templateUrl: 'skinsaver/app/dashboard/dashboard.html',
controller: 'DashboardController as dashboard',
});
function resolveUser(AppAuth){
return AppAuth.firebaseAuth.$requireAuth();
};
//watch for route errors to redirect to login
$rootScope.$on('$stateChangeError', function(event, toState, toParams, fromState, fromParams, error) {
if(error === 'AUTH_REQUIRED'){
event.preventDefault();
AppAuth.returnToState = toState;
$state.go("public.login");
}
});

Blank Firefox addon panel page with multiple windows

I've followed MDN's document to create a toggle button addon.
Everything works fine except one problem:
Open a second browser window (cmd+n or ctrl+n) and click on the toggle button there
Click on the toggle button on the original browser window without closing the toggle button on the second window
the toggle button's panel becomes blank with the following error message:
JavaScript error: resource:///modules/WindowsPreviewPerTab.jsm, line 406: NS_ERR
OR_FAILURE: Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsIT
askbarTabPreview.invalidate]
// ./lib/main.js
var { ToggleButton } = require("sdk/ui/button/toggle");
var panels = require("sdk/panel");
var self = require("sdk/self");
var buttonIndex = 0;
var lastKnownButtonIndex = 0;
var button = ToggleButton({
id: "button",
label: "my button",
icon: {
"16": "./icon-16.png"
},
onClick: handleChange,
});
var panel = panels.Panel({
contentURL: self.data.url("menu.html"),
onHide: handleHide
});
function handleChange(state) {
if (state.checked) {
panel.show({
position: button
});
}
}
function handleHide() {
button.state('window', {checked: false});
}
function assignButtonIndex() {
return (buttonIndex++).toString();
}
The complete addon is here: https://goo.gl/9N3jle
To reproduce: Extract the zip file and $ cd testButton; cfx run and follow the above steps.
I really hope someone can help me with this. Thank you in advance!
It's a bug; you're not doing anything wrong. It's a racing condition between the windows' focus events, and the panel's event, that prevent somehow the panel's hidden event to be emitted properly.
You can try to mitigate with a workaround the issue until is properly fixed. I added some explanation in the bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1174425#c2 but in short, you can try to add a setTimeout to delay a bit when the panel is shown, in order to avoid the racing condition with the window's focus. Something like:
const { setTimeout } = require("sdk/timers");
/* ... your code here ... */
function handleChange(state) {
if (state.checked) {
setTimeout(() => panel.show({ position: button }), 100);
}
}
I am currently using a workaround where I dynamically create a new Panel every time the user presses the toolbar button.
It is faster than the 100ms workaround and also handles a scenario where the user outright closes one of the browser windows while the panel is open. (The 100ms workaround fails in this case and a blank panel is still displayed.)
It works like this:
let myPanel = null;
const toolbarButton = ToggleButton({
...,
onChange: function (state) {
if (state.checked) {
createPanel();
}
}
});
function createPanel(){
// Prevent memory leaks
if(myPanel){
myPanel.destroy();
}
// Create a new instance of the panel
myPanel = Panel({
...,
onHide: function(){
// Destroy the panel instead of just hiding it.
myPanel.destroy();
}
});
// Display the panel immediately
myPanel.show();
}

Resources