Erstellen von React Native-Apps mit Microsoft Graph
In diesem Lernprogramm erfahren Sie, wie Sie eine React Native-App erstellen, die die Microsoft Graph-API zum Abrufen von Kalenderinformationen für einen Benutzer verwendet.
Tipp
Wenn Sie es vorziehen, nur das abgeschlossene Lernprogramm herunterzuladen, können Sie das GitHub Repository herunterladen oder klonen.
Voraussetzungen
Bevor Sie mit diesem Lernprogramm beginnen, sollten Sie Folgendes auf Ihrem Entwicklungscomputer installiert haben.
- Mindestens eine der folgenden Optionen:
- Android Studio und Java Development Kit (erforderlich, um das Beispiel unter Android auszuführen)
- Xcode (erforderlich, um das Beispiel unter iOS auszuführen)
- Node.js
Sie sollten auch über ein persönliches Microsoft-Konto mit einem Postfach auf Outlook.com oder ein Microsoft-Geschäfts-, Schul- oder Unikonto verfügen. Wenn Sie kein Microsoft-Konto haben, gibt es einige Optionen, um ein kostenloses Konto zu erhalten:
- Sie können sich für ein neues persönliches Microsoft-Konto registrieren.
- Sie können sich für das Microsoft 365 Entwicklerprogramm registrieren, um ein kostenloses Microsoft 365 Abonnement zu erhalten.
Hinweis
Dieses Lernprogramm wurde mithilfe der React Native CLI geschrieben, die je nach Betriebssystem und Zielplattformen bestimmte Voraussetzungen hat. Anweisungen zum Konfigurieren Ihres Entwicklungscomputers finden Sie unter React Native Erste Schritte. Die für dieses Lernprogramm verwendeten Versionen sind unten aufgeführt. Die Schritte in diesem Handbuch funktionieren möglicherweise mit anderen Versionen, die jedoch nicht getestet wurden.
- Android Studio Version 4.1 mit dem Android 9.0 SDK
- Java Development Kit, Version 12.0.2
- Xcode Version 12.5.1
- Node.js Version 14.15.0
Feedback
Bitte geben Sie Feedback zu diesem Lernprogramm im GitHub Repository.
Erstellen einer React Native-App
Erstellen Sie zunächst ein neues React Native Projekt.
Öffnen Sie die Befehlszeilenschnittstelle (CLI) in einem Verzeichnis, in dem Sie das Projekt erstellen möchten. Führen Sie den folgenden Befehl aus, um das Tool react-native-cli auszuführen und ein neues React Native Projekt zu erstellen.
npx react-native init GraphTutorial --template react-native-template-typescript
Optional: Stellen Sie sicher, dass Die Entwicklungsumgebung ordnungsgemäß konfiguriert ist, indem Sie das Projekt ausführen. Ändern Sie in Ihrer CLI das Verzeichnis in das soeben erstellte GraphTutorial-Verzeichnis , und führen Sie einen der folgenden Befehle aus.
- Für iOS:
npx react-native run-ios
- Für Android: Starten einer Android-Emulatorinstanz und Ausführen
npx react-native run-android
- Für iOS:
Installieren von Abhängigkeiten
Bevor Sie fortfahren, installieren Sie einige zusätzliche Abhängigkeiten, die Sie später verwenden werden.
- react-navigation to handle navigation between views in the app.
- react-native-gesture-handler, react-native-safe-area-context, react-native-screens, react-native-reanimate, and masked-view required by react-navigation.
- react-native-elements and react-native-vector-icons to provide icons for the UI.
- react-native-app-auth to handle authentication and token management.
- Asynchroner Speicher zur Bereitstellung von Speicher für Token.
- datetimepicker , um der Benutzeroberfläche Datums- und Uhrzeitauswahlen hinzuzufügen.
- moment to handle parsing and comparison of dates and times.
- windows-iana zum Übersetzen Windows Zeitzonen in das IANA-Format.
- Microsoft-Graph-Client für Aufrufe an die Microsoft-Graph.
Öffnen Sie Ihre CLI im Stammverzeichnis Ihres React Native Projekts.
Führen Sie den folgenden Befehl aus.
npm install @react-navigation/native@5.9.6 @react-navigation/drawer@5.12.7 @react-navigation/stack@5.14.7 npm install @react-native-community/masked-view@0.1.11 react-native-safe-area-context@3.3.0 windows-iana npm install react-native-reanimated@2.2.0 react-native-screens@3.5.0 @react-native-async-storage/async-storage@1.15.5 npm install react-native-elements@3.4.2 react-native-vector-icons@8.1.0 react-native-gesture-handler@1.10.3 npm install react-native-app-auth@6.4.0 moment@2.29.1 moment-timezone @microsoft/microsoft-graph-client@3.0.0 npm install @react-native-community/datetimepicker@3.5.2 npm install @microsoft/microsoft-graph-types --save-dev
Verknüpfen und Konfigurieren von Abhängigkeiten für iOS
Hinweis
Wenn Sie nicht auf iOS abzielen, können Sie diesen Abschnitt überspringen.
Öffnen Sie Ihre CLI im Verzeichnis GraphTutorial/ios .
Führen Sie den folgenden Befehl aus.
pod install
Öffnen Sie die Datei GraphTutorial/ios/GraphTutorial/Info.plist in einem Text-Editor. Fügen Sie Folgendes direkt vor der letzten
</dict>
Zeile in der Datei hinzu.<key>UIAppFonts</key> <array> <string>AntDesign.ttf</string> <string>Entypo.ttf</string> <string>EvilIcons.ttf</string> <string>Feather.ttf</string> <string>FontAwesome.ttf</string> <string>FontAwesome5_Brands.ttf</string> <string>FontAwesome5_Regular.ttf</string> <string>FontAwesome5_Solid.ttf</string> <string>Foundation.ttf</string> <string>Ionicons.ttf</string> <string>MaterialIcons.ttf</string> <string>MaterialCommunityIcons.ttf</string> <string>SimpleLineIcons.ttf</string> <string>Octicons.ttf</string> <string>Zocial.ttf</string> </array>
Öffnen Sie die Datei GraphTutorial/ios/GraphTutorial/AppDelegate.h in einem Text-Editor. Ersetzen Sie den Inhalt durch Folgendes.
#import <React/RCTBridgeDelegate.h> #import <UIKit/UIKit.h> #import "RNAppAuthAuthorizationFlowManager.h" @interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate, RNAppAuthAuthorizationFlowManager> @property (nonatomic, strong) UIWindow *window; @property (nonatomic, weak) id<RNAppAuthAuthorizationFlowManagerDelegate> authorizationFlowManagerDelegate; @end
Konfigurieren von Abhängigkeiten für Android
Hinweis
Wenn Sie nicht auf Android abzielen, können Sie diesen Abschnitt überspringen.
Öffnen Sie die Datei GraphTutorial/android/app/build.gradle in einem Editor.
Suchen Sie den
defaultConfig
Eintrag, und fügen Sie die folgende Eigenschaft indefaultConfig
.manifestPlaceholders = [ appAuthRedirectScheme: 'graph-tutorial' ]
Der
defaultConfig
Eintrag sollte etwa wie folgt aussehen.defaultConfig { applicationId "com.graphtutorial" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 1 versionName "1.0" manifestPlaceholders = [ appAuthRedirectScheme: 'graph-tutorial' ] }
Fügen Sie die folgende Zeile am Ende der Datei hinzu.
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
Speichern Sie die Datei.
Entwerfen der App
Die Anwendung verwendet eine Navigationsschublade , um zwischen verschiedenen Ansichten zu navigieren. In diesem Schritt erstellen Sie die grundlegenden Ansichten, die von der App verwendet werden, und implementieren die Navigationsschublade.
Erstellen von Ansichten
In diesem Abschnitt erstellen Sie die Ansichten für die App, um einen Authentifizierungsfluss zu unterstützen.
Öffnen Sie GraphTutorial/index.js , und fügen Sie am Anfang der Datei vor allen anderen
import
Anweisungen Folgendes hinzu.import 'react-native-gesture-handler';
Erstellen Sie eine neue Datei im GraphTutorial-Verzeichnis mit dem Namen "AuthContext.tsx" , und fügen Sie den folgenden Code hinzu.
import * as React from 'react'; type AuthContextType = { signIn: () => Promise<void>; signOut: () => void; } export const AuthContext = React.createContext<AuthContextType>({ signIn: async () => {}, signOut: () => {} });
Erstellen Sie eine neue Datei im GraphTutorial-Verzeichnis mit dem Namen "UserContext.tsx", und fügen Sie den folgenden Code hinzu.
import * as React from 'react'; import { ImageSourcePropType } from 'react-native'; type UserContextType = { userLoading: boolean; userFirstName: string; userFullName: string; userEmail: string; userTimeZone: string; userPhoto: ImageSourcePropType; } export const UserContext = React.createContext<UserContextType>({ userLoading: true, userFirstName: '', userFullName: '', userEmail: '', userTimeZone: '', userPhoto: require('./images/no-profile-pic.png') });
Erstellen Sie ein neues Verzeichnis im GraphTutorial-Verzeichnis namens Bildschirme.
Erstellen Sie eine neue Datei im Verzeichnis "GraphTutorial/screens " mit dem Namen " HomeScreen.tsx". Fügen Sie den folgenden Code in die Datei ein:
import React from 'react'; import { ActivityIndicator, Platform, StyleSheet, Text, View, } from 'react-native'; import { createStackNavigator } from '@react-navigation/stack'; import { UserContext } from '../UserContext'; const Stack = createStackNavigator(); const HomeComponent = () => { const userContext = React.useContext(UserContext); return ( <View style={styles.container}> <ActivityIndicator color={Platform.OS === 'android' ? '#276b80' : undefined} animating={userContext.userLoading} size='large' /> {userContext.userLoading ? null: <Text>Hello {userContext.userFirstName}!</Text>} </View> ); } export default class HomeScreen extends React.Component { render() { return ( <Stack.Navigator> <Stack.Screen name='Home' component={HomeComponent} options={{ headerShown: false }} /> </Stack.Navigator> ); } } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', justifyContent: 'center' } });
Erstellen Sie eine neue Datei im Verzeichnis "GraphTutorial/screens " mit dem Namen "CalendarScreen.tsx". Fügen Sie den folgenden Code in die Datei ein:
import React from 'react'; import {StyleSheet, Text, View} from 'react-native'; import {createStackNavigator} from '@react-navigation/stack'; const Stack = createStackNavigator(); // Temporary placeholder view const CalendarComponent = () => ( <View style={styles.container}> <Text>Calendar</Text> </View> ); export default class CalendarScreen extends React.Component { render() { return ( <Stack.Navigator> <Stack.Screen name='Calendar' component={CalendarComponent} options={{ headerShown: false, }} /> </Stack.Navigator> ); } } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', justifyContent: 'center', }, });
Erstellen Sie eine neue Datei im Verzeichnis "GraphTutorial/screens " mit dem Namen "SignInScreen.tsx". Fügen Sie den folgenden Code in die Datei ein:
// Adapted from https://reactnavigation.org/docs/auth-flow import React from 'react'; import {Alert, Button, StyleSheet, View} from 'react-native'; import {ParamListBase} from '@react-navigation/native'; import {StackNavigationProp} from '@react-navigation/stack'; import {AuthContext} from '../AuthContext'; type SignInProps = { navigation: StackNavigationProp<ParamListBase>; }; export default class SignInScreen extends React.Component<SignInProps> { static contextType = AuthContext; _signInAsync = async () => { await this.context.signIn(); }; componentDidMount() { this.props.navigation.setOptions({ title: 'Please sign in', headerShown: true, }); } render() { return ( <View style={styles.container}> <Button title='Sign In' onPress={this._signInAsync} /> </View> ); } } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', justifyContent: 'center', }, });
Erstellen Sie eine neue Datei im Verzeichnis "GraphTutorial/screens " mit dem Namen "AuthLoadingScreen.tsx". Fügen Sie den folgenden Code in die Datei ein:
// Adapted from https://reactnavigation.org/docs/auth-flow import React from 'react'; import { ActivityIndicator, Platform, Text, StyleSheet, View, } from 'react-native'; export default class AuthLoadingScreen extends React.Component { render() { return ( <View style={styles.container}> <ActivityIndicator color={Platform.OS === 'android' ? '#276b80' : undefined} size='large' /> <Text style={styles.statusText}>Logging in...</Text> </View> ); } } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', justifyContent: 'center' }, statusText: { marginTop: 10 } });
Erstellen einer Navigationsschublade
In diesem Abschnitt erstellen Sie ein Menü für die Anwendung und aktualisieren die Anwendung so, dass die React-Navigation verwendet wird, um zwischen Bildschirmen zu navigieren.
Erstellen Sie ein neues Verzeichnis im GraphTutorial-Verzeichnis namens Menüs.
Erstellen Sie eine neue Datei im Verzeichnis "GraphTutorial/menus" mit dem Namen "DrawerMenu.tsx". Fügen Sie den folgenden Code in die Datei ein:
import React, {FC} from 'react'; import { Alert, Image, StyleSheet, Text, View, ImageSourcePropType, } from 'react-native'; import { createDrawerNavigator, DrawerContentScrollView, DrawerItem, DrawerItemList, DrawerContentComponentProps, } from '@react-navigation/drawer'; import {ParamListBase} from '@react-navigation/native'; import {StackNavigationProp} from '@react-navigation/stack'; import {AuthContext} from '../AuthContext'; import {UserContext} from '../UserContext'; import HomeScreen from '../screens/HomeScreen'; import CalendarScreen from '../screens/CalendarScreen'; const Drawer = createDrawerNavigator(); type CustomDrawerContentProps = DrawerContentComponentProps & { userName: string; userEmail: string; userPhoto: ImageSourcePropType; signOut: () => void; }; type DrawerMenuProps = { navigation: StackNavigationProp<ParamListBase>; }; const CustomDrawerContent: FC<CustomDrawerContentProps> = props => ( <DrawerContentScrollView {...props}> <View style={styles.profileView}> <Image source={props.userPhoto} resizeMode='contain' style={styles.profilePhoto} /> <Text style={styles.profileUserName}>{props.userName}</Text> <Text style={styles.profileEmail}>{props.userEmail}</Text> </View> <DrawerItemList {...props} /> <DrawerItem label='Sign Out' onPress={props.signOut} /> </DrawerContentScrollView> ); export default class DrawerMenuContent extends React.Component<DrawerMenuProps> { static contextType = AuthContext; state = { // TEMPORARY userLoading: true, userFirstName: 'Adele', userFullName: 'Adele Vance', userEmail: 'adelev@contoso.com', userTimeZone: 'UTC', userPhoto: require('../images/no-profile-pic.png'), }; _signOut = async () => { this.context.signOut(); }; async componentDidMount() { this.props.navigation.setOptions({ headerShown: false, }); } render() { const userLoaded = !this.state.userLoading; return ( <UserContext.Provider value={this.state}> <Drawer.Navigator drawerType='front' screenOptions={{ headerShown: true, headerStyle: { backgroundColor: '#276b80', }, headerTintColor: 'white', }} drawerContent={props => ( <CustomDrawerContent {...props} userName={this.state.userFullName} userEmail={this.state.userEmail} userPhoto={this.state.userPhoto} signOut={this._signOut} /> )}> <Drawer.Screen name='Home' component={HomeScreen} options={{drawerLabel: 'Home', headerTitle: 'Welcome'}} /> {userLoaded && ( <Drawer.Screen name='Calendar' component={CalendarScreen} options={{drawerLabel: 'Calendar'}} /> )} </Drawer.Navigator> </UserContext.Provider> ); } } const styles = StyleSheet.create({ container: { flex: 1, }, profileView: { alignItems: 'center', padding: 10, }, profilePhoto: { width: 80, height: 80, borderRadius: 40, }, profileUserName: { fontWeight: '700', }, profileEmail: { fontWeight: '200', fontSize: 10, }, });
Erstellen Sie ein neues Verzeichnis im GraphTutorial-Verzeichnis namens "Images".
Fügen Sie in diesem Verzeichnis ein Standardprofilbild mit dem Namen no-profile-pic.png hinzu. Sie können ein beliebiges Bild verwenden oder das Bild aus diesem Beispiel verwenden.
Öffnen Sie die Datei GraphTutorial/App.tsx , und ersetzen Sie den gesamten Inhalt durch Folgendes.
// Adapted from https://reactnavigation.org/docs/auth-flow import * as React from 'react'; import {NavigationContainer, ParamListBase} from '@react-navigation/native'; import { createStackNavigator, StackNavigationProp, } from '@react-navigation/stack'; import {AuthContext} from './AuthContext'; import SignInScreen from './screens/SignInScreen'; import DrawerMenuContent from './menus/DrawerMenu'; import AuthLoadingScreen from './screens/AuthLoadingScreen'; const Stack = createStackNavigator(); type Props = { navigation: StackNavigationProp<ParamListBase>; }; export default function App({navigation}: Props) { const [state, dispatch] = React.useReducer( (prevState: any, action: any) => { switch (action.type) { case 'RESTORE_TOKEN': return { ...prevState, userToken: action.token, isLoading: false, }; case 'SIGN_IN': return { ...prevState, isSignOut: false, userToken: action.token, }; case 'SIGN_OUT': return { ...prevState, isSignOut: true, userToken: null, }; } }, { isLoading: true, isSignOut: false, userToken: null, }, ); React.useEffect(() => { const bootstrapAsync = async () => { let userToken = null; // TEMPORARY dispatch({type: 'RESTORE_TOKEN', token: userToken}); }; bootstrapAsync(); }, []); const authContext = React.useMemo( () => ({ signIn: async () => { dispatch({type: 'SIGN_IN', token: 'placeholder-token'}); }, signOut: async () => { dispatch({type: 'SIGN_OUT'}); }, }), [], ); return ( <AuthContext.Provider value={authContext}> <NavigationContainer> <Stack.Navigator> {state.isLoading ? ( <Stack.Screen name='Loading' component={AuthLoadingScreen} /> ) : state.userToken == null ? ( <Stack.Screen name='SignIn' component={SignInScreen} /> ) : ( <Stack.Screen name='Main' component={DrawerMenuContent} /> )} </Stack.Navigator> </NavigationContainer> </AuthContext.Provider> ); }
Speichern Sie alle Änderungen.
Laden Sie die Anwendung im Emulator neu.
Das Menü der App sollte funktionieren, um zwischen den beiden Fragmenten zu navigieren und sich zu ändern, wenn Sie auf die Schaltflächen " Anmelden " oder " Abmelden " tippen.
Registrieren der App im Portal
In dieser Übung erstellen Sie eine neue systemeigene Azure AD-Anwendung mithilfe des Azure Active Directory Admin Centers.
Öffnen Sie einen Browser, und navigieren Sie zum Azure Active Directory Admin Center. Melden Sie sich mit einem persönlichen Konto (auch: Microsoft-Konto) oder einem Geschäfts- oder Schulkonto an.
Wählen Sie in der linken Navigationsleiste Azure Active Directory aus, und wählen Sie dann App-Registrierungen unter Verwalten aus.
Wählen Sie Neue Registrierung aus. Legen Sie auf der Seite Anwendung registrieren die Werte wie folgt fest.
- Legen Sie Name auf
React Native Graph Tutorial
fest. - Legen Sie Unterstützte Kontotypen auf Konten in allen Organisationsverzeichnissen und persönliche Microsoft-Konten fest.
- Ändern Sie unter "Umleitungs-URI" das Dropdownmenü in öffentlichen Client (mobile & Desktop), und legen Sie den Wert auf
graph-tutorial://react-native-auth/
.
- Legen Sie Name auf
Wählen Sie Registrieren aus. Kopieren Sie auf der Seite React Native Graph Lernprogramm den Wert der Anwendungs-ID (Client-ID), und speichern Sie sie, sie benötigen Sie im nächsten Schritt.
Hinzufügen der Azure AD-Authentifizierung
In dieser Übung erweitern Sie die Anwendung aus der vorherigen Übung, um die Authentifizierung mit Azure AD zu unterstützen. Dies ist erforderlich, um das erforderliche OAuth-Zugriffstoken abzurufen, um die Microsoft Graph aufzurufen. Zu diesem Zweck integrieren Sie die React-Native-App-Authentifizierungsbibliothek in die Anwendung.
Erstellen Sie ein neues Verzeichnis im GraphTutorial-Verzeichnis mit dem Namen "Authentifizierung".
Erstellen Sie eine neue Datei im Verzeichnis "GraphTutorial/auth" mit dem Namen "AuthConfig.ts". Fügen Sie den folgenden Code in die Datei ein:
export const AuthConfig = { appId: 'YOUR_APP_ID_HERE', appScopes: [ 'openid', 'offline_access', 'profile', 'User.Read', 'MailboxSettings.Read', 'Calendars.ReadWrite' ] };
Ersetzen Sie
YOUR_APP_ID_HERE
dies durch die App-ID aus Ihrer App-Registrierung.
Wichtig
Wenn Sie die Quellcodeverwaltung wie Git verwenden, wäre es jetzt ein guter Zeitpunkt, die Datei "AuthConfig.ts " aus der Quellcodeverwaltung auszuschließen, um zu vermeiden, dass versehentlich Ihre App-ID offengelegt wird.
Implementieren der Anmeldung
In diesem Abschnitt erstellen Sie eine Authentifizierungshilfsklasse und aktualisieren die App so, dass sie sich anmeldet und abmeldet.
Erstellen Sie eine neue Datei im GraphTutorial/auth-Verzeichnis mit dem Namen "AuthManager.ts". Fügen Sie den folgenden Code in die Datei ein:
import AsyncStorage from '@react-native-async-storage/async-storage'; import { authorize, refresh, AuthConfiguration } from 'react-native-app-auth'; import { Platform } from 'react-native'; import moment from 'moment'; import { AuthConfig } from './AuthConfig'; const config: AuthConfiguration = { clientId: AuthConfig.appId, redirectUrl: 'graph-tutorial://react-native-auth/', scopes: AuthConfig.appScopes, additionalParameters: { prompt: 'select_account' }, serviceConfiguration: { authorizationEndpoint: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize', tokenEndpoint: 'https://login.microsoftonline.com/common/oauth2/v2.0/token', } }; export class AuthManager { static signInAsync = async () => { const result = await authorize(config); console.log(result.accessToken); // Store the access token, refresh token, and expiration time in storage await AsyncStorage.setItem('userToken', result.accessToken); await AsyncStorage.setItem('refreshToken', result.refreshToken); await AsyncStorage.setItem('expireTime', result.accessTokenExpirationDate); } static signOutAsync = async () => { // Clear storage await AsyncStorage.removeItem('userToken'); await AsyncStorage.removeItem('refreshToken'); await AsyncStorage.removeItem('expireTime'); } static getAccessTokenAsync = async() => { const expireTime = await AsyncStorage.getItem('expireTime'); if (expireTime !== null) { // Get expiration time - 5 minutes // If it's <= 5 minutes before expiration, then refresh const expire = moment(expireTime).subtract(5, 'minutes'); const now = moment(); if (now.isSameOrAfter(expire)) { // Expired, refresh console.log('Refreshing token'); const refreshToken = await AsyncStorage.getItem('refreshToken'); console.log(`Refresh token: ${refreshToken}`); const result = await refresh(config, { refreshToken: refreshToken || '' }); // Store the new access token, refresh token, and expiration time in storage await AsyncStorage.setItem('userToken', result.accessToken); await AsyncStorage.setItem('refreshToken', result.refreshToken || ''); await AsyncStorage.setItem('expireTime', result.accessTokenExpirationDate); return result.accessToken; } // Not expired, just return saved access token const accessToken = await AsyncStorage.getItem('userToken'); return accessToken; } return null; } }
Öffnen Sie die Datei GraphTutorial/App.tsx , und fügen Sie die folgende
import
Anweisung am Anfang der Datei hinzu.import {AuthManager} from './auth/AuthManager';
Ersetzen Sie die vorhandene
authContext
Deklaration durch Folgendes.const authContext = React.useMemo( () => ({ signIn: async () => { await AuthManager.signInAsync(); const token = await AuthManager.getAccessTokenAsync(); dispatch({ type: 'SIGN_IN', token: token }); }, signOut: async () => { await AuthManager.signOutAsync(); dispatch({ type: 'SIGN_OUT' }); } }), [] );
Öffnen Sie die Datei GraphTutorial/menus/DrawerMenu.tsx , und fügen Sie die folgende
import
Anweisung am Anfang der Datei hinzu.import {AuthManager} from '../auth/AuthManager';
Fügen Sie der Funktion den
componentDidMount
folgenden Code hinzu.try { const accessToken = await AuthManager.getAccessTokenAsync(); // TEMPORARY this.setState({userFirstName: accessToken, userLoading: false}); } catch (error) { Alert.alert( 'Error getting token', JSON.stringify(error), [ { text: 'OK' } ], { cancelable: false } ); }
Speichern Sie Ihre Änderungen, und laden Sie die Anwendung im Emulator neu.
Wenn Sie sich bei der App anmelden, sollte ein Zugriffstoken auf der Willkommensseite angezeigt werden.
Benutzerdetails abrufen
In diesem Abschnitt erstellen Sie einen benutzerdefinierten Authentifizierungsanbieter für die Graph Clientbibliothek, erstellen eine Hilfsklasse für alle Aufrufe von Microsoft Graph und aktualisieren die DrawerMenuContent
Klasse so, dass diese neue Klasse verwendet wird, um den angemeldeten Benutzer abzurufen.
Erstellen Sie ein neues Verzeichnis im GraphTutorial-Verzeichnis mit dem Namen "graph".
Erstellen Sie eine neue Datei im GraphTutorial/graph-Verzeichnis mit dem Namen "GraphAuthProvider.ts". Fügen Sie den folgenden Code in die Datei ein:
import { AuthManager } from '../auth/AuthManager'; // Used by Graph client to get access tokens // See https://github.com/microsoftgraph/msgraph-sdk-javascript/blob/dev/docs/CustomAuthenticationProvider.md export class GraphAuthProvider { getAccessToken = async() => { const token = await AuthManager.getAccessTokenAsync(); return token || ''; } }
Erstellen Sie eine neue Datei im GraphTutorial/graph-Verzeichnis mit dem Namen "GraphManager.ts". Fügen Sie den folgenden Code in die Datei ein:
import {Client} from '@microsoft/microsoft-graph-client'; import {GraphAuthProvider} from './GraphAuthProvider'; // Set the authProvider to an instance // of GraphAuthProvider const clientOptions = { authProvider: new GraphAuthProvider(), }; // Initialize the client const graphClient = Client.initWithMiddleware(clientOptions); export class GraphManager { static getUserAsync = async () => { // GET /me return await graphClient .api('/me') .select('displayName,givenName,mail,mailboxSettings,userPrincipalName') .get(); }; }
Öffnen Sie die Datei GraphTutorial/views/DrawerMenu.tsx , und fügen Sie die folgenden
import
Anweisungen am Anfang der Datei hinzu.import {GraphManager} from '../graph/GraphManager'; import * as MicrosoftGraph from '@microsoft/microsoft-graph-types';
Ersetzen Sie die
componentDidMount
Methode durch Folgendes.async componentDidMount() { this.props.navigation.setOptions({ headerShown: false, }); try { // Get the signed-in user from Graph const user: MicrosoftGraph.User = await GraphManager.getUserAsync(); // Update UI with display name and email this.setState({ userLoading: false, userFirstName: user.givenName!, userFullName: user.displayName!, // Work/School accounts have email address in mail attribute // Personal accounts have it in userPrincipalName userEmail: user.mail! || user.userPrincipalName!, userTimeZone: user.mailboxSettings?.timeZone! }); } catch(error) { Alert.alert( 'Error getting user', JSON.stringify(error), [ { text: 'OK' } ], { cancelable: false } ); } }
Wenn Sie Ihre Änderungen speichern und die App jetzt neu laden, wird die Benutzeroberfläche nach der Anmeldung mit dem Anzeigenamen und der E-Mail-Adresse des Benutzers aktualisiert.
Abrufen einer Kalenderansicht
In dieser Übung integrieren Sie die Microsoft Graph in die Anwendung. Für diese Anwendung verwenden Sie die Microsoft Graph JavaScript-Clientbibliothek, um Aufrufe an Microsoft Graph zu tätigen.
Abrufen von Kalenderereignissen von Outlook
In diesem Abschnitt erweitern Sie die GraphManager
Klasse, um eine Funktion hinzuzufügen, um die Ereignisse des Benutzers für die aktuelle Woche abzurufen und zu aktualisieren CalendarScreen
, um diese neuen Funktionen zu verwenden.
Öffnen Sie die Datei GraphTutorial/graph/GraphManager.tsx , und fügen Sie der Klasse die
GraphManager
folgende Methode hinzu.static getCalendarView = async(start: string, end: string, timezone: string) => { // GET /me/calendarview return await graphClient.api('/me/calendarview') .header('Prefer', `outlook.timezone="${timezone}"`) .query({ startDateTime: start, endDateTime: end}) // $select='subject,organizer,start,end' // Only return these fields in results .select('subject,organizer,start,end') // $orderby=createdDateTime DESC // Sort results by when they were created, newest first .orderby('start/dateTime') .top(50) .get(); }
Hinweis
Überlegen Sie, was der Code
getCalendarView
macht.- Die URL, die aufgerufen wird, lautet
/v1.0/me/calendarView
. - Die
header
Funktion fügt der Anforderung denPrefer: outlook.timezone
Header hinzu, wodurch sich die Zeiten in der Antwort in der bevorzugten Zeitzone des Benutzers befinden. - Die
query
Funktion fügt diestartDateTime
Parameter hinzu undendDateTime
definiert das Zeitfenster für die Kalenderansicht. - Die
select
Funktion beschränkt die für jedes Ereignis zurückgegebenen Felder auf die Felder, die die App tatsächlich verwendet. - Die
orderby
Funktion sortiert die Ergebnisse nach der Startzeit. - Die
top
Funktion beschränkt die Ergebnisse auf die ersten 50 Ereignisse.
- Die URL, die aufgerufen wird, lautet
Öffnen Sie GraphTutorial/views/CalendarScreen.tsx , und ersetzen Sie den gesamten Inhalt durch den folgenden Code.
import React from 'react'; import { ActivityIndicator, Alert, FlatList, Modal, Platform, ScrollView, StyleSheet, Text, View, } from 'react-native'; import {createStackNavigator} from '@react-navigation/stack'; import * as MicrosoftGraph from '@microsoft/microsoft-graph-types'; import moment from 'moment-timezone'; import {findIana} from 'windows-iana'; import {UserContext} from '../UserContext'; import {GraphManager} from '../graph/GraphManager'; const Stack = createStackNavigator(); const CalendarState = React.createContext<CalendarScreenState>({ loadingEvents: true, events: [], }); type CalendarScreenState = { loadingEvents: boolean; events: MicrosoftGraph.Event[]; }; // Temporary JSON view const CalendarComponent = () => { const calendarState = React.useContext(CalendarState); return ( <View style={styles.container}> <Modal visible={calendarState.loadingEvents}> <View style={styles.loading}> <ActivityIndicator color={Platform.OS === 'android' ? '#276b80' : undefined} animating={calendarState.loadingEvents} size='large' /> </View> </Modal> <ScrollView> <Text>{JSON.stringify(calendarState.events, null, 2)}</Text> </ScrollView> </View> ); }; export default class CalendarScreen extends React.Component { static contextType = UserContext; state: CalendarScreenState = { loadingEvents: true, events: [], }; async componentDidMount() { try { const tz = this.context.userTimeZone || 'UTC'; // Convert user's Windows time zone ("Pacific Standard Time") // to IANA format ("America/Los_Angeles") // Moment.js needs IANA format const ianaTimeZone = findIana(tz)[0]; // Get midnight on the start of the current week in the user's // time zone, but in UTC. For example, for PST, the time value // would be 07:00:00Z const startOfWeek = moment .tz(ianaTimeZone!.valueOf()) .startOf('week') .utc(); const endOfWeek = moment(startOfWeek).add(7, 'day'); const events = await GraphManager.getCalendarView( startOfWeek.format(), endOfWeek.format(), tz, ); this.setState({ loadingEvents: false, events: events.value, }); } catch (error) { Alert.alert( 'Error getting events', JSON.stringify(error), [ { text: 'OK', }, ], {cancelable: false}, ); } } render() { return ( <CalendarState.Provider value={this.state}> <Stack.Navigator> <Stack.Screen name='Calendar' component={CalendarComponent} options={{ headerShown: false, }} /> </Stack.Navigator> </CalendarState.Provider> ); } } const styles = StyleSheet.create({ container: { flex: 1, }, loading: { flex: 1, justifyContent: 'center', alignItems: 'center', }, eventItem: { padding: 10, }, eventSubject: { fontWeight: '700', fontSize: 18, }, eventOrganizer: { fontWeight: '200', }, eventDuration: { fontWeight: '200', }, });
Sie können jetzt die App ausführen, sich anmelden und im Menü auf das Navigationselement "Kalender " tippen. Es sollte ein JSON-Dump der Ereignisse in der App angezeigt werden.
Anzeigen der Ergebnisse
Jetzt können Sie das JSON-Dump durch etwas ersetzen, um die Ergebnisse auf benutzerfreundliche Weise anzuzeigen. In diesem Abschnitt fügen Sie dem Kalenderbildschirm einen FlatList
hinzu, um die Ereignisse zu rendern.
Fügen Sie die folgende Methode oberhalb der
CalendarScreen
Klassendeklaration hinzu.const convertDateTime = (dateTime: string): string => { return moment(dateTime).format('MMM Do H:mm a'); };
Ersetzen Sie die
ScrollView
in derCalendarComponent
Methode durch Folgendes.<FlatList data={calendarState.events} renderItem={({item}) => ( <View style={styles.eventItem}> <Text style={styles.eventSubject}>{item.subject}</Text> <Text style={styles.eventOrganizer}> {item.organizer!.emailAddress!.name} </Text> <Text style={styles.eventDuration}> {convertDateTime(item.start!.dateTime!)} -{' '} {convertDateTime(item.end!.dateTime!)} </Text> </View> )} />
Führen Sie die App aus, melden Sie sich an, und tippen Sie auf das Navigationselement "Kalender ". Die Liste der Ereignisse sollte angezeigt werden.
Erstellen eines neuen Ereignisses
In diesem Abschnitt fügen Sie die Möglichkeit hinzu, Ereignisse im Kalender des Benutzers zu erstellen.
Erstellen des neuen Ereignisbildschirms
Öffnen Sie ./graph/GraphManager.ts , und fügen Sie der Klasse die
GraphManager
folgende Funktion hinzu.static createEvent = async(newEvent: any) => { // POST /me/events await graphClient.api('/me/events') .post(newEvent); }
Diese Funktion verwendet das Graph SDK, um ein neues Ereignis zu erstellen.
Create a new file in the ./screens named NewEventScreen.tsx and add the following code.
import React from 'react'; import { ActivityIndicator, Alert, Button, Modal, Platform, ScrollView, StyleSheet, Text, View, } from 'react-native'; import DateTimePicker from '@react-native-community/datetimepicker'; import { createStackNavigator } from '@react-navigation/stack'; import * as MicrosoftGraph from '@microsoft/microsoft-graph-types'; import moment from 'moment-timezone'; import { UserContext } from '../UserContext'; import { GraphManager } from '../graph/GraphManager'; import { TextInput } from 'react-native-gesture-handler'; const Stack = createStackNavigator(); const NewEventState = React.createContext<NewEventState>({ isCreating: false, subject: '', attendees: '', body: '', timeZone: '', startDate: new Date(), endDate: new Date(), disableCreate: () => { return true }, updateValue: () => {} }); type NewEventState = { isCreating: boolean; subject: string; attendees: string; body: string; timeZone: string; startDate: Date; endDate: Date; disableCreate: () => boolean; updateValue: (newValue: string | Date | boolean, fieldName: string) => void; } type DateTimeInputProps = { value: Date; onChange: (event: Event, newValue: Date | undefined) => void; } // The picker acts very differently on Android and iOS // iOS can use a single picker for both date and time, // where Android requires two. Also the iOS version can // be displayed all the time, while the Android version is a // modal pop-up. Encapsulating this into a reusable component const DateTimeInput = (props: DateTimeInputProps) => { const [showDatePicker, setShowDatePicker] = React.useState(false); const [showTimePicker, setShowTimePicker] = React.useState(Platform.OS === 'ios'); return ( <View style={Platform.OS === 'android' ? styles.dateTime : null}> { Platform.OS === 'android' && <Text style={styles.time} onPress={()=>{setShowTimePicker(true)}}> {formatTime(props.value)} </Text> } { showTimePicker && <DateTimePicker mode={Platform.OS === 'ios' ? 'datetime' : 'time'} value={props.value} onChange={(e, d) => { setShowTimePicker(Platform.OS === 'ios'); if (d) props.onChange(e,d); }} /> } { Platform.OS === 'android' && <Text style={styles.date} onPress={()=>{setShowDatePicker(true)}}> {formatDate(props.value)} </Text> } { showDatePicker && Platform.OS === 'android' && <DateTimePicker mode='date' value={props.value} onChange={(e, d) => { setShowDatePicker(Platform.OS === 'ios'); if (d) props.onChange(e,d); }} /> } </View> ) } const NewEventComponent = () => { const newEventState = React.useContext(NewEventState); const createEvent = async () => { newEventState.updateValue(true, 'isCreating'); // Create a new Event object with the // required fields const newEvent: MicrosoftGraph.Event = { subject: newEventState.subject, start: { dateTime: moment(newEventState.startDate).format('YYYY-MM-DDTHH:mm:ss'), timeZone: newEventState.timeZone }, end: { dateTime: moment(newEventState.endDate).format('YYYY-MM-DDTHH:mm:ss'), timeZone: newEventState.timeZone } }; // Only add attendees if the user specified them if (newEventState.attendees.length > 0) { newEvent.attendees = []; // Value should be a ;-delimited list of email addresses // NOTE: The app does no validation of this const emails = newEventState.attendees.split(';') emails.forEach((email) => { newEvent.attendees!.push({ emailAddress: { address: email } }); }); } // Only add body if the user specified one if (newEventState.body.length > 0) { newEvent.body = { content: newEventState.body, // For simplicity, add it as a plain-text body contentType: 'text' }; } await GraphManager.createEvent(newEvent); Alert.alert('Success', 'Event created', [ { text: 'OK', onPress: () => { newEventState.updateValue(false, 'isCreating'); } } ] ); } return ( <ScrollView style={styles.container}> <Modal visible={newEventState.isCreating}> <View style={styles.loading}> <ActivityIndicator color={Platform.OS === 'android' ? '#276b80' : undefined} animating={newEventState.isCreating} size='large' /> </View> </Modal> <View style={styles.formField}> <Text style={styles.fieldLabel}>Subject</Text> <TextInput style={styles.textInput} value={newEventState.subject} onChangeText={(text) => newEventState.updateValue(text, 'subject')} /> </View> <View style={styles.formField}> <Text style={styles.fieldLabel}>Attendees</Text> <TextInput style={styles.textInput} placeholder="Email (separate multiple with ';')" value={newEventState.attendees} onChangeText={(text) => newEventState.updateValue(text, 'attendees')} /> </View> <View style={styles.formField}> <Text style={styles.fieldLabel}>Start</Text> <DateTimeInput value={newEventState.startDate} onChange={(e, date) => newEventState.updateValue(date!, 'startDate')} /> </View> <View style={styles.formField}> <Text style={styles.fieldLabel}>End</Text> <DateTimeInput value={newEventState.endDate} onChange={(e, date) => newEventState.updateValue(date!, 'endDate')} /> </View> <View style={styles.formField}> <TextInput style={styles.multiLineTextInput} multiline={true} textAlignVertical='top' placeholder='Body' value={newEventState.body} onChangeText={(text) => newEventState.updateValue(text, 'body')} /> </View> <View style={styles.formField}> <Button title="Create" disabled={newEventState.disableCreate()} onPress={createEvent}/> </View> </ScrollView> ); } const formatTime = (dateTime: Date): string => { return moment(dateTime).format('h:mm A'); } const formatDate = (dateTime: Date): string => { return moment(dateTime).format('MMM D, YYYY'); } // When first loading the form, set the start time // to the nearest hour or half-hour const getDefaultStart = (): Date => { const now = moment().startOf('minute'); const offset = 30 - (now.minute() % 30); return now.add(offset, 'minutes').toDate(); } // When first loading the form, set the end time // to start + 30 min const getDefaultEnd = (): Date => { return moment(getDefaultStart()).add(30, 'minutes').toDate(); } export default class NewEventScreen extends React.Component { static contextType = UserContext; // Disable the create button if: // - App is waiting for the result of create request // - Subject is empty // - Start time is after end time disableCreate = () => { return this.state.isCreating || this.state.subject.length <= 0 || moment(this.state.startDate).isAfter(this.state.endDate); } onStateValueChange = (newValue: string | Date | boolean, fieldName: string) => { this.setState({ [fieldName]: newValue }); } state: NewEventState = { isCreating: false, subject: '', attendees: '', body: '', timeZone: this.context.userTimeZone, startDate: getDefaultStart(), endDate: getDefaultEnd(), disableCreate: this.disableCreate, updateValue: this.onStateValueChange }; render() { return ( <NewEventState.Provider value={this.state}> <Stack.Navigator> <Stack.Screen name='NewEvent' component={ NewEventComponent } options={{ headerShown: false }} /> </Stack.Navigator> </NewEventState.Provider> ); } } const styles = StyleSheet.create({ container: { flex: 1 }, loading: { flex: 1, justifyContent: 'center', alignItems: 'center' }, formField: { paddingHorizontal: 10, paddingVertical: 5 }, fieldLabel: { fontWeight: '700', marginBottom: 10 }, textInput: { borderColor: 'gray', borderWidth: 1, height: 40, padding: 10 }, multiLineTextInput: { borderColor: 'gray', borderWidth: 1, height: 200, padding: 10 }, time: { padding: 10, backgroundColor: '#e6e6e6', color: '#147efb', marginRight: 10 }, date: { padding: 10, backgroundColor: '#e6e6e6', color: '#147efb' }, dateTime: { flexDirection: 'row' } });
Überlegen Sie, was die
createEvent
Funktion bewirkt. Es erstellt einMicrosoftGraph.Event
Objekt mithilfe der Werte aus dem Formular und übergibt dieses Objekt dann an dieGraphManager.createEvent
Funktion.Öffnen Sie "./menus/DrawerMenu.tsx" , und fügen Sie die folgende
import
Anweisung am Anfang der Datei hinzu.import NewEventScreen from '../screens/NewEventScreen';
Fügen Sie den folgenden Code innerhalb des
<Drawer.Navigator>
Elements direkt über der</Drawer.Navigator>
Zeile hinzu.{userLoaded && ( <Drawer.Screen name='NewEvent' component={NewEventScreen} options={{drawerLabel: 'New event'}} /> )}
Speichern Sie Ihre Änderungen, und starten Sie die App neu, oder aktualisieren Sie sie. Wählen Sie im Menü die Option "Neues Ereignis " aus, um zum neuen Ereignisformular zu gelangen.
Füllen Sie das Formular aus, und wählen Sie " Erstellen" aus.
Herzlichen Glückwunsch!
Sie haben das Lernprogramm React Native Microsoft Graph abgeschlossen. Nachdem Sie nun über eine funktionierende App verfügen, die Microsoft Graph aufruft, können Sie experimentieren und neue Features hinzufügen. Besuchen Sie die Übersicht über Microsoft Graph, um alle Daten anzuzeigen, auf die Sie mit Microsoft Graph zugreifen können.
Feedback
Bitte geben Sie Feedback zu diesem Lernprogramm im GitHub Repository.
Liegt ein Problem mit diesem Abschnitt vor? Wenn ja, senden Sie uns Feedback, damit wir den Abschnitt verbessern können.