feat: Ability to record videos and capture photos directly in app by selecting + in Files view (#2095)
This commit is contained in:
@@ -302,6 +302,7 @@ const ContentListView = forwardRef<HTMLDivElement, Props>(
|
||||
isFilesSmartView={isFilesSmartView}
|
||||
optionsSubtitle={optionsSubtitle}
|
||||
selectedTag={selectedTag}
|
||||
filesController={filesController}
|
||||
/>
|
||||
)}
|
||||
<SearchBar itemListController={itemListController} searchOptionsController={searchOptionsController} />
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
import PhotoCaptureModal from '@/Components/CameraCaptureModal/PhotoCaptureModal'
|
||||
import VideoCaptureModal from '@/Components/CameraCaptureModal/VideoCaptureModal'
|
||||
import Icon from '@/Components/Icon/Icon'
|
||||
import Menu from '@/Components/Menu/Menu'
|
||||
import MenuItem from '@/Components/Menu/MenuItem'
|
||||
import Popover from '@/Components/Popover/Popover'
|
||||
import { FilesController } from '@/Controllers/FilesController'
|
||||
import { PhotoRecorder } from '@/Controllers/Moments/PhotoRecorder'
|
||||
import { classNames } from '@standardnotes/snjs'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
|
||||
type Props = {
|
||||
isDailyEntry: boolean
|
||||
isInFilesSmartView: boolean
|
||||
addButtonLabel: string
|
||||
addNewItem: () => void
|
||||
filesController: FilesController
|
||||
}
|
||||
|
||||
const AddItemMenuButton = ({
|
||||
filesController,
|
||||
isDailyEntry,
|
||||
addButtonLabel,
|
||||
isInFilesSmartView,
|
||||
addNewItem,
|
||||
}: Props) => {
|
||||
const addItemButtonRef = useRef<HTMLButtonElement>(null)
|
||||
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false)
|
||||
const [deviceHasCamera, setDeviceHasCamera] = useState(false)
|
||||
const [captureType, setCaptureType] = useState<'photo' | 'video'>()
|
||||
|
||||
useEffect(() => {
|
||||
const setCameraSupport = async () => {
|
||||
setDeviceHasCamera(await PhotoRecorder.isSupported())
|
||||
}
|
||||
|
||||
void setCameraSupport()
|
||||
}, [])
|
||||
|
||||
const canShowMenu = isInFilesSmartView && deviceHasCamera
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
className={classNames(
|
||||
'hidden md:flex',
|
||||
'h-8 w-8 hover:brightness-125',
|
||||
'z-editor-title-bar ml-3 cursor-pointer items-center',
|
||||
`justify-center rounded-full border border-solid border-transparent ${
|
||||
isDailyEntry ? 'bg-danger text-danger-contrast' : 'bg-info text-info-contrast'
|
||||
}`,
|
||||
)}
|
||||
title={addButtonLabel}
|
||||
aria-label={addButtonLabel}
|
||||
onClick={() => {
|
||||
if (canShowMenu) {
|
||||
setIsMenuOpen((isOpen) => !isOpen)
|
||||
} else {
|
||||
addNewItem()
|
||||
}
|
||||
}}
|
||||
ref={addItemButtonRef}
|
||||
>
|
||||
<Icon type="add" size="custom" className="h-5 w-5" />
|
||||
</button>
|
||||
<Popover
|
||||
open={canShowMenu && isMenuOpen}
|
||||
anchorElement={addItemButtonRef.current}
|
||||
togglePopover={() => {
|
||||
setIsMenuOpen((isOpen) => !isOpen)
|
||||
}}
|
||||
side="bottom"
|
||||
align="center"
|
||||
className="py-2"
|
||||
>
|
||||
<Menu a11yLabel={'test'} isOpen={isMenuOpen}>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
addNewItem()
|
||||
setIsMenuOpen(false)
|
||||
}}
|
||||
>
|
||||
<Icon type="add" className="mr-2" />
|
||||
{addButtonLabel}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={async () => {
|
||||
setCaptureType('photo')
|
||||
setIsMenuOpen(false)
|
||||
}}
|
||||
>
|
||||
<Icon type="camera" className="mr-2" />
|
||||
Take photo
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={async () => {
|
||||
setCaptureType('video')
|
||||
setIsMenuOpen(false)
|
||||
}}
|
||||
>
|
||||
<Icon type="camera" className="mr-2" />
|
||||
Record video
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</Popover>
|
||||
{captureType === 'photo' && (
|
||||
<PhotoCaptureModal
|
||||
filesController={filesController}
|
||||
close={() => {
|
||||
setCaptureType(undefined)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{captureType === 'video' && (
|
||||
<VideoCaptureModal
|
||||
filesController={filesController}
|
||||
close={() => {
|
||||
setCaptureType(undefined)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default AddItemMenuButton
|
||||
@@ -9,6 +9,8 @@ import { isTag, VectorIconNameOrEmoji } from '@standardnotes/snjs'
|
||||
import RoundIconButton from '@/Components/Button/RoundIconButton'
|
||||
import { AnyTag } from '@/Controllers/Navigation/AnyTagType'
|
||||
import { MediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
|
||||
import AddItemMenuButton from './AddItemMenuButton'
|
||||
import { FilesController } from '@/Controllers/FilesController'
|
||||
|
||||
type Props = {
|
||||
application: WebApplication
|
||||
@@ -19,6 +21,7 @@ type Props = {
|
||||
isFilesSmartView: boolean
|
||||
optionsSubtitle?: string
|
||||
selectedTag: AnyTag
|
||||
filesController: FilesController
|
||||
}
|
||||
|
||||
const ContentListHeader = ({
|
||||
@@ -30,6 +33,7 @@ const ContentListHeader = ({
|
||||
isFilesSmartView,
|
||||
optionsSubtitle,
|
||||
selectedTag,
|
||||
filesController,
|
||||
}: Props) => {
|
||||
const displayOptionsContainerRef = useRef<HTMLDivElement>(null)
|
||||
const displayOptionsButtonRef = useRef<HTMLButtonElement>(null)
|
||||
@@ -84,23 +88,15 @@ const ContentListHeader = ({
|
||||
|
||||
const AddButton = useMemo(() => {
|
||||
return (
|
||||
<button
|
||||
className={classNames(
|
||||
'hidden md:flex',
|
||||
'h-8 w-8 hover:brightness-125',
|
||||
'z-editor-title-bar ml-3 cursor-pointer items-center',
|
||||
`justify-center rounded-full border border-solid border-transparent ${
|
||||
isDailyEntry ? 'bg-danger text-danger-contrast' : 'bg-info text-info-contrast'
|
||||
}`,
|
||||
)}
|
||||
title={addButtonLabel}
|
||||
aria-label={addButtonLabel}
|
||||
onClick={addNewItem}
|
||||
>
|
||||
<Icon type="add" size="custom" className="h-5 w-5" />
|
||||
</button>
|
||||
<AddItemMenuButton
|
||||
isInFilesSmartView={isFilesSmartView}
|
||||
isDailyEntry={isDailyEntry}
|
||||
addButtonLabel={addButtonLabel}
|
||||
addNewItem={addNewItem}
|
||||
filesController={filesController}
|
||||
/>
|
||||
)
|
||||
}, [addButtonLabel, addNewItem, isDailyEntry])
|
||||
}, [addButtonLabel, addNewItem, filesController, isDailyEntry, isFilesSmartView])
|
||||
|
||||
const FolderName = useMemo(() => {
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user