From 66e6b3c95604686ebf6e88e891d1add10f2ff157 Mon Sep 17 00:00:00 2001 From: DCloud_LXH <283700113@qq.com> Date: Tue, 26 Oct 2021 18:35:49 +0800 Subject: [PATCH] feat(H5): judge user activation when choose file --- .../src/helpers/useUserAction.ts | 23 +++++----- packages/uni-components/src/index.ts | 6 ++- packages/uni-core/src/i18n/en.json | 1 + packages/uni-core/src/i18n/es.json | 1 + packages/uni-core/src/i18n/fr.json | 1 + packages/uni-core/src/i18n/messages.ts | 45 +++++++++++++++++++ packages/uni-core/src/i18n/zh-Hans.json | 1 + packages/uni-core/src/i18n/zh-Hant.json | 1 + .../src/service/api/media/chooseFile.ts | 10 ++++- .../src/service/api/media/chooseImage.ts | 10 ++++- .../src/service/api/media/chooseVideo.ts | 10 ++++- .../src/service/api/media/createInput.ts | 2 + 12 files changed, 96 insertions(+), 15 deletions(-) diff --git a/packages/uni-components/src/helpers/useUserAction.ts b/packages/uni-components/src/helpers/useUserAction.ts index 889447cbd..879304529 100644 --- a/packages/uni-components/src/helpers/useUserAction.ts +++ b/packages/uni-components/src/helpers/useUserAction.ts @@ -10,7 +10,11 @@ const passiveOptions = passive(true) const states: UserActionState[] = [] let userInteract: number = 0 let inited: boolean -function addInteractListener(vm: UserActionState) { +const setUserAction = (userAction: boolean) => + states.forEach((vm) => (vm.userAction = userAction)) +export function addInteractListener( + vm: UserActionState = { userAction: false } +) { if (!inited) { const eventNames = [ 'touchstart', @@ -23,16 +27,12 @@ function addInteractListener(vm: UserActionState) { document.addEventListener( eventName, function () { - states.forEach((vm) => { - vm.userAction = true - userInteract++ - setTimeout(() => { - userInteract-- - if (!userInteract) { - vm.userAction = false - } - }, 0) - }) + !userInteract && setUserAction(true) + userInteract++ + + setTimeout(() => { + !--userInteract && setUserAction(false) + }, 0) }, passiveOptions ) @@ -47,6 +47,7 @@ function removeInteractListener(vm: UserActionState) { states.splice(index, 1) } } +export const getInteractStatus = () => !!userInteract export function useUserAction() { const state: UserActionState = reactive({ diff --git a/packages/uni-components/src/index.ts b/packages/uni-components/src/index.ts index acf0afd7d..066ee7e9a 100644 --- a/packages/uni-components/src/index.ts +++ b/packages/uni-components/src/index.ts @@ -13,7 +13,11 @@ export type { } from './helpers/useEvent' export * from './helpers/scroller' export { parseText } from './helpers/text' -export { useUserAction } from './helpers/useUserAction' +export { + useUserAction, + addInteractListener, + getInteractStatus, +} from './helpers/useUserAction' export { useAttrs } from './helpers/useAttrs' export { useBooleanAttr } from './helpers/useBooleanAttr' export { useTouchtrack } from './helpers/useTouchtrack' diff --git a/packages/uni-core/src/i18n/en.json b/packages/uni-core/src/i18n/en.json index c5f053887..2a9dee83e 100644 --- a/packages/uni-core/src/i18n/en.json +++ b/packages/uni-core/src/i18n/en.json @@ -12,6 +12,7 @@ "uni.chooseVideo.cancel": "Cancel", "uni.chooseVideo.sourceType.album": "Album", "uni.chooseVideo.sourceType.camera": "Camera", + "uni.chooseFile.notUserActivation": "File chooser dialog can only be shown with a user activation", "uni.previewImage.cancel": "Cancel", "uni.previewImage.button.save": "Save Image", "uni.previewImage.save.success": "Saved successfully", diff --git a/packages/uni-core/src/i18n/es.json b/packages/uni-core/src/i18n/es.json index 0e29efddd..e945b0048 100644 --- a/packages/uni-core/src/i18n/es.json +++ b/packages/uni-core/src/i18n/es.json @@ -12,6 +12,7 @@ "uni.chooseVideo.cancel": "Cancelar", "uni.chooseVideo.sourceType.album": "Álbum", "uni.chooseVideo.sourceType.camera": "Cámara", + "uni.chooseFile.notUserActivation": "El cuadro de diálogo del selector de archivos solo se puede mostrar con la activación del usuario", "uni.previewImage.cancel": "Cancelar", "uni.previewImage.button.save": "Guardar imagen", "uni.previewImage.save.success": "Guardado exitosamente", diff --git a/packages/uni-core/src/i18n/fr.json b/packages/uni-core/src/i18n/fr.json index 3e8a5fc30..3d38c4eb9 100644 --- a/packages/uni-core/src/i18n/fr.json +++ b/packages/uni-core/src/i18n/fr.json @@ -12,6 +12,7 @@ "uni.chooseVideo.cancel": "Annuler", "uni.chooseVideo.sourceType.album": "Album", "uni.chooseVideo.sourceType.camera": "Caméra", + "uni.chooseFile.notUserActivation": "La boîte de dialogue du sélecteur de fichier ne peut être affichée qu'avec une activation par l'utilisateur", "uni.previewImage.cancel": "Annuler", "uni.previewImage.button.save": "Guardar imagen", "uni.previewImage.save.success": "Enregistré avec succès", diff --git a/packages/uni-core/src/i18n/messages.ts b/packages/uni-core/src/i18n/messages.ts index 385c3237b..b8e2f72d9 100644 --- a/packages/uni-core/src/i18n/messages.ts +++ b/packages/uni-core/src/i18n/messages.ts @@ -344,6 +344,51 @@ export const initI18nChooseVideoMsgsOnce = /*#__PURE__*/ once(() => { ) } }) +export const initI18nChooseFileMsgsOnce = /*#__PURE__*/ once(() => { + const name = 'uni.chooseFile.' + const keys = ['notUserActivation'] + if (__UNI_FEATURE_I18N_EN__) { + useI18n().add( + LOCALE_EN, + normalizeMessages(name, keys, [ + 'File chooser dialog can only be shown with a user activation', + ]), + false + ) + } + if (__UNI_FEATURE_I18N_ES__) { + useI18n().add( + LOCALE_ES, + normalizeMessages(name, keys, [ + 'El cuadro de diálogo del selector de archivos solo se puede mostrar con la activación del usuario', + ]), + false + ) + } + if (__UNI_FEATURE_I18N_FR__) { + useI18n().add( + LOCALE_FR, + normalizeMessages(name, keys, [ + "La boîte de dialogue du sélecteur de fichier ne peut être affichée qu'avec une activation par l'utilisateur", + ]), + false + ) + } + if (__UNI_FEATURE_I18N_ZH_HANS__) { + useI18n().add( + LOCALE_ZH_HANS, + normalizeMessages(name, keys, ['文件选择器对话框只能在用户激活时显示']), + false + ) + } + if (__UNI_FEATURE_I18N_ZH_HANT__) { + useI18n().add( + LOCALE_ZH_HANT, + normalizeMessages(name, keys, ['文件選擇器對話框只能在用戶激活時顯示']), + false + ) + } +}) export const initI18nPreviewImageMsgsOnce = /*#__PURE__*/ once(() => { const name = 'uni.previewImage.' const keys = ['cancel', 'button.save', 'save.success', 'save.fail'] diff --git a/packages/uni-core/src/i18n/zh-Hans.json b/packages/uni-core/src/i18n/zh-Hans.json index f6ade87d8..e6a99270c 100644 --- a/packages/uni-core/src/i18n/zh-Hans.json +++ b/packages/uni-core/src/i18n/zh-Hans.json @@ -12,6 +12,7 @@ "uni.chooseVideo.cancel": "取消", "uni.chooseVideo.sourceType.album": "从相册选择", "uni.chooseVideo.sourceType.camera": "拍摄", + "uni.chooseFile.notUserActivation": "文件选择器对话框只能在用户激活时显示", "uni.previewImage.cancel": "取消", "uni.previewImage.button.save": "保存图像", "uni.previewImage.save.success": "保存图像到相册成功", diff --git a/packages/uni-core/src/i18n/zh-Hant.json b/packages/uni-core/src/i18n/zh-Hant.json index 857144362..50ce91f2f 100644 --- a/packages/uni-core/src/i18n/zh-Hant.json +++ b/packages/uni-core/src/i18n/zh-Hant.json @@ -12,6 +12,7 @@ "uni.chooseVideo.cancel": "取消", "uni.chooseVideo.sourceType.album": "從相冊選擇", "uni.chooseVideo.sourceType.camera": "拍攝", + "uni.chooseFile.notUserActivation": "文件選擇器對話框只能在用戶激活時顯示", "uni.previewImage.cancel": "取消", "uni.previewImage.button.save": "保存圖像", "uni.previewImage.save.success": "保存圖像到相冊成功", diff --git a/packages/uni-h5/src/service/api/media/chooseFile.ts b/packages/uni-h5/src/service/api/media/chooseFile.ts index 50faa8ae6..6c7286986 100644 --- a/packages/uni-h5/src/service/api/media/chooseFile.ts +++ b/packages/uni-h5/src/service/api/media/chooseFile.ts @@ -7,6 +7,8 @@ import { } from '@dcloudio/uni-api' import { fileToUrl } from '../../../helpers/file' import _createInput from './createInput' +import { getInteractStatus } from '@dcloudio/uni-components' +import { useI18n, initI18nChooseFileMsgsOnce } from '@dcloudio/uni-core' //#endregion //#region types @@ -29,6 +31,8 @@ export const chooseFile = defineAsyncApi( }, { resolve, reject } ) => { + initI18nChooseFileMsgsOnce() + const { t } = useI18n() // TODO handle sizeType 尝试通过 canvas 压缩 if (fileInput) { document.body.removeChild(fileInput) @@ -75,7 +79,11 @@ export const chooseFile = defineAsyncApi( // TODO 用户取消选择时,触发 fail,目前尚未找到合适的方法。 }) - fileInput.click() + if (getInteractStatus()) { + fileInput.click() + } else { + reject(t('uni.chooseFile.notUserActivation')) + } }, ChooseFileProtocol, ChooseFileOptions diff --git a/packages/uni-h5/src/service/api/media/chooseImage.ts b/packages/uni-h5/src/service/api/media/chooseImage.ts index b08cfe05d..7c54585fe 100644 --- a/packages/uni-h5/src/service/api/media/chooseImage.ts +++ b/packages/uni-h5/src/service/api/media/chooseImage.ts @@ -7,6 +7,8 @@ import { } from '@dcloudio/uni-api' import { fileToUrl } from '../../../helpers/file' import _createInput from './createInput' +import { getInteractStatus } from '@dcloudio/uni-components' +import { useI18n, initI18nChooseFileMsgsOnce } from '@dcloudio/uni-core' //#endregion //#region types @@ -29,6 +31,8 @@ export const chooseImage = defineAsyncApi( { resolve, reject } ) => { // TODO handle sizeType 尝试通过 canvas 压缩 + initI18nChooseFileMsgsOnce() + const { t } = useI18n() if (imageInput) { document.body.removeChild(imageInput) @@ -75,7 +79,11 @@ export const chooseImage = defineAsyncApi( // TODO 用户取消选择时,触发 fail,目前尚未找到合适的方法。 }) - imageInput.click() + if (getInteractStatus()) { + imageInput.click() + } else { + reject(t('uni.chooseFile.notUserActivation')) + } }, ChooseImageProtocol, ChooseImageOptions diff --git a/packages/uni-h5/src/service/api/media/chooseVideo.ts b/packages/uni-h5/src/service/api/media/chooseVideo.ts index 34f8aaed5..ecc8bc5d5 100644 --- a/packages/uni-h5/src/service/api/media/chooseVideo.ts +++ b/packages/uni-h5/src/service/api/media/chooseVideo.ts @@ -8,6 +8,8 @@ import { } from '@dcloudio/uni-api' import { fileToUrl, revokeObjectURL } from '../../../helpers/file' import _createInput from './createInput' +import { getInteractStatus } from '@dcloudio/uni-components' +import { useI18n, initI18nChooseFileMsgsOnce } from '@dcloudio/uni-core' //#endregion //#region types @@ -20,6 +22,8 @@ let videoInput: HTMLInputElement = null as any export const chooseVideo = defineAsyncApi( API_CHOOSE_VIDEO, ({ sourceType, extension }, { resolve, reject }) => { + initI18nChooseFileMsgsOnce() + const { t } = useI18n() if (videoInput) { document.body.removeChild(videoInput) videoInput = null as any @@ -80,7 +84,11 @@ export const chooseVideo = defineAsyncApi( // TODO 用户取消选择时,触发 fail,目前尚未找到合适的方法。 }) - videoInput.click() + if (getInteractStatus()) { + videoInput.click() + } else { + reject(t('uni.chooseFile.notUserActivation')) + } }, ChooseVideoProtocol, ChooseVideoOptions diff --git a/packages/uni-h5/src/service/api/media/createInput.ts b/packages/uni-h5/src/service/api/media/createInput.ts index 932af17e1..6060e3fed 100644 --- a/packages/uni-h5/src/service/api/media/createInput.ts +++ b/packages/uni-h5/src/service/api/media/createInput.ts @@ -1,5 +1,6 @@ import { updateElementStyle } from '@dcloudio/uni-shared' import MIMEType from './MIMEType' +import { addInteractListener } from '@dcloudio/uni-components' export type createInputOptions = Pick< UniApp.ChooseFileOptions, @@ -7,6 +8,7 @@ export type createInputOptions = Pick< > const ALL = 'all' +addInteractListener() function isWXEnv(): boolean { const ua = window.navigator.userAgent.toLowerCase() -- GitLab