Skip to content

Instantly share code, notes, and snippets.

@kasrak
Created May 22, 2020 22:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save kasrak/f1fb524f9364f8cfc594bca60dcc4248 to your computer and use it in GitHub Desktop.
Save kasrak/f1fb524f9364f8cfc594bca60dcc4248 to your computer and use it in GitHub Desktop.
export const mapsToken = 'Your Google Maps API token';
export const weatherToken = 'Your OpenWeatherMap API token';
export const fromPhoneNumber = 'Your Twilio phone number';
export const twilioSid = 'Your Twilio SID';
export const twilioToken = 'Your Twilio token';
import React, {useState, useEffect} from 'react';
import {cursor} from '@airtable/blocks';
import {
initializeBlock,
useBase,
useRecordById,
useLoadable,
useWatchable,
Box,
Text,
Button,
} from '@airtable/blocks/ui';
import {
mapsToken,
weatherToken,
fromPhoneNumber,
twilioSid,
twilioToken,
} from './config';
function ScoutBlock() {
const [selectedRecordId, setSelectedRecordId] = useState(null);
useLoadable(cursor);
useWatchable(cursor, ['selectedRecordIds'], () => {
if (cursor.selectedRecordIds.length > 0) {
setSelectedRecordId(cursor.selectedRecordIds[0]);
}
});
useWatchable(cursor, ['activeTableId', 'activeViewId'], () => {
setSelectedRecordId(null);
});
const base = useBase();
const table = base.getTable('Filming Locations');
let content;
if (cursor.activeTableId !== table.id || !selectedRecordId) {
content = <Text>Select a record from the “{table.name}” table to see previews.</Text>;
} else {
content = <RecordPreview table={table} selectedRecordId={selectedRecordId} />;
}
return (
<Box
position="absolute"
width="100vw"
height="100vh"
display="flex"
flexDirection="column"
alignItems="center" justifyContent="center">
{content}
</Box>
);
}
function Weather({lat, lng}) {
const [forecast, setForecast] = useState(null);
useEffect(() => {
async function fetchForecast() {
const response = await fetch(`https://api.openweathermap.org/data/2.5/weather?units=imperial&lat=${lat}&lon=${lng}&appid=${weatherToken}`);
const json = await response.json();
setForecast(json);
}
fetchForecast();
}, [lat, lng]);
return forecast ? (
<>
{Math.round(forecast.main.temp)}°F
<br />
{forecast.weather[0].description}
</>
) : <>Loading weather...</>;
}
function SendSmsButton({message}) {
const base = useBase();
const [sendingProgress, setSendingProgress] = useState(null);
async function sendSms() {
setSendingProgress(0);
const crew = await base.getTable('Crew').selectRecordsAsync();
let progress = 0;
for (const crewMember of crew.records) {
const toPhoneNumber = crewMember.getCellValue('Phone Number');
const formData = new FormData();
formData.append('To', toPhoneNumber);
formData.append('From', fromPhoneNumber);
formData.append('Body', message);
await fetch(
`https://api.twilio.com/2010-04-01/Accounts/${twilioSid}/Messages.json`,
{
method: 'POST',
body: formData,
headers: {
Authorization: 'Basic ' + btoa(`${twilioSid}:${twilioToken}`),
},
},
);
progress++;
setSendingProgress(progress / crew.records.length);
}
setSendingProgress(null);
}
return (
<Button
variant="primary"
icon="phone"
disabled={sendingProgress !== null}
onClick={sendSms}
>
{sendingProgress ? `Sending SMS... (${Math.round(sendingProgress * 100)}%)` : 'Meet here!'}
</Button>
);
}
// Shows a preview, or a message about what the user should do to see a preview.
function RecordPreview({
table,
selectedRecordId,
}) {
// Triggers a re-render if the record changes. Preview URL cell value
// might have changed, or record might have been deleted.
const selectedRecord = useRecordById(table, selectedRecordId ? selectedRecordId : '', {
fields: ['Address', 'Lat', 'Lng'],
});
const address = selectedRecord.getCellValue('Address');
const lat = selectedRecord.getCellValue('Lat');
const lng = selectedRecord.getCellValue('Lng');
if (!address) {
return <Text>The address field is empty</Text>;
} else {
const previewUrl = `https://google.com/maps/embed/v1/streetview?key=${mapsToken}&location=${lat},${lng}&fov=100`;
return (
<>
<iframe
key={previewUrl}
style={{flex: 'auto', width: '100%'}}
src={previewUrl}
frameBorder="0"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
/>
<Box
display="flex"
alignItems="center"
justifyContent="space-between"
backgroundColor="#111"
paddingX={2}
paddingY={1}
textColor="white"
fontSize="large"
width="100%">
<Weather lat={lat} lng={lng} />
<SendSmsButton message={`Hello! Please meet at ${address}`} />
</Box>
</>
);
}
}
initializeBlock(() => <ScoutBlock />);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment