Navigation in react-native - react-native-navigation

This's the homepage
What I want is when i click on the clock icon at the BottomTabNavigator, i have a new page with the features below:
The Tab navigator will be hidden
A new header
possibility to go back (go to the homepage)
I have already fixed the two first point... The third one makes me confused!
Is there someone who can help me ?
"dependencies": {
"expo": "^31.0.2",
"react": "16.5.0",
"react-elements": "^1.3.1",
"react-native": "https://github.com/expo/react-native/archive/sdk-31.0.0.tar.gz",
"react-native-elements": "^0.19.1",
"react-native-snap-carousel": "^3.7.5",
"react-native-vector-icons": "^6.1.0",
"react-navigation": "^3.0.0"
},
**CODE : **
//Differents Stack Navigators
const AppStackNavigator = createAppContainer(createStackNavigator({
Home: {
screen: HomeScreen,
navigationOptions: {
header: < Head / >
}
},
Search: {
screen: Search,
navigationOptions: {
title: "Rechercher",
headerStyle: {
backgroundColor: '#00aced'
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
}
}
}
}));
const HoraireStackNAvigator = createAppContainer(createStackNavigator({
Horaire: {
screen: Horaires,
navigationOptions: {
title: "Horaires"
}
}
}))
const PaimentStackNAvigator = createAppContainer(createStackNavigator({
Horaire: {
screen: Paiement
}
}))
//The Principle TabNavigator
const TabContainer = createBottomTabNavigator({
Home: {
screen: AppStackNavigator,
},
Paiement: {
screen: PaimentStackNAvigator,
},
Horaires: {
screen: HoraireStackNAvigator,
navigationOptions: {
tabBarVisible: false
}
}
}, {
initialRouteName: 'Home',
order: ['Paiement', 'Horaires', 'Home', 'Proximite', 'Settings'],
//Default Options for the bottom Tab
defaultNavigationOptions: ({
navigation
}) => ({
tabBarIcon: ({
focused,
horizontal,
tintColor
}) => {
const {
routeName
} = navigation.state;
let iconName;
if (routeName === 'Home') {
iconName = `ios-home${focused ? '' : ''}`;
} else if (routeName === 'Settings') {
iconName = `ios-settings${focused ? '' : ''}`;
} else if (routeName === 'Horaires') {
iconName = `ios-clock${focused ? '' : ''}`;
} else if (routeName === 'Proximite') {
iconName = `ios-locate${focused ? '' : ''}`;
} else if (routeName === 'Paiement') {
iconName = `ios-cart${focused ? '' : ''}`;
}
return <Ionicons name = {
iconName
}
size = {
horizontal ? 20 : 35
}
color = {
tintColor
}
/>;
}
}),
tabBarOptions: {
activeTintColor: 'tomato',
inactiveTintColor: 'gray',
tabStyle: {
backgroundColor: '#000'
},
showLabel: false,
showIcon: true
}
})
export default AppTabNavigator = createAppContainer(TabContainer);

React navigation stack provide default go back option its usually based on your stack.
close active screen and move back in the stack using
this.props.navigate("goBack")
this.props.navigate.pop("HomeScreen")
We can also push or navigate to HomeScreen by using
this.props.navigate.navigate("HomeScreen")
this.props.navigate.push("HomeScreen")

Related

Checkbox condition in react native

im trying to put a condition on my checkboxs , i want to set the number of checks for each state
so here i have my JSON file that has 4 elements
[
{
"id": 0,
"key": "Viande hachee",
"checked": false,
"image": "https://i.imgur.com/RuVD8qi.png "
},
{
"id": 1,
"key": "Escalope pane",
"checked": false,
"image": "https://i.imgur.com/WNeYn6d.png"
},
{
"id": 2,
"key": "Escalope grille",
"checked": false,
"image": "https://i.imgur.com/iFpCySw.png"
},
{
"id": 3,
"key": "Cordon bleu",
"checked": false,
"image": "https://i.imgur.com/iRo6ivZ.png"
}
]
this is my code :
export default class Commande extends Component {
constructor(props) {
super(props)
this.state = {
data: TacosData,
SelectedItem: [],
taille: "M"
};
}
onchecked(id) {
const data = this.state.data
const index = data.findIndex(x => x.id === id);
data[index].checked = !data[index].checked
this.setState(data)
}
renderTacos(item, key) {
return (
<TouchableOpacity style={{ alignItems: 'center', marginLeft: normalize(20), marginRight: normalize(20 )}} key={key} onPress={() => { this.onchecked(item.id) }}>
<Image style={styles.rednerImg} source={{ uri: item.image }} ></Image>
<Text style={styles.rednertext}>{item.key}</Text>
<CheckBox value={item.checked}
style={{ transform: [{ scaleX: 0.8 }, { scaleY: 0.8 }], }}
onValueChange={() => { this.onchecked(item.id) }}
tintColors={{ true: '#D05A0B', false: 'black' }}
/>
</TouchableOpacity>
)
}
render() {
return (
<FlatList
data={this.state.data}
style={{ width: '100%', height: '100%', }}
numColumns={2}
renderItem={({ item }) => this.renderTacos(item)}
keyExtractor={(item, index) => index.toString()}
/>
)}
}
For example this the state of taille is 'M' then i can only check only 1 checkox ,
is this case im able to check all of the checkboxes
is there any solution for this ?
From what you said if...
the state of taille is 'M' then i can only check only 1 checkox , is
this case im able to check all of the checkboxes
then code is my answer to handle that case
onchecked(id) {
const data = this.state.data
const index = data.findIndex(x => x.id === id)
if (this.state.taille == 'M') {
for(const i = 0; i < data.length; i++) {
if (i != index) {
data[i].checked = false // reset other checkboxes to unchecked, so when taille is 'M' only clicked checkbox that can be true
}
}
}
data[index].checked = !data[index].checked
this.setState({data})
}
Edit responding case based to the following comment
onchecked(id) {
const data = this.state.data
const index = data.findIndex(x => x.id === id)
let maxCheck = undefined
if (this.state.taille == 'M') {
maxCheck = 1
} else if (this.state.taille == 'L') {
maxCheck = 2
}
let checkboxCheckedTotal = 0
for(const i = 0; i < data.length; i++) {
if (data[i].checked) {
checkboxCheckedTotal++
}
}
if (maxCheck == undefined || data[index].checked || checkboxCheckedTotal < maxCheck) {
data[index].checked = !data[index].checked
}
this.setState({data})
}

Setting a react state in callback does not work as expected

I am making barcode reading app. I use Quaggajs and Reactjs.
Quaggajs has a function Quagga.onDetected(callback), the callback has one parameter result. The result is containing the detected barcode. I am having a react state (const [count, setCount] = useState(0);) in which I am counting the detected barcodes. The problem is that when I use setCount(count + 1) in the callback the count variable is allways it's initial value (0) so every time onDetect the setCount is setting the new value to be 1.
Here is an example of the functional react component I use(I think that there is no problem getting the count and setCount from the props of ):
const Body = ({setCount, count, ...rest }) => {
const start = () => {
Quagga.init({
inputStream: {
name: "Live",
type: "LiveStream",
constraints: {
facingMode: "environment",
},
area: {
top: "30%",
bottom: "30%"
}
},
locate: false,
frequency: 50,
decoder: {
readers: [
// "code_128_reader",
"ean_reader",
// "ean_8_reader",
// "code_39_reader",
// "code_39_vin_reader",
// "codabar_reader",
// "upc_reader",
// "upc_e_reader",
// "i2of5_reader"
],
debug: {
showCanvas: true,
showPatches: true,
showFoundPatches: true,
showSkeleton: true,
showLabels: true,
showPatchLabels: true,
showRemainingPatchLabels: true,
boxFromPatches: {
showTransformed: true,
showTransformedBox: true,
showBB: true
}
}
},
}, function (err) {
if (err) {
console.log(err);
return
}
console.log("Initialization finished. Ready to start");
Quagga.start();
});
Quagga.onProcessed(function (result) {
var drawingCtx = Quagga.canvas.ctx.overlay,
drawingCanvas = Quagga.canvas.dom.overlay;
if (result) {
if (result.boxes) {
drawingCtx.clearRect(0, 0, parseInt(drawingCanvas.getAttribute("width")), parseInt(drawingCanvas.getAttribute("height")));
result.boxes.filter(function (box) {
return box !== result.box;
}).forEach(function (box) {
Quagga.ImageDebug.drawPath(box, { x: 0, y: 1 }, drawingCtx, { color: "green", lineWidth: 2 });
});
}
if (result.box) {
Quagga.ImageDebug.drawPath(result.box, { x: 0, y: 1 }, drawingCtx, { color: "#00F", lineWidth: 2 });
}
if (result.codeResult && result.codeResult.code) {
Quagga.ImageDebug.drawPath(result.line, { x: 'x', y: 'y' }, drawingCtx, { color: 'red', lineWidth: 3 });
}
}
});
Quagga.onDetected((result) => {
if (result && result.codeResult && result.codeResult.startInfo && result.codeResult.startInfo.error < 0.03) {
console.log("Sould change")
setCount(count + 1);
}
});
}
const stop = () => { Quagga.stop(); }
useEffect(() => {
start();
console.log("CHANGE BODY");
return () => {
stop();
}
}, [])
return (
<Grid container justify="center">
<div id="interactive" className="viewport" style={{ position: 'unset', width: '100vw', height: "100vh" }}>
<video src=""></video>
<canvas class="drawingBuffer"></canvas>
</div>
</Grid>
)
}

How to access ReactQuill Ref when using dynamic import in NextJS?

I'm running into a funny problem. I'm using NextJS for its server-side rendering capabilities and am using ReactQuill as my rich-text editor. To get around ReactQuill's tie to the DOM, I'm dynamically importing it. However, that presents another problem which is that when I try to attach a ref to the ReactQuill component, it's treated as a loadable component instead of the ReactQuill component. I need the ref in order to customize how images are handled when uploaded into the rich-text editor. Right now, the ref returns current:null instead of the function I can use .getEditor() on to customize image handling.
Anybody have any thoughts on how I can address this? I tried ref-forwarding, but it's still applying refs to a loadable component, instead of the React-Quill one. Here's a snapshot of my code.
const ReactQuill = dynamic(import('react-quill'), { ssr: false, loading: () => <p>Loading ...</p> }
);
const ForwardedRefComponent = React.forwardRef((props, ref) => {return (
<ReactQuill {...props} forwardedRef={(el) => {ref = el;}} />
)})
class Create extends Component {
constructor() {
super();
this.reactQuillRef = React.createRef();
}
imageHandler = () => {
console.log(this.reactQuillRef); //this returns current:null, can't use getEditor() on it.
}
render() {
const modules = {
toolbar: {
container: [[{ 'header': [ 2, 3, false] }],
['bold', 'italic', 'underline', 'strike'],
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
[{ 'script': 'sub'}, { 'script': 'super' }],
['link', 'image'],
[{ 'indent': '-1'}, { 'indent': '+1' }],
[{ 'align': [] }],
['blockquote', 'code-block'],],
handlers: {
'image': this.imageHandler
}
}
};
return(
<ForwardedRefComponent
value={this.state.text}
onChange={this.handleChange}
modules={modules}
ref={this.reactQuillRef}/> //this.reactQuillRef is returning current:null instead of the ReactQuill function for me to use .getEditor() on
)
}
}
const mapStateToProps = state => ({
tutorial: state.tutorial,
});
export default connect(
mapStateToProps, {createTutorial}
)(Create);
I share my solution with hope that it helps you too.
Helped from https://github.com/zenoamaro/react-quill/issues/642#issuecomment-717661518
const ReactQuill = dynamic(
async () => {
const { default: RQ } = await import("react-quill");
return ({ forwardedRef, ...props }) => <RQ ref={forwardedRef} {...props} />;
},
{
ssr: false
}
);
export default function QuillWrapper() {
const quillRef = React.useRef(false)
return <>
<ReactQuill forwardedRef={quillRef} />
</>
}
for example you can use the ref to upload image with custom hanlder
import React, { useMemo } from "react";
import dynamic from "next/dynamic";
const ReactQuill = dynamic(
async () => {
const { default: RQ } = await import("react-quill");
return ({ forwardedRef, ...props }) => <RQ ref={forwardedRef} {...props} />;
},
{
ssr: false,
}
);
export default function QuillWrapper({ value, onChange, ...props }) {
const quillRef = React.useRef(false);
// Custom image upload handler
function imgHandler() {
// from https://github.com/quilljs/quill/issues/1089#issuecomment-318066471
const quill = quillRef.current.getEditor();
let fileInput = quill.root.querySelector("input.ql-image[type=file]");
// to prevent duplicate initialization I guess
if (fileInput === null) {
fileInput = document.createElement("input");
fileInput.setAttribute("type", "file");
fileInput.setAttribute(
"accept",
"image/png, image/gif, image/jpeg, image/bmp, image/x-icon"
);
fileInput.classList.add("ql-image");
fileInput.addEventListener("change", () => {
const files = fileInput.files;
const range = quill.getSelection(true);
if (!files || !files.length) {
console.log("No files selected");
return;
}
const formData = new FormData();
formData.append("file", files[0]);
formData.append("uid", uid);
formData.append("img_type", "detail");
quill.enable(false);
console.log(files[0]);
axios
.post("the/url/for/handle/uploading", formData)
.then((response) => {
// after uploading succeed add img tag in the editor.
// for detail visit https://quilljs.com/docs/api/#editor
quill.enable(true);
quill.insertEmbed(range.index, "image", response.data.url);
quill.setSelection(range.index + 1);
fileInput.value = "";
})
.catch((error) => {
console.log("quill image upload failed");
console.log(error);
quill.enable(true);
});
});
quill.root.appendChild(fileInput);
}
fileInput.click();
}
I don't know much about useMemo
but if i don't use the hook,
the editor keeps rerendered resulting in losing focus and I guess perfomance trouble too.
const modules = useMemo(
() => ({
toolbar: {
container: [
[{ font: [] }],
[{ size: ["small", false, "large", "huge"] }], // custom dropdown
["bold", "italic", "underline", "strike"], // toggled buttons
[{ color: [] }, { background: [] }], // dropdown with defaults from theme
[{ script: "sub" }, { script: "super" }], // superscript/subscript
[{ header: 1 }, { header: 2 }], // custom button values
["blockquote", "code-block"],
[{ list: "ordered" }, { list: "bullet" }],
[{ indent: "-1" }, { indent: "+1" }], // outdent/indent
[{ direction: "rtl" }], // text direction
[{ align: [] }],
["link", "image"],
["clean"], // remove formatting button
],
handlers: { image: imgHandler }, // Custom image handler
},
}),
[]
);
return (
<ReactQuill
forwardedRef={quillRef}
modules={modules}
value={value}
onChange={onChange}
{...props}
/>
);
}
In NextJS, React.useRef or React.createRef do not work with dynamic import.
You should Replace
const ReactQuill = dynamic(import('react-quill'), { ssr: false, loading: () => <p>Loading ...</p> }
);
with
import ReactQuill from 'react-quill';
and render after when window is loaded.
import ReactQuill from 'react-quill';
class Create extends Component {
constructor() {
super();
this.reactQuillRef = React.createRef();
this.state = {isWindowLoaded: false};
}
componentDidMount() {
this.setState({...this.state, isWindowLoaded: true});
}
.........
.........
render(){
return (
<div>
{this.isWindowLoaded && <ReactQuil {...this.props}/>}
</div>
)
}
}
Use onChange and pass all the arguments, here one example to use the editor.getHTML()
import React, { Component } from 'react'
import dynamic from 'next/dynamic'
import { render } from 'react-dom'
const QuillNoSSRWrapper = dynamic(import('react-quill'), {
ssr: false,
loading: () => <p>Loading ...</p>,
})
const modules = {
toolbar: [
[{ header: '1' }, { header: '2' }, { font: [] }],
[{ size: [] }],
['bold', 'italic', 'underline', 'strike', 'blockquote'],
[
{ list: 'ordered' },
{ list: 'bullet' },
{ indent: '-1' },
{ indent: '+1' },
],
['link', 'image', 'video'],
['clean'],
],
clipboard: {
// toggle to add extra line breaks when pasting HTML:
matchVisual: false,
},
}
/*
* Quill editor formats
* See https://quilljs.com/docs/formats/
*/
const formats = [
'header',
'font',
'size',
'bold',
'italic',
'underline',
'strike',
'blockquote',
'list',
'bullet',
'indent',
'link',
'image',
'video',
]
class BlogEditor extends Component {
constructor(props) {
super(props)
this.state = { value: null } // You can also pass a Quill Delta here
this.handleChange = this.handleChange.bind(this)
this.editor = React.createRef()
}
handleChange = (content, delta, source, editor) => {
this.setState({ value: editor.getHTML() })
}
render() {
return (
<>
<div dangerouslySetInnerHTML={{ __html: this.state.value }} />
<QuillNoSSRWrapper ref={this.editor} onChange={this.handleChange} modules={modules} formats={formats} theme="snow" />
<QuillNoSSRWrapper value={this.state.value} modules={modules} formats={formats} theme="snow" />
</>
)
}
}
export default BlogEditor
If you want to use ref in Next.js with dynamic import
you can use React.forwardRef API
more info

Get child component reference in parent component

I am new to Angular and stuck on an issue. Anyone can help me to figure out that what I am doing wrong in parent component. I am not able to get the child component reference in parent. I already followed following reference but not succeeded.
Angular 2 #ViewChild annotation returns undefined
https://expertcodeblog.wordpress.com/2018/01/12/angular-resolve-error-viewchild-annotation-returns-undefined/
Parent:
import { Component, OnInit, OnDestroy, ViewChild, HostListener, AfterViewInit, ViewChildren, QueryList } from '#angular/core';
import { Router, NavigationEnd, NavigationStart } from '#angular/router';
import { NavItem, NavItemType } from '../../md/md.module';
import { Subscription } from 'rxjs/Subscription';
import { Location, LocationStrategy, PathLocationStrategy, PopStateEvent } from '#angular/common';
import 'rxjs/add/operator/filter';
import { NavbarComponent } from '../../shared/navbar/navbar.component';
import PerfectScrollbar from 'perfect-scrollbar';
import { ChatService } from 'app/services/chat.service';
import swal from 'sweetalert2';
import { JitsiService } from 'app/services/jitsi.service';
import { UserService } from 'app/services/user.service';
import { ConferenceStudentComponent } from 'app/conference-student/conference-student.component';
declare const $: any;
#Component({
selector: 'app-layout',
templateUrl: './admin-layout.component.html'
})
export class AdminLayoutComponent implements OnInit, AfterViewInit {
public navItems: NavItem[];
private _router: Subscription;
private lastPoppedUrl: string;
private yScrollStack: number[] = [];
url: string;
location: Location;
#ViewChild('sidebar') sidebar: any;
#ViewChild(NavbarComponent) navbar: NavbarComponent;
#ViewChildren(ConferenceStudentComponent) stuConf: QueryList<ConferenceStudentComponent>;
constructor( private router: Router, location: Location,
private chatService: ChatService,
private jitsiService: JitsiService,
private userService: UserService
) {
this.location = location;
this.chatService.callVisibilityChange
.subscribe(callFrom => {
console.log('admin layout call from', callFrom);
if (callFrom) {
this.userService.getLoggedUserDetail()
.subscribe(loggedUser => {
if (!loggedUser) {
console.log(`Invalid token, logged user data not fetched`);
return false;
}
this.userService.getUser(callFrom['fromUser'])
.subscribe(otherUser => {
swal({
title: `${otherUser['fullName']} is calling`,
text: `Click on accept to join session`,
type: `info`,
showCancelButton: true,
cancelButtonColor: `#d33`,
cancelButtonText: `reject`,
confirmButtonColor: `#3085d6`,
confirmButtonText: `accept`
}).then((result) => {
if (result.value) {
const jitsiSessionData = {
loggedUser,
otherUser,
roomName: callFrom['roomName']
}
this.router.navigateByUrl(`/conference-student/${otherUser['_id']}`);
window.setTimeout(() => this.jitsiService.joinSession(jitsiSessionData), 10000);
} else {
console.log('user select rejected');
this.chatService.jitsiCallReject(otherUser._id, loggedUser._id, callFrom['roomName']);
}
})
});
});
}
});
}
ngOnInit() {
const elemMainPanel = <HTMLElement>document.querySelector('.main-panel');
const elemSidebar = <HTMLElement>document.querySelector('.sidebar .sidebar-wrapper');
this.location.subscribe((ev:PopStateEvent) => {
this.lastPoppedUrl = ev.url;
});
this.router.events.subscribe((event:any) => {
if (event instanceof NavigationStart) {
if (event.url != this.lastPoppedUrl)
this.yScrollStack.push(window.scrollY);
} else if (event instanceof NavigationEnd) {
if (event.url == this.lastPoppedUrl) {
this.lastPoppedUrl = undefined;
window.scrollTo(0, this.yScrollStack.pop());
}
else
window.scrollTo(0, 0);
}
});
this._router = this.router.events.filter(event => event instanceof NavigationEnd).subscribe((event: NavigationEnd) => {
elemMainPanel.scrollTop = 0;
elemSidebar.scrollTop = 0;
});
const html = document.getElementsByTagName('html')[0];
if (window.matchMedia(`(min-width: 960px)`).matches && !this.isMac()) {
let ps = new PerfectScrollbar(elemMainPanel);
ps = new PerfectScrollbar(elemSidebar);
html.classList.add('perfect-scrollbar-on');
}
else {
html.classList.add('perfect-scrollbar-off');
}
this._router = this.router.events.filter(event => event instanceof NavigationEnd).subscribe((event: NavigationEnd) => {
this.navbar.sidebarClose();
});
this.navItems = [
{ type: NavItemType.NavbarLeft, title: 'Dashboard', iconClass: 'fa fa-dashboard' },
{
type: NavItemType.NavbarRight,
title: '',
iconClass: 'fa fa-bell-o',
numNotifications: 5,
dropdownItems: [
{ title: 'Notification 1' },
{ title: 'Notification 2' },
{ title: 'Notification 3' },
{ title: 'Notification 4' },
{ title: 'Another Notification' }
]
},
{
type: NavItemType.NavbarRight,
title: '',
iconClass: 'fa fa-list',
dropdownItems: [
{ iconClass: 'pe-7s-mail', title: 'Messages' },
{ iconClass: 'pe-7s-help1', title: 'Help Center' },
{ iconClass: 'pe-7s-tools', title: 'Settings' },
'separator',
{ iconClass: 'pe-7s-lock', title: 'Lock Screen' },
{ iconClass: 'pe-7s-close-circle', title: 'Log Out' }
]
},
{ type: NavItemType.NavbarLeft, title: 'Search', iconClass: 'fa fa-search' },
{ type: NavItemType.NavbarLeft, title: 'Account' },
{
type: NavItemType.NavbarLeft,
title: 'Dropdown',
dropdownItems: [
{ title: 'Action' },
{ title: 'Another action' },
{ title: 'Something' },
{ title: 'Another action' },
{ title: 'Something' },
'separator',
{ title: 'Separated link' },
]
},
{ type: NavItemType.NavbarLeft, title: 'Log out' }
];
}
ngAfterViewInit() {
this.runOnRouteChange();
this.stuConf.changes.subscribe((comp: QueryList<ConferenceStudentComponent>) => {
console.log(`student component`, comp);
})
}
public isMap() {
if (this.location.prepareExternalUrl(this.location.path()) === '/maps/fullscreen') {
return true;
} else {
return false;
}
}
runOnRouteChange(): void {
if (window.matchMedia(`(min-width: 960px)`).matches && !this.isMac()) {
const elemSidebar = <HTMLElement>document.querySelector('.sidebar .sidebar-wrapper');
const elemMainPanel = <HTMLElement>document.querySelector('.main-panel');
let ps = new PerfectScrollbar(elemMainPanel);
ps = new PerfectScrollbar(elemSidebar);
ps.update();
}
}
isMac(): boolean {
let bool = false;
if (navigator.platform.toUpperCase().indexOf('MAC') >= 0 || navigator.platform.toUpperCase().indexOf('IPAD') >= 0) {
bool = true;
}
return bool;
}
}
Child:
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute, ParamMap } from '#angular/router';
import '../../vendor/jitsi/external_api.js';
import { JitsiService } from 'app/services/jitsi.service.js';
import { UserService, UserSchema } from 'app/services/user.service.js';
import swal from 'sweetalert2';
declare var JitsiMeetExternalAPI: any;
declare const $: any;
#Component({
selector: "app-conference-student-cmp",
templateUrl: "conference-student.component.html"
})
export class ConferenceStudentComponent implements OnInit {
roomName: string;
tutor: UserSchema
student: UserSchema;
domain: string;
options: any;
api: any;
hasActiveRoom: boolean;
tutorId: string;
conferenceJoined: boolean;
constructor(
private route: ActivatedRoute,
private jitsiService: JitsiService,
private userService: UserService
) { }
ngOnInit() {
this.conferenceJoined = false;
// this.domain = "jitsi.liquidclouds.in";
this.domain = 'meet.jit.si';
this.route.paramMap.subscribe((params: ParamMap) => this.tutorId = params.get('id'));
this.userService
.getLoggedUserDetail()
.subscribe(student => {
// store student
this.student = student;
this.userService.getUser(this.tutorId)
.subscribe((tutor: UserSchema) => {
// store tutor
this.tutor = tutor;
const obj = { tutorId: this.tutor['_id'], studentId: this.student['_id'] };
this.jitsiService.getActiveRoomForStudent(obj).subscribe(resp => {
if (resp && resp['result'] && resp['result']['roomName']) {
this.hasActiveRoom = true;
this.roomName = resp['result']['roomName'];
}
});
});
});
}
joinSession() {
this.options = {
roomName: this.roomName,
width: 800,
height: 500,
parentNode: document.querySelector('#jitsiVideo'),
configOverwrite: {},
interfaceConfigOverwrite: {
// filmStripOnly: true,
TOOLBAR_BUTTONS: [
'microphone', 'camera', 'closedcaptions', 'desktop', 'fullscreen',
'hangup', 'profile', 'chat', 'recording'
],
}
};
this.api = new JitsiMeetExternalAPI(this.domain, this.options);
this.api.executeCommand('displayName', this.student['fullName']);
this.api.addEventListeners({
readyToClose: this.unload,
participantLeft: this.handleParticipantLeft,
participantJoined: this.handleParticipantJoined,
videoConferenceJoined: this.handleVideoConferenceJoined,
videoConferenceLeft: this.handleVideoConferenceLeft
});
this.conferenceJoined = true;
}
unload = () => {
if (this.api instanceof JitsiMeetExternalAPI) {
this.api.dispose();
}
$('#jitsiVideo').html('');
this.conferenceJoined = false;
this.hasActiveRoom = false;
}
handleParticipantLeft = (arg) => {
this.jitsiService.getJitsiDetailByParticipantId(arg.id)
.subscribe(async roomDetail => {
if (!roomDetail) {
console.log(`No room is joined by the participant id: ${arg.id}`);
return false;
} else {
const participantDetail = roomDetail['participants'].filter(el => el.jitsiParticipantId === arg.id)[0];
if (participantDetail) {
switch (participantDetail.type) {
case 'manager':
console.log('Left participant is manager');
break;
case 'supervisor':
console.log('Left participant is supervisor');
break;
case 'student':
console.log('Left participant is student');
break;
case 'tutor':
console.log('Left participant is tutor');
this.api.dispose();
this.conferenceJoined = false;
this.hasActiveRoom = false;
alert('Tutor left the session.');
break;
default:
console.log('Left participant is not a valid type');
break;
}
}
}
});
}
handleParticipantJoined = (arg) => {
console.log('participant joined: ', arg, this.api);
}
handleVideoConferenceJoined = (arg) => {
const obj = {
participantId: arg.id,
roomName: arg.roomName,
tutorId: this.tutor['_id'],
studentId: this.student['_id'],
studentJoined: 'yes',
joineeType: 'student',
}
// save new room
this.jitsiService.updateJitsiRoomForStudent(obj);
}
handleVideoConferenceLeft = (arg) => {
}
}
admin-layout.componet.html
<div class="wrapper">
<div class="sidebar" data-color="rose" data-background-color="white" data-image="./assets/img/sidebar-1.jpg">
<app-sidebar-cmp></app-sidebar-cmp>
<div class="sidebar-background" style="background-image: url(assets/img/sidebar-1.jpg)"></div>
</div>
<div class="main-panel">
<router-outlet></router-outlet>
<div *ngIf="!isMap()">
<app-footer-cmp></app-footer-cmp>
</div>
</div>
<app-fixedplugin></app-fixedplugin>
</div>

Opening SideMenu on button press

I'm currently trying to upgrade to react-native-navigation V2 from V1 and got stuck trying to find a way to toggle side menus on top bar button press.
My app starts with
Navigation.setRoot({
root: {
sideMenu: {
left: {
component: {
name: 'testApp.SideDrawer',
passProps: {
text: 'This is a left side menu screen'
}
}
},
center: {
bottomTabs: {
...
}
},
},
},
});
Is there a way to do it in current version?
Turned out you can't use this.props.navigator.toggleDrawer in V2 and should use Navigator.mergeOptions instead to change drawer visibility.
In my case:
1) First assign an Id to the drawer (id: leftSideDrawer)
Navigation.setRoot({
root: {
sideMenu: {
left: {
component: {
name: 'testApp.SideDrawer',
id: 'leftSideDrawer'
}
},
center: {
bottomTabs: {
...
}
},
},
},
});
2) Use it in to change drawer visibility
Navigation.mergeOptions('leftSideDrawer', {
sideMenu: {
left: {
visible: true
}
}
});
You can set a boolean in your component to identify the current state of the side drawer screen and then you can use that boolean to set the visibility of the drawer with mergeOptions. Basically toggle! Below is the snippet to achieve this.
constructor(props) {
super(props);
this.isSideDrawerVisible = false;
Navigation.events().bindComponent(this);
}
navigationButtonPressed({ buttonId }) {
if (buttonId === "openSideDrawer") {
(!this.isSideDrawerVisible) ? this.isSideDrawerVisible = true : this.isSideDrawerVisible = false
Navigation.mergeOptions(this.props.componentId, {
sideMenu: {
left: {
visible: this.isSideDrawerVisible,
}
}
});
}
}

Resources