diff --git a/frontend/SettingsForm.js b/frontend/SettingsForm.js new file mode 100644 index 0000000..8885d20 --- /dev/null +++ b/frontend/SettingsForm.js @@ -0,0 +1,98 @@ +import PropTypes from 'prop-types'; +import React, {Fragment} from 'react'; +import {Field, FieldType, Table} from '@airtable/blocks/models'; +import { + Box, + Button, + FieldPickerSynced, + FormField, + Heading, + SelectButtonsSynced, + TablePickerSynced, +} from '@airtable/blocks/ui'; + +import {ConfigKeys, IsEnforced} from './settings'; + +function SettingsForm({setIsSettingsVisible, settings}) { + return ( + + + Settings + + + + {settings.table && ( + + + + + + )} + + + + + + + + + ); +} + +SettingsForm.propTypes = { + setIsSettingsVisible: PropTypes.func.isRequired, + settings: PropTypes.shape({ + isEnforced: PropTypes.bool, + table: PropTypes.instanceOf(Table), + urlField: PropTypes.instanceOf(Field), + }).isRequired, +}; + +export default SettingsForm; diff --git a/frontend/index.js b/frontend/index.js index 0e78650..143849e 100644 --- a/frontend/index.js +++ b/frontend/index.js @@ -6,23 +6,58 @@ import { useBase, useRecordById, useLoadable, + useSettingsButton, + useViewport, useWatchable, Box, - Text, - TextButton, Dialog, - Link, Heading, + Link, + Text, + TextButton, } from '@airtable/blocks/ui'; +import {useSettings} from './settings'; +import SettingsForm from './SettingsForm'; + // How this block chooses a preview to show: // -// - The user selects a row in grid view. -// - The block looks in the selected field for a supported URL +// Without a specified Table & Field: +// - The user selects a row in grid view. +// - The block looks in the Selected Field for a supported URL // (e.g. https://www.youtube.com/watch?v=KYz2wyBy3kc) -// - The block uses this URL to construct an embed URL and inserts this URL into an iframe. - +// - The block uses this URL to construct an embed URL and inserts this URL into an iframe. +// +// With a Specified Table & Specified Field: +// +// - The user may use "Settings" to set a Specified Table and Specified Field for URL previews. +// - The user may use "Settings" to toggle the Specified Table and Specified Field constraint. +// - The user selects a row in grid view. +// - If the Selected Field in the Active Table match the Specified Field & Specified Table, then: +// - The block looks in the Selected Field for a supported URL +// (e.g. https://www.youtube.com/watch?v=KYz2wyBy3kc) +// - If the block supports this URL, then: +// - The block uses this URL to construct an embed URL and inserts this URL into an iframe. +// - Else, +// - Display: "Select a cell to see a preview, View supported URLs" +// - Else, +// - If the Active Table does not match the Specified Table, then: +// - Display: "Switch to the “[Specified Table]” table to see previews." +// - If the Selected Field does match the Specified Field, then: +// - Display: "Switch to the “[Specified Field]” field to see previews." +// +// function UrlPreviewBlock() { + const viewport = useViewport(); + const [isSettingsVisible, setIsSettingsVisible] = useState(false); + useSettingsButton(() => { + if (!isSettingsVisible) { + viewport.enterFullscreenIfPossible(); + } + setIsSettingsVisible(!isSettingsVisible); + }); + const settingsValidationResult = useSettings(); + // Caches the currently selected record and field in state. If the user // selects a record and a preview appears, and then the user de-selects the // record (but does not select another), the preview will remain. This is @@ -65,32 +100,77 @@ function UrlPreviewBlock() { }); const base = useBase(); - const table = base.getTableByIdIfExists(cursor.activeTableId); + const activeTable = base.getTableByIdIfExists(cursor.activeTableId); - // table is briefly null when switching to a newly created table. - if (!table) { + // activeTable is briefly null when switching to a newly created activeTable. + if (!activeTable) { return null; } return ( - + + {isSettingsVisible ? ( + + ) : ( + + )} + ); } // Shows a preview, or a message about what the user should do to see a preview. -function RecordPreview({table, selectedRecordId, selectedFieldId}) { +function RecordPreview({activeTable, settingsValidationResult, selectedRecordId, selectedFieldId}) { + const {settings, isValid, message} = settingsValidationResult; + const {isEnforced, urlField} = settings; const [isDialogOpen, setIsDialogOpen] = useState(false); + let table = activeTable; + let content; + + if (!isValid) { + content = ( + + {message} + + ); + } + + // If the creator has specified a Table and Field for URL previews... + if (isEnforced && settings.table) { + table = settings.table; + + if (!content) { + if (cursor.activeTableId !== table.id) { + content = ( + + Switch to the “{table.name}” table to see previews. + + ); + } else { + if (urlField.id !== selectedFieldId) { + content = ( + + Switch to the “{urlField.name}” field to see previews. + + ); + } + } + } + } + // We use getFieldByIdIfExists because the field might be deleted. const selectedField = selectedFieldId ? table.getFieldByIdIfExists(selectedFieldId) : null; - - // 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: [selectedField], + // When an explicit urlField exists, limit lookup to that field, + // otherwise, use the selectedField + fields: [(isEnforced && urlField) || selectedField], }); // Triggers a re-render if the user switches table or view. @@ -104,10 +184,10 @@ function RecordPreview({table, selectedRecordId, selectedFieldId}) { ); - let content; if ( - cursor.activeViewId === null || // activeViewId is briefly null when switching views - table.getViewById(cursor.activeViewId).type !== ViewType.GRID + !content && + (cursor.activeViewId === null || // activeViewId is briefly null when switching views + table.getViewById(cursor.activeViewId).type !== ViewType.GRID) ) { content = ( @@ -128,42 +208,45 @@ function RecordPreview({table, selectedRecordId, selectedFieldId}) { ); } else { - // Using getCellValueAsString guarantees we get a string back. If - // we use getCellValue, we might get back numbers, booleans, or - // arrays depending on the field type. - const previewUrl = getPreviewUrlForCellValue( - selectedRecord.getCellValueAsString(selectedField), - ); - - // In this case, the FIELD_NAME field of the currently selected - // record either contains no URL, or contains a URL that cannot be - // resolved to a supported preview. - if (!previewUrl) { - content = ( - - No preview - {viewSupportedURLsButton} - - ); - } else { - content = ( - -