Browse Source

Nc fix(nc-gui): allow copy paste email id from gmail in team & settings, for example, abc <abc@gmail.com> (#7963)

* fix(nc-gui): allow copy paste email id from gmail

* fix(nc-gui): skip display error & add only valid emails if user copy paste bulk emails in team & settings invite input element

* fix(nc-gui): add a comment explaining the regex pattern for email validate/extract

* fix(nc-gui): allow copy paste emailid from gmail in team & settings oss

* fix(nc-gui): add support to extract email from pasted string in email cell if accept only email is true
pull/7976/head
Ramesh Mane 8 months ago committed by GitHub
parent
commit
1b8c47e2b0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 8
      packages/nc-gui/components/account/UsersModal.vue
  2. 12
      packages/nc-gui/components/cell/Email.vue
  3. 14
      packages/nc-gui/components/project/ShareBaseDlg.vue
  4. 13
      packages/nc-gui/components/workspace/InviteSection.vue
  5. 7
      packages/nc-gui/composables/useMultiSelect/convertCellData.ts
  6. 10
      packages/nc-gui/helpers/parsers/parserHelpers.ts

8
packages/nc-gui/components/account/UsersModal.vue

@ -16,6 +16,7 @@ import {
useI18n,
useNuxtApp,
} from '#imports'
import { extractEmail } from '~/helpers/parsers/parserHelpers'
interface Props {
show: boolean
@ -99,6 +100,12 @@ const clickInviteMore = () => {
}
const emailInput: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
const onPaste = (e: ClipboardEvent) => {
const pastedText = e.clipboardData?.getData('text') ?? ''
usersData.value.emails = extractEmail(pastedText) || pastedText
}
</script>
<template>
@ -189,6 +196,7 @@ const emailInput: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
size="middle"
validate-trigger="onBlur"
:placeholder="$t('labels.email')"
@paste.prevent="onPaste"
/>
</a-form-item>
</div>

12
packages/nc-gui/components/cell/Email.vue

@ -11,6 +11,7 @@ import {
useI18n,
validateEmail,
} from '#imports'
import { extractEmail } from '~/helpers/parsers/parserHelpers'
interface Props {
modelValue: string | null | undefined
@ -56,6 +57,16 @@ const validEmail = computed(() => vModel.value && validateEmail(vModel.value))
const focus: VNodeRef = (el) =>
!isExpandedFormOpen.value && !isEditColumn.value && !isForm.value && (el as HTMLInputElement)?.focus()
const onPaste = (e: ClipboardEvent) => {
const pastedText = e.clipboardData?.getData('text') ?? ''
if (parseProp(column.value.meta).validate) {
vModel.value = extractEmail(pastedText) || pastedText
} else {
vModel.value = pastedText
}
}
watch(
() => editEnabled.value,
() => {
@ -90,6 +101,7 @@ watch(
@keydown.delete.stop
@selectstart.capture.stop
@mousedown.stop
@paste.prevent="onPaste"
/>
<span v-else-if="vModel === null && showNull" class="nc-cell-field nc-null uppercase">{{ $t('general.null') }}</span>

14
packages/nc-gui/components/project/ShareBaseDlg.vue

@ -2,6 +2,8 @@
import type { RoleLabels } from 'nocodb-sdk'
import { OrderedProjectRoles, ProjectRoles } from 'nocodb-sdk'
import type { User } from '#imports'
import { extractEmail } from '~/helpers/parsers/parserHelpers'
const props = defineProps<{
modelValue: boolean
baseId?: string
@ -64,13 +66,17 @@ const insertOrUpdateString = (str: string) => {
emailBadges.value.push(str)
}
const emailInputValidation = (input: string): boolean => {
const emailInputValidation = (input: string, isBulkEmailCopyPaste: boolean = false): boolean => {
if (!input.length) {
if (isBulkEmailCopyPaste) return false
emailValidation.isError = true
emailValidation.message = 'Email should not be empty'
return false
}
if (!validateEmail(input.trim())) {
if (isBulkEmailCopyPaste) return false
emailValidation.isError = true
emailValidation.message = 'Invalid Email'
return false
@ -157,6 +163,8 @@ watch(dialogShow, (newVal) => {
// when bulk email is pasted
const onPaste = (e: ClipboardEvent) => {
emailValidation.isError = false
const pastedText = e.clipboardData?.getData('text')
const inputArray = pastedText?.split(',') || pastedText?.split(' ')
@ -168,7 +176,9 @@ const onPaste = (e: ClipboardEvent) => {
}
inputArray?.forEach((el) => {
const isEmailIsValid = emailInputValidation(el)
el = extractEmail(el) || el
const isEmailIsValid = emailInputValidation(el, inputArray.length > 1)
if (!isEmailIsValid) return

13
packages/nc-gui/components/workspace/InviteSection.vue

@ -4,6 +4,7 @@ import type { RoleLabels } from 'nocodb-sdk'
import { OrderedWorkspaceRoles, WorkspaceUserRoles } from 'nocodb-sdk'
import { extractSdkResponseErrorMsg, useWorkspace } from '#imports'
import { validateEmail } from '~/utils/validation'
import { extractEmail } from '~/helpers/parsers/parserHelpers'
const inviteData = reactive({
email: '',
@ -42,13 +43,17 @@ const insertOrUpdateString = (str: string) => {
emailBadges.value.push(str)
}
const emailInputValidation = (input: string): boolean => {
const emailInputValidation = (input: string, isBulkEmailCopyPaste: boolean = false): boolean => {
if (!input.length) {
if (isBulkEmailCopyPaste) return false
emailValidation.isError = true
emailValidation.message = 'Email should not be empty'
return false
}
if (!validateEmail(input.trim())) {
if (isBulkEmailCopyPaste) return false
emailValidation.isError = true
emailValidation.message = 'Invalid Email'
return false
@ -164,6 +169,8 @@ onKeyStroke('Backspace', () => {
// when bulk email is pasted
const onPaste = (e: ClipboardEvent) => {
emailValidation.isError = false
const pastedText = e.clipboardData?.getData('text')
const inputArray = pastedText?.split(',') || pastedText?.split(' ')
@ -175,7 +182,9 @@ const onPaste = (e: ClipboardEvent) => {
}
inputArray?.forEach((el) => {
const isEmailIsValid = emailInputValidation(el)
el = extractEmail(el) || el
const isEmailIsValid = emailInputValidation(el, inputArray.length > 1)
if (!isEmailIsValid) return

7
packages/nc-gui/composables/useMultiSelect/convertCellData.ts

@ -3,6 +3,7 @@ import type { AttachmentType, ColumnType, LinkToAnotherRecordType, SelectOptions
import { UITypes, getDateFormat, getDateTimeFormat, populateUniqueFileName } from 'nocodb-sdk'
import type { AppInfo } from '~/composables/useGlobal'
import { isBt, isMm, isOo, parseProp } from '#imports'
import { extractEmail } from '~/helpers/parsers/parserHelpers'
export default function convertCellData(
args: { to: UITypes; value: string; column: ColumnType; appInfo: AppInfo; files?: FileList | File[]; oldValue?: unknown },
@ -291,6 +292,12 @@ export default function convertCellData(
throw new Error(`Unsupported conversion for ${to}`)
}
}
case UITypes.Email: {
if (parseProp(column.meta).validate) {
return extractEmail(value) || value
}
return value
}
case UITypes.Lookup:
case UITypes.Rollup:
case UITypes.Formula:

10
packages/nc-gui/helpers/parsers/parserHelpers.ts

@ -1,8 +1,18 @@
import { UITypes } from 'nocodb-sdk'
import isURL from 'validator/lib/isURL'
// This regex pattern matches email addresses by looking for sequences that start with characters before the "@" symbol, followed by the domain.
// It's designed to capture most email formats, including those with periods and "+" symbols in the local part.
const validateEmail = (v: string) =>
/^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i.test(v)
export const extractEmail = (v: string) => {
const matches = v.match(
/(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})/i,
)
return matches ? matches[0] : null
}
const booleanOptions = [
{ checked: true, unchecked: false },
{ 'x': true, '': false },

Loading…
Cancel
Save