Last active
February 13, 2021 17:08
-
-
Save vgribok/6de9e51996f174129e85185b4d9520f8 to your computer and use it in GitHub Desktop.
An expanded sample for the "Building Offline-first Cross-platform Apps with Expo and Amplify DataStore" youtube video
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import * as React from 'react' | |
| import Constants from 'expo-constants' | |
| import Amplify from '@aws-amplify/core' | |
| import { DataStore } from '@aws-amplify/datastore' | |
| import { TapGestureHandler, State } from 'react-native-gesture-handler' | |
| import { | |
| Text, | |
| View, | |
| TextInput, | |
| Button, | |
| ScrollView, | |
| SafeAreaView, | |
| } from 'react-native' | |
| import { Message } from './models' | |
| import config from './aws-exports' | |
| Amplify.configure(config) | |
| // styles | |
| const container = { flex: 1, margin: 10, marginTop: Constants.statusBarHeight } | |
| const input = { marginBottom: 10, padding: 7, backgroundColor: '#ddd' } | |
| const heading = { fontWeight: 'normal', fontSize: 30 } | |
| const messageBg = { backgroundColor: 'white' } | |
| const messageStyle = { padding: 10, marginTop: 7, borderRadius: 4 } | |
| const messageTitle = { margin: 0, padding: 9, fontSize: 20 } | |
| // Application | |
| export default function App() { | |
| return ( | |
| <SafeAreaView style={container}> | |
| <Text style={heading}>Real Time Message Board</Text> | |
| <NewMessageForm /> | |
| <MessageListView /> | |
| </SafeAreaView> | |
| ) | |
| } | |
| // NewMessageForm component | |
| const initialState = { color: 'black', title: '' } | |
| function NewMessageForm() { | |
| const [formState, updateFormState] = React.useState(initialState) | |
| const onChangeText = (key) => (value) => | |
| updateFormState({ ...formState, [key]: value }) | |
| async function createMessage() { | |
| if (!formState.title) return | |
| await DataStore.save(new Message({ ...formState })) | |
| updateFormState(initialState) | |
| } | |
| return ( | |
| <View> | |
| <TextInput | |
| onChangeText={onChangeText('title')} | |
| placeholder="Message title" | |
| value={formState.title} | |
| style={input} | |
| /> | |
| <TextInput | |
| onChangeText={onChangeText('color')} | |
| placeholder="Message color" | |
| value={formState.color} | |
| style={input} | |
| autoCapitalize="none" | |
| /> | |
| <Text>Color:</Text> | |
| <Text style={{ fontWeight: 'bold', color: formState.color }}> | |
| {formState.color} | |
| </Text> | |
| <Button onPress={createMessage} title="Create Message" /> | |
| </View> | |
| ) | |
| } | |
| // MessageListView component | |
| function MessageListView() { | |
| const doubleTapRef = React.useRef(null) | |
| const [messages, updateMessages] = React.useReducer( | |
| updateMessagesReducer, | |
| null, | |
| fetchMessages | |
| ) | |
| React.useEffect(() => { | |
| const subscription = DataStore.observe(Message).subscribe(updateMessages) | |
| return () => subscription.unsubscribe() | |
| }, [messages]) | |
| function fetchMessages() { | |
| DataStore.query(Message).then((msgs) => { | |
| console.log(`fetched ${msgs.length} messages`) | |
| updateMessages(msgs) | |
| }) | |
| return {} | |
| } | |
| const deleteItem = (message) => (event) => { | |
| if (event.nativeEvent.state === State.ACTIVE) { | |
| DataStore.delete(message) | |
| } | |
| } | |
| return ( | |
| <ScrollView> | |
| {Object.entries(messages).map(([msgId, message]) => ( | |
| <TapGestureHandler | |
| key={msgId} | |
| ref={doubleTapRef} | |
| onHandlerStateChange={deleteItem(message)} | |
| numberOfTaps={2} | |
| > | |
| <View | |
| style={{ ...messageStyle, backgroundColor: message.color }} | |
| > | |
| <View style={messageBg}> | |
| <Text style={messageTitle}>{message.title}</Text> | |
| </View> | |
| </View> | |
| </TapGestureHandler> | |
| ))} | |
| </ScrollView> | |
| ) | |
| } | |
| // updateMessagesReducer | |
| function updateMessagesReducer(messages, subMessage) { | |
| if (!subMessage) return messages | |
| // initial update after fetching entries from DataStore | |
| if (subMessage.length && Object.keys(messages).length === 0) { | |
| return Object.fromEntries(subMessage.map((msg) => [msg.id, msg])) | |
| } | |
| // update after individual CRUD operations | |
| const msg = subMessage.element | |
| const opType = subMessage.opType | |
| const updated = { ...messages } | |
| switch (opType) { | |
| case 'INSERT': | |
| case 'UPDATE': | |
| updated[msg.id] = msg | |
| break; | |
| case 'DELETE': | |
| delete updated[msg.id] | |
| break; | |
| default: | |
| console.warn(`Amplify DataStore operation ${opType} is not supported`) | |
| } | |
| return updated | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment