Implementing deep link (#41)

This commit is contained in:
Gustavo Emanuel Farias Rosa
2022-06-15 16:14:57 -03:00
committed by GitHub
parent f2115e23c7
commit b4dbb55548
8 changed files with 181 additions and 73 deletions

View File

@@ -35,6 +35,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate, RCTBridgeDelegate {
return true
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
BigBlueButtonSDK.handleDeepLink(app, open: url, options: options)
return true
}
func sourceURL(for bridge: RCTBridge!) -> URL! {
//#if DEBUG

View File

@@ -20,6 +20,19 @@
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>com.bigbluebutton.mobile</string>
<key>CFBundleURLSchemes</key>
<array>
<string>bigbluebutton</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>ITSAppUsesNonExemptEncryption</key>

View File

@@ -0,0 +1,14 @@
import {
NativeModules,
NativeEventEmitter,
} from 'react-native';
const {
ReactNativeEventEmitter,
} = NativeModules;
export const emitter: NativeEventEmitter = new NativeEventEmitter(
ReactNativeEventEmitter
)

View File

@@ -1,25 +1,26 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import {ICreatePortal, IPortal, IPortalProp, IPortalToAdd} from './types';
import { IPortal, IPortalProp, IPortalToAdd} from './types';
export async function createNewPortal({
name,
url,
}: ICreatePortal): Promise<IPortal[]> {
temporary = false
}: IPortal): Promise<IPortal[]> {
let portalsStorage: IPortalProp = await getPortals();
if (portalsStorage === null || portalsStorage === '[]')
return await createFromEmptyStorage(name, url);
return await createFromEmptyStorage({name, url, temporary});
portalsStorage = parseString(portalsStorage);
return await addPortalToStorage({portals: portalsStorage, name, url});
return await addPortalToStorage({portals: portalsStorage, name, url, temporary});
}
async function createFromEmptyStorage(name: string, url: string) {
async function createFromEmptyStorage({name, url, temporary}:IPortal) {
await createStorageEmpty();
let portalsStorage = await getPortals();
const portalStorage = parseString(portalsStorage!);
await addPortalToStorage({portals: portalStorage, name, url});
await addPortalToStorage({portals: portalStorage, name, url, temporary});
return portalStorage;
}
@@ -39,8 +40,9 @@ async function addPortalToStorage({
portals,
name,
url,
temporary
}: IPortalToAdd): Promise<IPortal[]> {
portals.push({name, url});
portals.push({name, url, temporary});
await AsyncStorage.setItem('portal', JSON.stringify(portals));
return portals;
}

View File

@@ -1,17 +1,14 @@
export type ICreatePortal = {
name: string;
url: string;
};
export type IPortal = {
name: string;
url: string;
temporary: boolean
};
export type IPortalToAdd = {
portals: Array<IPortal>;
name: string;
url: string;
temporary: boolean;
};
export type IPortalProp = string | null | IPortal[];

View File

@@ -1,60 +0,0 @@
import * as React from 'react';
import {NavigationContainer} from '@react-navigation/native';
import {ListPortals} from '../pages/list_portals/component';
import {usePortal} from '../contexts/portals/hook';
import AsyncStorage from '@react-native-async-storage/async-storage';
import i18next from 'i18next';
import {createDrawerNavigator} from '@react-navigation/drawer';
import SdkContainer from '../../bootstrap/sdk/container';
import {initTranslation} from '../translations/index';
const Drawer = createDrawerNavigator();
export const Routes = () => {
initTranslation();
const {portals, setPortals} = usePortal();
async function getPortals() {
try {
let items = await AsyncStorage.getAllKeys();
if (items.includes('portal')) {
let portalsStorage = await AsyncStorage.getItem('portal');
portalsStorage = JSON.parse(portalsStorage);
setPortals(portalsStorage);
}
} catch (e) {
console.log('error', e);
return null;
}
}
React.useEffect(() => {
getPortals();
}, []);
return (
<NavigationContainer>
<Drawer.Navigator
initialRouteName="Portals"
screenOptions={{
headerShown: false,
}}>
<Drawer.Screen
name={i18next.t('mobileApp.portals.drawerNavigation.button.label')}
component={ListPortals}
/>
{portals && portals.length
? portals.map(item => {
return (
<Drawer.Screen
key={item.name}
name={item.name}
children={() => <SdkContainer url={item.url} />}
/>
);
})
: null}
</Drawer.Navigator>
</NavigationContainer>
);
};

View File

@@ -0,0 +1,133 @@
import * as React from 'react';
import { NavigationContainer, useNavigation} from '@react-navigation/native';
import {ListPortals} from '../pages/list_portals/component';
import {usePortal} from '../contexts/portals/hook';
import AsyncStorage from '@react-native-async-storage/async-storage';
import i18next from 'i18next';
import {createDrawerNavigator} from '@react-navigation/drawer';
import SdkContainer from '../../bootstrap/sdk/container';
import {initTranslation} from '../translations/index';
import { Alert, Linking } from 'react-native';
import { createNewPortal } from '../pages/utils/createNewPortal';
import { emitter } from '../emitter/emitter';
import { IPortal } from '../pages/utils/types';
const DeepLink = ()=>{
initTranslation();
const SCHEME = 'bigbluebutton://';
const SCHEME_DEFAULT = 'https://'
var NAME_PORTALS_DEEP_LINK = i18next.t('mobileApp.portals.namePortal.deepLink');
const navigate = useNavigation();
const {portals, setPortals} = usePortal();
async function createTemporaryPortalFromDeepLink(link: string){
const linkWithoutScheme = link.replace(SCHEME, '');
if(linkWithoutScheme === ''){
navigate.navigate(i18next.t('mobileApp.portals.drawerNavigation.button.label'))
return Alert.alert(i18next.t('mobileApp.portals.handleWithoutURL'))
}
const roomName = linkWithoutScheme.match(/^\w+/)
if(roomName != 'bigbluebutton'){
NAME_PORTALS_DEEP_LINK = roomName[0]
}
const linkWhitoutSchemeAndName = linkWithoutScheme.replace(/^\w+[\/]/, '')
const portalToAdd:IPortal = {
name: NAME_PORTALS_DEEP_LINK,
url: SCHEME_DEFAULT+linkWhitoutSchemeAndName,
temporary: true
}
// Adding LinkedPortal to AsyncStorage
const newPortals = await createNewPortal(portalToAdd)
// Adding to context
setPortals(newPortals)
// Navigation to portal
navigate.navigate(NAME_PORTALS_DEEP_LINK)
}
async function checkIfHaveTemporaryPortal(){
const portalsFromStorage = await AsyncStorage.getItem('portal')
const portalsParsed = JSON.parse(portalsFromStorage)
const portalsWithoutTemporary = portalsParsed.filter((portal: IPortal)=>{
if(portal.temporary == true) return false
if(portal.name == NAME_PORTALS_DEEP_LINK) return false
return portal
})
await AsyncStorage.setItem('portal', JSON.stringify(portalsWithoutTemporary))
setPortals(portalsWithoutTemporary)
getLinkFromAppClosed() //To app running in backgrond then open throug deep link
Linking.addEventListener('url', (url)=>{
// if app is open when it's minimized (not running in background)
createTemporaryPortalFromDeepLink(url.url)
})
}
async function getLinkFromAppClosed(){
const linkFromAppClosed = await Linking.getInitialURL()
if(linkFromAppClosed === null) return
createTemporaryPortalFromDeepLink(linkFromAppClosed)
}
React.useEffect(() => {
checkIfHaveTemporaryPortal()
}, []);
return null
}
const Drawer = createDrawerNavigator();
export const Routes = () => {
initTranslation();
const {portals, setPortals} = usePortal();
async function getPortals() {
try {
let items = await AsyncStorage.getAllKeys();
if (items.includes('portal')) {
let portalsStorage = await AsyncStorage.getItem('portal');
portalsStorage = JSON.parse(portalsStorage);
setPortals(portalsStorage);
}
} catch (e) {
console.log('error', e);
return null;
}
}
React.useEffect(() => {
getPortals();
}, []);
return (
<NavigationContainer>
<DeepLink/>
<Drawer.Navigator
initialRouteName={i18next.t('mobileApp.portals.drawerNavigation.button.label')}
screenOptions={{
headerShown: false,
}}>
<Drawer.Screen
name={i18next.t('mobileApp.portals.drawerNavigation.button.label')}
component={ListPortals}
/>
{portals && portals.length
? portals.map((item: { name: React.Key | null | undefined; url: string; }) => {
return (
<Drawer.Screen
key={item.name}
name={item.name}
children={() => <SdkContainer url={item.url} />}
/>
);
})
: null}
</Drawer.Navigator>
</NavigationContainer>
);
};

View File

@@ -9,5 +9,7 @@ export default {
"mobileApp.portals.drawerNavigation.button.label": "Portals",
"mobileApp.portals.addPortalPopup.validation.emptyFields": "Required Fields",
"mobileApp.portals.addPortalPopup.validation.portalNameAlreadyExists": "Name already in use",
"mobileApp.portals.addPortalPopup.validation.urlInvalid": "Error trying to load the page - check URL and network connection"
"mobileApp.portals.addPortalPopup.validation.urlInvalid": "Error trying to load the page - check URL and network connection",
"mobileApp.portals.namePortal.deepLink": "Linked Portal",
"mobileApp.portals.handleWithoutURL": "Dont have link, you was redirected to list portals home.",
}