Ensure once one type of media is selected, you can only select more of that type
(cherry picked from commit 1a9e6e0cdb5234667f08e3dd9107ae598941fc23)
This commit is contained in:
committed by
Samuel Newman
parent
9b8fd2eb48
commit
d498eeee46
@@ -133,6 +133,7 @@ import * as toast from '#/components/Toast'
|
|||||||
import {Text as NewText} from '#/components/Typography'
|
import {Text as NewText} from '#/components/Typography'
|
||||||
import {BottomSheetPortalProvider} from '../../../../modules/bottom-sheet'
|
import {BottomSheetPortalProvider} from '../../../../modules/bottom-sheet'
|
||||||
import {
|
import {
|
||||||
|
type AssetType,
|
||||||
SelectMediaButton,
|
SelectMediaButton,
|
||||||
type SelectMediaButtonProps,
|
type SelectMediaButtonProps,
|
||||||
} from './SelectMediaButton'
|
} from './SelectMediaButton'
|
||||||
@@ -1267,6 +1268,13 @@ function ComposerFooter({
|
|||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const {isMobile} = useWebMediaQueries()
|
const {isMobile} = useWebMediaQueries()
|
||||||
|
/*
|
||||||
|
* Once we've allowed a certain type of asset to be selected, we don't allow
|
||||||
|
* other types of media to be selected.
|
||||||
|
*/
|
||||||
|
const [selectedAssetsType, setSelectedAssetsType] = useState<
|
||||||
|
AssetType | undefined
|
||||||
|
>(undefined)
|
||||||
|
|
||||||
const media = post.embed.media
|
const media = post.embed.media
|
||||||
const images = media?.type === 'images' ? media.images : []
|
const images = media?.type === 'images' ? media.images : []
|
||||||
@@ -1304,8 +1312,17 @@ function ComposerFooter({
|
|||||||
[dispatch],
|
[dispatch],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset if the user clears any selected media
|
||||||
|
*/
|
||||||
|
if (selectedAssetsType !== undefined && !media) {
|
||||||
|
setSelectedAssetsType(undefined)
|
||||||
|
}
|
||||||
|
|
||||||
const onSelectAssets = useCallback<SelectMediaButtonProps['onSelectAssets']>(
|
const onSelectAssets = useCallback<SelectMediaButtonProps['onSelectAssets']>(
|
||||||
async ({type, assets, errors}) => {
|
async ({type, assets, errors}) => {
|
||||||
|
setSelectedAssetsType(type)
|
||||||
|
|
||||||
if (assets.length) {
|
if (assets.length) {
|
||||||
if (type === 'image') {
|
if (type === 'image') {
|
||||||
const images: ComposerImage[] = []
|
const images: ComposerImage[] = []
|
||||||
@@ -1363,6 +1380,7 @@ function ComposerFooter({
|
|||||||
<ToolbarWrapper style={[a.flex_row, a.align_center, a.gap_xs]}>
|
<ToolbarWrapper style={[a.flex_row, a.align_center, a.gap_xs]}>
|
||||||
<SelectMediaButton
|
<SelectMediaButton
|
||||||
disabled={isMediaSelectionDisabled}
|
disabled={isMediaSelectionDisabled}
|
||||||
|
allowedAssetTypes={selectedAssetsType}
|
||||||
selectedAssetsCount={selectedAssetsCount}
|
selectedAssetsCount={selectedAssetsCount}
|
||||||
onSelectAssets={onSelectAssets}
|
onSelectAssets={onSelectAssets}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ import * as toast from '#/components/Toast'
|
|||||||
|
|
||||||
export type SelectMediaButtonProps = {
|
export type SelectMediaButtonProps = {
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
|
/**
|
||||||
|
* If set, this limits the types of assets that can be selected.
|
||||||
|
*/
|
||||||
|
allowedAssetTypes: AssetType | undefined
|
||||||
selectedAssetsCount: number
|
selectedAssetsCount: number
|
||||||
onSelectAssets: (props: {
|
onSelectAssets: (props: {
|
||||||
type: AssetType
|
type: AssetType
|
||||||
@@ -37,7 +41,7 @@ export type SelectMediaButtonProps = {
|
|||||||
/**
|
/**
|
||||||
* Generic asset classes, or buckets, that we support.
|
* Generic asset classes, or buckets, that we support.
|
||||||
*/
|
*/
|
||||||
type AssetType = 'video' | 'image' | 'gif'
|
export type AssetType = 'video' | 'image' | 'gif'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shadows `ImagePickerAsset` from `expo-image-picker`, but with a guaranteed `mimeType`
|
* Shadows `ImagePickerAsset` from `expo-image-picker`, but with a guaranteed `mimeType`
|
||||||
@@ -231,9 +235,11 @@ async function getAdditionalVideoMetadata(asset: ValidatedImagePickerAsset) {
|
|||||||
async function processImagePickerAssets(
|
async function processImagePickerAssets(
|
||||||
assets: ImagePickerAsset[],
|
assets: ImagePickerAsset[],
|
||||||
{
|
{
|
||||||
selectionLimit,
|
selectionCountRemaining,
|
||||||
|
allowedAssetTypes,
|
||||||
}: {
|
}: {
|
||||||
selectionLimit: number
|
selectionCountRemaining: number
|
||||||
|
allowedAssetTypes: AssetType | undefined
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
/*
|
/*
|
||||||
@@ -242,10 +248,11 @@ async function processImagePickerAssets(
|
|||||||
const errors = new Set<SelectedAssetError>()
|
const errors = new Set<SelectedAssetError>()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We only support selecting a single type of media at a time, so this
|
* We only support selecting a single type of media at a time, so this gets
|
||||||
* gets set to whatever the first asset type is.
|
* set to whatever the first valid asset type is, OR to whatever
|
||||||
|
* `allowedAssetTypes` is set to.
|
||||||
*/
|
*/
|
||||||
let primaryMediaType: AssetType | undefined
|
let selectableAssetType: AssetType | undefined
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This will hold the assets that we can actually use, after filtering
|
* This will hold the assets that we can actually use, after filtering
|
||||||
@@ -260,11 +267,15 @@ async function processImagePickerAssets(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the primary media type to the first valid asset type
|
/*
|
||||||
primaryMediaType = primaryMediaType || type
|
* If we have an `allowedAssetTypes` prop, constrain to that. Otherwise,
|
||||||
|
* set this to the first valid asset type we see, and then use that to
|
||||||
|
* constrain all remaining selected assets.
|
||||||
|
*/
|
||||||
|
selectableAssetType = allowedAssetTypes || selectableAssetType || type
|
||||||
|
|
||||||
// ignore mixed types
|
// ignore mixed types
|
||||||
if (type !== primaryMediaType) {
|
if (type !== selectableAssetType) {
|
||||||
errors.add(SelectedAssetError.MixedTypes)
|
errors.add(SelectedAssetError.MixedTypes)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -303,12 +314,12 @@ async function processImagePickerAssets(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (primaryMediaType === 'image') {
|
if (selectableAssetType === 'image') {
|
||||||
if (supportedAssets.length > selectionLimit) {
|
if (supportedAssets.length > selectionCountRemaining) {
|
||||||
errors.add(SelectedAssetError.MaxImages)
|
errors.add(SelectedAssetError.MaxImages)
|
||||||
supportedAssets = supportedAssets.slice(0, selectionLimit)
|
supportedAssets = supportedAssets.slice(0, selectionCountRemaining)
|
||||||
}
|
}
|
||||||
} else if (primaryMediaType === 'video') {
|
} else if (selectableAssetType === 'video') {
|
||||||
if (supportedAssets.length > 1) {
|
if (supportedAssets.length > 1) {
|
||||||
errors.add(SelectedAssetError.MaxVideos)
|
errors.add(SelectedAssetError.MaxVideos)
|
||||||
supportedAssets = supportedAssets.slice(0, 1)
|
supportedAssets = supportedAssets.slice(0, 1)
|
||||||
@@ -346,7 +357,7 @@ async function processImagePickerAssets(
|
|||||||
errors.add(SelectedAssetError.VideoTooLong)
|
errors.add(SelectedAssetError.VideoTooLong)
|
||||||
supportedAssets = []
|
supportedAssets = []
|
||||||
}
|
}
|
||||||
} else if (primaryMediaType === 'gif') {
|
} else if (selectableAssetType === 'gif') {
|
||||||
if (supportedAssets.length > 1) {
|
if (supportedAssets.length > 1) {
|
||||||
errors.add(SelectedAssetError.MaxGIFs)
|
errors.add(SelectedAssetError.MaxGIFs)
|
||||||
supportedAssets = supportedAssets.slice(0, 1)
|
supportedAssets = supportedAssets.slice(0, 1)
|
||||||
@@ -354,7 +365,7 @@ async function processImagePickerAssets(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: primaryMediaType!, // set above
|
type: selectableAssetType!, // set above
|
||||||
assets: supportedAssets,
|
assets: supportedAssets,
|
||||||
errors,
|
errors,
|
||||||
}
|
}
|
||||||
@@ -362,6 +373,7 @@ async function processImagePickerAssets(
|
|||||||
|
|
||||||
export function SelectMediaButton({
|
export function SelectMediaButton({
|
||||||
disabled,
|
disabled,
|
||||||
|
allowedAssetTypes,
|
||||||
selectedAssetsCount,
|
selectedAssetsCount,
|
||||||
onSelectAssets,
|
onSelectAssets,
|
||||||
}: SelectMediaButtonProps) {
|
}: SelectMediaButtonProps) {
|
||||||
@@ -371,7 +383,7 @@ export function SelectMediaButton({
|
|||||||
const sheetWrapper = useSheetWrapper()
|
const sheetWrapper = useSheetWrapper()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
|
|
||||||
const selectionLimit = MAX_IMAGES - selectedAssetsCount
|
const selectionCountRemaining = MAX_IMAGES - selectedAssetsCount
|
||||||
|
|
||||||
const processSelectedAssets = useCallback(
|
const processSelectedAssets = useCallback(
|
||||||
async (rawAssets: ImagePickerAsset[]) => {
|
async (rawAssets: ImagePickerAsset[]) => {
|
||||||
@@ -379,7 +391,10 @@ export function SelectMediaButton({
|
|||||||
type,
|
type,
|
||||||
assets,
|
assets,
|
||||||
errors: errorCodes,
|
errors: errorCodes,
|
||||||
} = await processImagePickerAssets(rawAssets, {selectionLimit})
|
} = await processImagePickerAssets(rawAssets, {
|
||||||
|
selectionCountRemaining,
|
||||||
|
allowedAssetTypes,
|
||||||
|
})
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Convert error codes to user-friendly messages.
|
* Convert error codes to user-friendly messages.
|
||||||
@@ -420,7 +435,7 @@ export function SelectMediaButton({
|
|||||||
errors,
|
errors,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
[_, onSelectAssets, selectionLimit],
|
[_, onSelectAssets, selectionCountRemaining, allowedAssetTypes],
|
||||||
)
|
)
|
||||||
|
|
||||||
const onPressSelectMedia = useCallback(async () => {
|
const onPressSelectMedia = useCallback(async () => {
|
||||||
@@ -445,7 +460,7 @@ export function SelectMediaButton({
|
|||||||
quality: 1,
|
quality: 1,
|
||||||
allowsMultipleSelection: true,
|
allowsMultipleSelection: true,
|
||||||
legacy: true,
|
legacy: true,
|
||||||
selectionLimit: isIOS ? selectionLimit : undefined,
|
selectionLimit: isIOS ? selectionCountRemaining : undefined,
|
||||||
preferredAssetRepresentationMode:
|
preferredAssetRepresentationMode:
|
||||||
UIImagePickerPreferredAssetRepresentationMode.Current,
|
UIImagePickerPreferredAssetRepresentationMode.Current,
|
||||||
}),
|
}),
|
||||||
@@ -460,7 +475,7 @@ export function SelectMediaButton({
|
|||||||
requestVideoAccessIfNeeded,
|
requestVideoAccessIfNeeded,
|
||||||
sheetWrapper,
|
sheetWrapper,
|
||||||
processSelectedAssets,
|
processSelectedAssets,
|
||||||
selectionLimit,
|
selectionCountRemaining,
|
||||||
])
|
])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user