Skip to content

Instantly share code, notes, and snippets.

@vgribok
Last active February 13, 2021 17:08
Show Gist options
  • Save vgribok/6de9e51996f174129e85185b4d9520f8 to your computer and use it in GitHub Desktop.
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
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