I'm running a React.js/Cordova/OnsenUI application that is intended to be used both in the browser and on mobile devices. I'd like the user to be able to scan a QR code, then jump to a screen in my application.
This is what the application looks like right now:
import React from 'react';
import {
Navigator
} from 'react-onsenui';
import MainPage from './MainPage';
import Vendor from './Vendor';
const renderPage = (route, navigator) => (
<route.component key={route.key} navigator={navigator} />
);
const App = () => (
<Navigator
renderPage={renderPage}
initialRoute={{component: MainPage, key: 'MAIN_PAGE'}}
/>
);
export default App;
When I start up, depending on the URL, I might want to start with a Vendor component or a MainPage component.
I figured that the easiest thing to do would be to dynamically create the initialRoute object based on the QR code that was scanned. Given that I might be on an iOS device, how do I know what the URL was that was scanned? Is there a different way that I should be jumping to a specific screen when I start the app?
Related
I'm looking for a little guidance and suggestions here. My attempts and theories will be at the bottom.
I have a NextJS project from which I want to export the top level component (essentially the entry file) so that I can use it as a preview in my dashboard.
The nextjs project is very simple. For the sake of simplicity, let's imagine that all it renders is a colored <h1>Hello world</h1>. Then in my dashboard, I want to render a cellphone with my NextJS component embedded and then from the dashboard change the color of the text, as a way to preview how it would look like. I hope this makes sense.
I'm lost at how I could export this component from NextJS and import it into my dashboard. The dashboard is rendered in Ruby on Rails. It would be simple enough to just import the repo from git and access the file directly form node_modules, but I'm looking for a solution that doesn't require installing npm on our Rails project.
Paths I have thought about:
1 - Install npm on Rails and just import the source code from NextJS repo and access the file and render with react (Simple, but we're looking for a non-npm solution)
2 - Bundle the component with webpack and load it directly into rails (does this even work?) - I exported the js and all it did was freeze everything :P Still trying this path for now
3 - Using an iframe and just accessing the page (then I can't pass any callbacks into the iframe to change the color directly from the dashboard)
4 - I cannot separate this component from NextJS to use as a library in both repos. The component we are exporting is the "ENTIRE" NextJS app jsx and it wouldn't make sense to separate in a different repo
Does anyone have a suggestion on how I could achieve this?
I think you could use an iframe with the nextjs app url. Then if you want to change the color, simply add the color in query parameter of the iframe and handle it on nextjs app.
Simple example
Rails view (erb)
<iframe src="#{#nextjs_url}?color=#{#color}" />
NextJS
# do something to get the query param of the page and and set to prop of the component
const YourComponent = ({color}) => {
return <h1 style={{color}}>Lorem</h1>;
}
While trying Hoang's solution, I decided to dive deeper into how to communicate with an iframe and the solution actually feels quite good.
You can set up listeners on either side and post messages in between the projects.
So in my dashboard:
function handleEvent(e) {
const data = JSON.parse(e.data)
if (data.type === "card_click") {
//if type is what we want from this event, handle it
}
}
// Setup a listener with a handler
// This will run every time a message is posted from my app
window.addEventListener("message", handleEvent, false)
const postMessage = (color) => {
const event = JSON.stringify({
type: "color_update",
color,
})
// Find the iframe and post a message to it
// This will be picked up by the listener on the other side
document.getElementById("my-iframe-id").contentWindow.postMessage(event, "*")
}
And on my app:
function handleEvent(e) {
const data = JSON.parse(e.data)
if (data.type === "color_update") {
// Do whatever is necessary with the data
}
}
// Setup listener
// This will fire with every message posted from my dashboard
window.addEventListener("message", handleEvent, false)
const handleCardClick = (cardIndex) => {
const event = JSON.stringify({
type: "card_click",
cardIndex,
})
// post message to parent, that will be picked up by listener
// on the other side
window.parent.postMessage(event, "*")
}
It feels pretty straight forward to communicate with an iframe with this solution.
In my NativeScript (Angular) App i use a RadListView to create a list and each element has many different informations to display. It looks like that
Because of many hints at Stackoverflow and other sources i reduced the amount of nested layouts (StackLayout, GridLayout, ...) as much as possible to make the RadListView faster. On Android is the performance by using the list much better as on iOS. With an iPad Pro (2020) the rendering of the list at scrolling is not smooth. If the user change the orientation of the device the screen is freezing and have black bars at the side or bottom for a moment. The time of the freezing depends on the amount of elements to display in each row. The same row layout in a ListView is much faster but not the same as native (SwiftUI) and with missing features like swipe and pull to refresh.
Sorry for the lyric but i think a little background explains why i try the next step.
To improve the user experience i make a tiny native test app with SwiftUI and nearly the same row layout. The feeling is much better, fast first loading, smooth scrolling and no delay by orientation changes. My next idea is to create a native component in SwiftUI to show/render each row of the RadListView if possible
<RadListView [items]="items">
<ListViewLinearLayout tkListViewLayout></ListViewLinearLayout>
<ng-template tkListItemTemplate let-item="item" let-i="index" let-odd="odd">
<MyNativeSwiftUIComponentElement data="item.rowData"></MyNativeSwiftUIComponentElement>
</ng-template>
</RadListView>
or use the List from SwiftUI to show/render the whole list
<ActionBar title="Objects"></ActionBar>
<MyNativeSwiftUIListComponent data="items"></MyNativeSwiftUIListComponent>
Looking for docs and examples was difficult. I found this very short advise Adding Objective-C/Swift code and the linked tutorial there for Objective-C (Adding Objective-C Code to a NativeScript App) and some questions on Stackoverflow but there all about classes and not SwiftUI (with struct and views). One question was about SwiftUI: Is it possible to display a View written with SwiftUI with NativeScript the answer was unfortunately not helpful for me (btw. thank you #Manoj for your great support for NativeScript at Stackoverflow!).
How can i use a SwiftUI View as native component in my {N}app?
Have anyone a hint, a link to a tutorial or a link to a public repository for a app/plugin? Every tiny tip is welcome.
You might be able to use Nativescript's placeholder component (more info on that here
So you would have the Placeholder tag on your template, and use the creatingView event to add the native UIs
<Placeholder creatingView="creatingView"/>
import { CreateViewEventData } from "#nativescript/core";
export function creatingView(args: CreateViewEventData) {
let nativeView = new UILabel(); // where this would be your native UI
nativeView.text = "Native";
args.view = nativeView;
}
After a while i give up with my attempts to use directly SwiftUI in the project ({N}+Angular) and instead i try the <Placeholder> component which #William-Juan suggested. But it looks like, that the <Placeholder> not official supported in the Angular flavor - see github issue #283
To move on, i looked at the samples for NativeScript plugins and build a working solution. If anybody interested the full sample source code are in this repository: https://github.com/teha-at/sample-nativescript-native-ui-component
First, create a class which extends the #nativescript/core/View class and has an item to get the data which will be to display.
// object-list-item.d.ts
// [...]
export class ObjectListItem extends View {
item: ObjectModel;
}
export const itemProperty: Property<ObjectListItem, string>;
Than create a abstract base class which also extends the #nativescript/core/View class and this creates the base for Android and iOS
// object-list-item.common.ts
// [...]
export const itemProperty = new Property<ObjectListItemBase, string>({
name: 'item',
defaultValue: null,
affectsLayout: isIOS,
});
export abstract class ObjectListItemBase extends View {
item: PortalObjectModel;
}
// defines 'item' property on the ObjectListItemBase class
itemProperty.register(ObjectListItemBase);
ObjectListItemBase.prototype.recycleNativeView = 'auto';
Because i was only looking for a component for iOS the object-list-item.android.ts are very simple:
// object-list-item.android.ts
import { ObjectListItemBase } from './object-list-item.common';
export class ObjectListItem extends ObjectListItemBase {}
For iOS there are much more lines, for the complete file content look at the github repo please.
/// object-list-item.ios.ts
// [...]
export class ObjectListItem extends ObjectListItemBase {
// added for TypeScript intellisense.
nativeView: UIView;
// [...]
/**
* Creates new native button.
*/
public createNativeView(): Object {
const mainUiStackView = UIStackView.new();
// [...]
}
/**
* Initializes properties/listeners of the native view.
*/
initNativeView(): void {
// Attach the owner to nativeView.
// When nativeView is tapped we get the owning JS object through this field.
(<any>this.nativeView).owner = this;
super.initNativeView();
}
/**
* Clean up references to the native view and resets nativeView to its original state.
* If you have changed nativeView in some other way except through setNative callbacks
* you have a chance here to revert it back to its original state
* so that it could be reused later.
*/
disposeNativeView(): void {
// Remove reference from native listener to this instance.
(<any>this.nativeView).owner = null;
// If you want to recycle nativeView and have modified the nativeView
// without using Property or CssProperty (e.g. outside our property system - 'setNative' callbacks)
// you have to reset it to its initial state here.
super.disposeNativeView();
}
[itemProperty.setNative](item: ObjectModel) {
this.item = item;
// [...]
}
}
Add an Angular directive
// object-list-item.directives.ts
#Directive({
selector: 'ObjectListItem',
})
export class ObjectListItemDirective {
}
export const ObjectListItemDirectives = [ObjectListItemDirective];
At least register the component in an Angular module.
// object-list-item.module.ts
// [...]
#NgModule({
imports: [],
declarations: [
ObjectListItemDirectives,
],
schemas: [NO_ERRORS_SCHEMA],
exports: [
ObjectListItemDirectives,
],
entryComponents: [],
})
export class ObjectListItemModule {
}
registerElement('ObjectListItem', () => ObjectListItem);
After all this steps call the new component in the template
<!-- [...] -->
<RadListView #myListView [items]="items$ | async">
<ng-template tkListItemTemplate let-item="item">
<StackLayout margin="0" padding="0" class="-separator m-y-5" height="90">
<android>
<!-- [...] -->
</android>
<ios>
<ObjectListItem [item]="item"></ObjectListItem>
</ios>
</StackLayout>
</ng-template>
</RadListView>
<!-- [...] -->
All this work is well spent. The UI is much faster and it feels more like a native app. At the mean time i build a prototype as a native iOS App in Swift and SwiftUI, of course this pure native app are a little bit more smoother, but at the moment i work with my {N}-App and the native component. Hope this sample will be useful for someone.
This isn't exactly a critical bug, but I always feel weird shaking phones at my desk at work, even more so when it doesn't work first time. If we start talking about shaking iPad Pros, it just feels wrong.
On Android, I can run the following command: adb shell input keyevent KEYCODE_MENU
Is there an iOS equivalent?
Thanks
Sadly no.
You can vote for it on Canny here. Until then, your best bet for iOS is to use a workaround such as one of the ones suggested from the original Github issue. For example, creating your own multi-touch shortcut for opening the dev menu as seen here. It's not ideal but it should work. (code copy pasted below)
import React from 'react';
import {
View,
PanResponder,
NativeModules,
} from 'react-native';
const DevMenuTrigger = ({children}) => {
const {DevMenu} = NativeModules;
const panResponder = PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => {
if (gestureState.numberActiveTouches === 3) {
DevMenu.show();
}
},
});
return <View style={{flex: 1}} {...panResponder.panHandlers}>{children}</View>;
};
...
AppRegistry.registerComponent('myApp', (): any => <DevMenuTrigger><MyApp></DevMenuTrigger>
I am trying create a storybook for my react-realy app, but i don't know how to set mockup data for that component. For simple a component it is ok, because i can use dummy UI component vs Container approach, but i can't use this for nested relay components, for example there is a UserList component, which i want add to storybook, i can split relay fragment part to container and UI part to the component, but what if UserList children are too relay component? I can't split their when they are a part of the composition of UserList?
Is there some solution for add relay components to the storybook?
I created a NPM package called use-relay-mock-environment, which is based on relay-test-utils which allows you to make Storybook stories out of your Relay components.
It allows nesting of Relay components, so you can actually make stories for full pages made out of Relay components. Here's an example:
// MyComponent.stories.(js | jsx | ts | tsx)
import React from 'react';
import { RelayEnvironmentProvider } from 'react-relay';
import createRelayMockEnvironmentHook from 'use-relay-mock-environment';
import MyComponent from './MyComponentQuery';
const useRelayMockEnvironment = createRelayMockEnvironmentHook({
// ...Add global options here (optional)
});
export default {
title: 'MyComponent',
component: MyComponent,
};
export const Default = () => {
const environment = useRelayMockEnvironment({
// ...Add story specific options here (optional)
});
return (
<RelayEnvironmentProvider environment={environment}>
<MyComponent />
</RelayEnvironmentProvider>
);
};
export const Loading = () => {
const environment = useRelayMockEnvironment({
forceLoading: true
});
return (
<RelayEnvironmentProvider environment={environment}>
<MyComponent />
</RelayEnvironmentProvider>
);
};
You can also add <RelayEnvironmentProvider /> as a decorator, but I recommend not doing that if you want to create multiple stories for different states/mock data. In the above example I show 2 stories, the Default one, and a Loading one.
Not only that, it requires minimal coding, where you don't need to add the #relay-test-operation directive to your query, and the mocked data is automatically generated for you using faker.js, allowing you to focus on what matters, which is building great UI.
Feel free to review the source code here if you want to implement something similar: https://github.com/richardguerre/use-relay-mock-environment.
Note: it's still in its early days, so some things might change, but would love some feedback!
I also created relay-butler, which is a CLI that takes in GraphQL fragments and outputs Relay components, including a auto-generated query component that wraps the fragment component, and Storybook stories (the Default and Loading ones by default) that wrap that query component. And literally within minutes, I can create beautiful Relay components that are "documented" within Storybook.
Would also love some feedback for it!
Enlargement of size of the device font will sometimes break (Styling wise).
Disabling font scaling can hurt the accessibility of your app, ideally if you want to limit scaling for Apps using React native 0.58.0 and above; use the maxFontSizeMultiplier prop on specific Text components.
However if you absolutely want to disable font scaling across your entire Application, you can do so by globally setting the allowFontScaling prop in the defaultProps of Text.
You should place these lines in your root entrypoint (normally index.js) before AppRegistry.registerComponent.
For React Native 0.56.0+
Text.defaultProps = Text.defaultProps || {};
Text.defaultProps.allowFontScaling = false;
For earlier versions of React Native you should only need the second line, but having both won't hurt. The first line just protects against the Text component not having defaultProps which is the case for React Native 0.56.0 and above.
Add the above lines in the entry point file of your React Native application (usually index.js, app.js or main.js) to apply this prop to all Text components in your application.
This prop will only affect Text components and you may want to apply the same changes to TextInput which can be done with a similar snippet:
TextInput.defaultProps = TextInput.defaultProps || {};
TextInput.defaultProps.allowFontScaling = false;
Also note that some components wont obey font scaling settings, for example: Alert, PickerIOS, DatePickerIOS, TabBarIOS, SegmentedControlIOS as these are all natively drawn and don't rely on the Text component.
For React native 0.58+
Preferable to keep font scaling but you can limit it by using Text new prop maxFontSizeMultiplier
For React native 0.56+ use Levi's answer
Text.defaultProps = Text.defaultProps || {};
Text.defaultProps.allowFontScaling = false;
For React native 0.55 and lower
Add Text.defaultProps.allowFontScaling=false at the beginning of the app (e.g. main.js or app.js etc ...) to apply this prop on all Text components through out the whole app.
When user increase full font size from setting
Enlargement of size of the device font will not break (Styling wise).
index.js file
import {AppRegistry} from 'react-native';
import App from './src/App';
import {name as appName} from './app.json';
import {Text, TextInput} from 'react-native';
AppRegistry.registerComponent(appName, () => App);
//ADD this
if (Text.defaultProps == null) {
Text.defaultProps = {};
Text.defaultProps.allowFontScaling = false;
}
if (TextInput.defaultProps == null) {
TextInput.defaultProps = {};
TextInput.defaultProps.allowFontScaling = false;
}
<CalendarStrip
shouldAllowFontScaling={false}
/>
Also note that some components wont obey font scaling settings, for example: Alert, PickerIOS, DatePickerIOS, TabBarIOS, SegmentedControlIOS as these are all natively drawn and don't rely on the Text component.
if (Text.defaultProps == null) {
Text.defaultProps = {};
Text.defaultProps.allowFontScaling = false;
}
I kept this piece of code inside the constructor of index.js.It really worked well. By the I am using react native version 0.59.9 FYI.
Create an <AppText> component and use it with your presets instead of the original one, with your own default, including font scaling false. This is better because you can enrich it with your own API.
For example, my AppText permit to do things like:
<AppText id="some.translation.key" color="primary" size="l" underline italic bold/>
In another file, import the actual Text component as ScaledText so as a backup, and then redefine Text, overriding the allowFontScaling prop.
export function Text(props) {
return <ScaledText {...props} allowFontScaling={false} />;
}
Then, import your locally defined Text component, instead of the built-in React Native Text. This is also useful if you want to elegantly disable font scaling on only certain parts of your app.
For webview we can use textZoom={100} props to handle font-size change if font size is changed from mobile setting.
if imported from react-native-webview
<WebView
textZoom={100}
source={}/>
I'm kinda late, but if anyone wants a answer with Typescript, here it is
interface TextWithDefaultProps extends Text {
defaultProps?: { allowFontScaling?: boolean };
}
(Text as unknown as TextWithDefaultProps).defaultProps = {
...((Text as unknown as TextWithDefaultProps).defaultProps || {}),
allowFontScaling: false,
};