Compare commits
1 Commits
db-mongodb
...
db-postgre
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e3ef443217 |
@@ -347,7 +347,7 @@ The `useForm` hook returns an object with the following properties: |
|
|||||||
value: <strong><code>rowIndex</code></strong>,
|
value: <strong><code>rowIndex</code></strong>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: "The index of the row to add. If omitted, the row will be added to the end of the array.",
|
value: "The index of the row to add",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@payloadcms/db-mongodb",
|
"name": "@payloadcms/db-mongodb",
|
||||||
"version": "1.0.3",
|
"version": "1.0.2",
|
||||||
"description": "The officially supported MongoDB database adapter for Payload",
|
"description": "The officially supported MongoDB database adapter for Payload",
|
||||||
"repository": "https://github.com/payloadcms/payload",
|
"repository": "https://github.com/payloadcms/payload",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -68,9 +68,7 @@ export type MongooseAdapter = BaseDatabaseAdapter &
|
|||||||
type MongooseAdapterResult = (args: { payload: Payload }) => MongooseAdapter
|
type MongooseAdapterResult = (args: { payload: Payload }) => MongooseAdapter
|
||||||
|
|
||||||
declare module 'payload' {
|
declare module 'payload' {
|
||||||
export interface DatabaseAdapter
|
export interface DatabaseAdapter extends Args {
|
||||||
extends Omit<BaseDatabaseAdapter, 'sessions'>,
|
|
||||||
Omit<Args, 'migrationDir'> {
|
|
||||||
collections: {
|
collections: {
|
||||||
[slug: string]: CollectionModel
|
[slug: string]: CollectionModel
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@payloadcms/db-postgres",
|
"name": "@payloadcms/db-postgres",
|
||||||
"version": "0.1.3",
|
"version": "0.1.2",
|
||||||
"description": "The officially supported Postgres database adapter for Payload",
|
"description": "The officially supported Postgres database adapter for Payload",
|
||||||
"repository": "https://github.com/payloadcms/payload",
|
"repository": "https://github.com/payloadcms/payload",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -69,9 +69,7 @@ export type MigrateUpArgs = { payload: Payload }
|
|||||||
export type MigrateDownArgs = { payload: Payload }
|
export type MigrateDownArgs = { payload: Payload }
|
||||||
|
|
||||||
declare module 'payload' {
|
declare module 'payload' {
|
||||||
export interface DatabaseAdapter
|
export interface DatabaseAdapter extends Omit<Args, 'pool'> {
|
||||||
extends Omit<Args, 'migrationDir' | 'pool'>,
|
|
||||||
BaseDatabaseAdapter {
|
|
||||||
drizzle: DrizzleDB
|
drizzle: DrizzleDB
|
||||||
enums: Record<string, GenericEnum>
|
enums: Record<string, GenericEnum>
|
||||||
pool: Pool
|
pool: Pool
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export const ArrayAction: React.FC<Props> = ({
|
|||||||
<PopupList.Button
|
<PopupList.Button
|
||||||
className={`${baseClass}__action ${baseClass}__add`}
|
className={`${baseClass}__action ${baseClass}__add`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
addRow(index + 1)
|
addRow(index)
|
||||||
close()
|
close()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -132,9 +132,7 @@ export function fieldReducer(state: Fields, action: FieldAction): Fields {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'ADD_ROW': {
|
case 'ADD_ROW': {
|
||||||
const { blockType, path, rowIndex: rowIndexFromArgs, subFieldState } = action
|
const { blockType, path, rowIndex, subFieldState } = action
|
||||||
const rowIndex =
|
|
||||||
typeof rowIndexFromArgs === 'number' ? rowIndexFromArgs : state[path]?.rows?.length || 0
|
|
||||||
|
|
||||||
const rowsMetadata = [...(state[path]?.rows || [])]
|
const rowsMetadata = [...(state[path]?.rows || [])]
|
||||||
rowsMetadata.splice(
|
rowsMetadata.splice(
|
||||||
@@ -157,22 +155,19 @@ export function fieldReducer(state: Fields, action: FieldAction): Fields {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add new row to array _field state_
|
const { remainingFields, rows } = separateRows(path, state)
|
||||||
const { remainingFields, rows: siblingRows } = separateRows(path, state)
|
|
||||||
siblingRows.splice(rowIndex, 0, subFieldState)
|
|
||||||
|
|
||||||
// add new row to array _value_
|
// actual form state (value saved in db)
|
||||||
const currentValue = (Array.isArray(state[path]?.value) ? state[path]?.value : []) as Fields[]
|
rows.splice(rowIndex, 0, subFieldState)
|
||||||
const newValue = currentValue.splice(rowIndex, 0, reduceFieldsToValues(subFieldState, true))
|
|
||||||
|
|
||||||
const newState: Fields = {
|
const newState: Fields = {
|
||||||
...remainingFields,
|
...remainingFields,
|
||||||
...flattenRows(path, siblingRows),
|
...flattenRows(path, rows),
|
||||||
[path]: {
|
[path]: {
|
||||||
...state[path],
|
...state[path],
|
||||||
disableFormData: true,
|
disableFormData: true,
|
||||||
rows: rowsMetadata,
|
rows: rowsMetadata,
|
||||||
value: newValue,
|
value: rows,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,8 +176,8 @@ export function fieldReducer(state: Fields, action: FieldAction): Fields {
|
|||||||
|
|
||||||
case 'REPLACE_ROW': {
|
case 'REPLACE_ROW': {
|
||||||
const { blockType, path, rowIndex: rowIndexArg, subFieldState } = action
|
const { blockType, path, rowIndex: rowIndexArg, subFieldState } = action
|
||||||
const { remainingFields, rows: siblingRows } = separateRows(path, state)
|
const { remainingFields, rows } = separateRows(path, state)
|
||||||
const rowIndex = Math.max(0, Math.min(rowIndexArg, siblingRows?.length - 1 || 0))
|
const rowIndex = Math.max(0, Math.min(rowIndexArg, rows?.length - 1 || 0))
|
||||||
|
|
||||||
const rowsMetadata = [...(state[path]?.rows || [])]
|
const rowsMetadata = [...(state[path]?.rows || [])]
|
||||||
rowsMetadata[rowIndex] = {
|
rowsMetadata[rowIndex] = {
|
||||||
@@ -200,21 +195,17 @@ export function fieldReducer(state: Fields, action: FieldAction): Fields {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// replace form _field state_
|
// replace form field state
|
||||||
siblingRows[rowIndex] = subFieldState
|
rows[rowIndex] = subFieldState
|
||||||
|
|
||||||
// replace array _value_
|
|
||||||
const newValue = Array.isArray(state[path]?.value) ? state[path]?.value : []
|
|
||||||
newValue[rowIndex] = reduceFieldsToValues(subFieldState, true)
|
|
||||||
|
|
||||||
const newState: Fields = {
|
const newState: Fields = {
|
||||||
...remainingFields,
|
...remainingFields,
|
||||||
...flattenRows(path, siblingRows),
|
...flattenRows(path, rows),
|
||||||
[path]: {
|
[path]: {
|
||||||
...state[path],
|
...state[path],
|
||||||
disableFormData: true,
|
disableFormData: true,
|
||||||
rows: rowsMetadata,
|
rows: rowsMetadata,
|
||||||
value: newValue,
|
value: rows,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -431,7 +431,7 @@ const Form: React.FC<Props> = (props) => {
|
|||||||
[],
|
[],
|
||||||
)
|
)
|
||||||
|
|
||||||
const getRowSchemaByPath = React.useCallback(
|
const getRowConfigByPath = React.useCallback(
|
||||||
({ blockType, path }: { blockType?: string; path: string }) => {
|
({ blockType, path }: { blockType?: string; path: string }) => {
|
||||||
const rowConfig = traverseRowConfigs({
|
const rowConfig = traverseRowConfigs({
|
||||||
fieldConfig: collection?.fields || global?.fields,
|
fieldConfig: collection?.fields || global?.fields,
|
||||||
@@ -449,24 +449,23 @@ const Form: React.FC<Props> = (props) => {
|
|||||||
const addFieldRow: Context['addFieldRow'] = useCallback(
|
const addFieldRow: Context['addFieldRow'] = useCallback(
|
||||||
async ({ data, path, rowIndex }) => {
|
async ({ data, path, rowIndex }) => {
|
||||||
const preferences = await getDocPreferences()
|
const preferences = await getDocPreferences()
|
||||||
const rowSchema = getRowSchemaByPath({
|
const fieldConfig = getRowConfigByPath({
|
||||||
blockType: data?.blockType,
|
blockType: data?.blockType,
|
||||||
path,
|
path,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (rowSchema) {
|
if (fieldConfig) {
|
||||||
const subFieldState = await buildStateFromSchema({
|
const subFieldState = await buildStateFromSchema({
|
||||||
id,
|
id,
|
||||||
config,
|
config,
|
||||||
data,
|
data,
|
||||||
fieldSchema: rowSchema,
|
fieldSchema: fieldConfig,
|
||||||
locale,
|
locale,
|
||||||
operation,
|
operation,
|
||||||
preferences,
|
preferences,
|
||||||
t,
|
t,
|
||||||
user,
|
user,
|
||||||
})
|
})
|
||||||
|
|
||||||
dispatchFields({
|
dispatchFields({
|
||||||
blockType: data?.blockType,
|
blockType: data?.blockType,
|
||||||
path,
|
path,
|
||||||
@@ -476,11 +475,11 @@ const Form: React.FC<Props> = (props) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[dispatchFields, getDocPreferences, id, user, operation, locale, t, getRowSchemaByPath, config],
|
[dispatchFields, getDocPreferences, id, user, operation, locale, t, getRowConfigByPath, config],
|
||||||
)
|
)
|
||||||
|
|
||||||
const removeFieldRow: Context['removeFieldRow'] = useCallback(
|
const removeFieldRow: Context['removeFieldRow'] = useCallback(
|
||||||
({ path, rowIndex }) => {
|
async ({ path, rowIndex }) => {
|
||||||
dispatchFields({ path, rowIndex, type: 'REMOVE_ROW' })
|
dispatchFields({ path, rowIndex, type: 'REMOVE_ROW' })
|
||||||
},
|
},
|
||||||
[dispatchFields],
|
[dispatchFields],
|
||||||
@@ -489,17 +488,17 @@ const Form: React.FC<Props> = (props) => {
|
|||||||
const replaceFieldRow: Context['replaceFieldRow'] = useCallback(
|
const replaceFieldRow: Context['replaceFieldRow'] = useCallback(
|
||||||
async ({ data, path, rowIndex }) => {
|
async ({ data, path, rowIndex }) => {
|
||||||
const preferences = await getDocPreferences()
|
const preferences = await getDocPreferences()
|
||||||
const rowSchema = getRowSchemaByPath({
|
const fieldConfig = getRowConfigByPath({
|
||||||
blockType: data?.blockType,
|
blockType: data?.blockType,
|
||||||
path,
|
path,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (rowSchema) {
|
if (fieldConfig) {
|
||||||
const subFieldState = await buildStateFromSchema({
|
const subFieldState = await buildStateFromSchema({
|
||||||
id,
|
id,
|
||||||
config,
|
config,
|
||||||
data,
|
data,
|
||||||
fieldSchema: rowSchema,
|
fieldSchema: fieldConfig,
|
||||||
locale,
|
locale,
|
||||||
operation,
|
operation,
|
||||||
preferences,
|
preferences,
|
||||||
@@ -515,7 +514,7 @@ const Form: React.FC<Props> = (props) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[dispatchFields, getDocPreferences, id, user, operation, locale, t, getRowSchemaByPath, config],
|
[dispatchFields, getDocPreferences, id, user, operation, locale, t, getRowConfigByPath, config],
|
||||||
)
|
)
|
||||||
|
|
||||||
const getFields = useCallback(() => contextRef.current.fields, [contextRef])
|
const getFields = useCallback(() => contextRef.current.fields, [contextRef])
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ export const separateRows = (path: string, fields: Fields): Result => {
|
|||||||
const newRows = incomingRows
|
const newRows = incomingRows
|
||||||
|
|
||||||
if (fieldPath.indexOf(`${path}.`) === 0) {
|
if (fieldPath.indexOf(`${path}.`) === 0) {
|
||||||
const [rowIndex] = fieldPath.replace(`${path}.`, '').split('.')
|
const index = Number(fieldPath.replace(`${path}.`, '').split('.')[0])
|
||||||
if (!newRows[rowIndex]) newRows[rowIndex] = {}
|
if (!newRows[index]) newRows[index] = {}
|
||||||
newRows[rowIndex][fieldPath.replace(`${path}.${String(rowIndex)}.`, '')] = { ...field }
|
newRows[index][fieldPath.replace(`${path}.${String(index)}.`, '')] = { ...field }
|
||||||
} else {
|
} else {
|
||||||
remainingFields[fieldPath] = field
|
remainingFields[fieldPath] = field
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -174,10 +174,7 @@ export type Context = {
|
|||||||
}: {
|
}: {
|
||||||
data?: Data
|
data?: Data
|
||||||
path: string
|
path: string
|
||||||
/*
|
rowIndex: number
|
||||||
* by default the new row will be added to the end of the list
|
|
||||||
*/
|
|
||||||
rowIndex?: number
|
|
||||||
}) => Promise<void>
|
}) => Promise<void>
|
||||||
buildRowErrors: () => void
|
buildRowErrors: () => void
|
||||||
createFormData: CreateFormData
|
createFormData: CreateFormData
|
||||||
@@ -193,7 +190,7 @@ export type Context = {
|
|||||||
getField: GetField
|
getField: GetField
|
||||||
getFields: GetFields
|
getFields: GetFields
|
||||||
getSiblingData: GetSiblingData
|
getSiblingData: GetSiblingData
|
||||||
removeFieldRow: ({ path, rowIndex }: { path: string; rowIndex: number }) => void
|
removeFieldRow: ({ path, rowIndex }: { path: string; rowIndex: number }) => Promise<void>
|
||||||
replaceFieldRow: ({
|
replaceFieldRow: ({
|
||||||
data,
|
data,
|
||||||
path,
|
path,
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import type { Props } from './types'
|
|||||||
import { createNestedFieldPath } from '../../Form/createNestedFieldPath'
|
import { createNestedFieldPath } from '../../Form/createNestedFieldPath'
|
||||||
import RenderFields from '../../RenderFields'
|
import RenderFields from '../../RenderFields'
|
||||||
import withCondition from '../../withCondition'
|
import withCondition from '../../withCondition'
|
||||||
import { fieldBaseClass } from '../shared'
|
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
import { RowProvider } from './provider'
|
import { RowProvider } from './provider'
|
||||||
|
import { fieldBaseClass } from '../shared'
|
||||||
|
|
||||||
const Row: React.FC<Props> = (props) => {
|
const Row: React.FC<Props> = (props) => {
|
||||||
const {
|
const {
|
||||||
@@ -29,6 +29,7 @@ const Row: React.FC<Props> = (props) => {
|
|||||||
}))}
|
}))}
|
||||||
fieldTypes={fieldTypes}
|
fieldTypes={fieldTypes}
|
||||||
indexPath={indexPath}
|
indexPath={indexPath}
|
||||||
|
margins={false}
|
||||||
permissions={permissions}
|
permissions={permissions}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@payloadcms/richtext-slate",
|
"name": "@payloadcms/richtext-slate",
|
||||||
"version": "1.0.2",
|
"version": "1.0.1",
|
||||||
"description": "The officially supported Slate richtext adapter for Payload",
|
"description": "The officially supported Slate richtext adapter for Payload",
|
||||||
"repository": "https://github.com/payloadcms/payload",
|
"repository": "https://github.com/payloadcms/payload",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ export const AddCustomBlocks: React.FC = () => {
|
|||||||
const { addFieldRow, replaceFieldRow } = useForm()
|
const { addFieldRow, replaceFieldRow } = useForm()
|
||||||
const { value } = useField({ path: 'customBlocks' })
|
const { value } = useField({ path: 'customBlocks' })
|
||||||
|
|
||||||
|
const nextIndex = Array.isArray(value) ? value.length : 0
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={baseClass}>
|
<div className={baseClass}>
|
||||||
<div className={`${baseClass}__blocks-grid`}>
|
<div className={`${baseClass}__blocks-grid`}>
|
||||||
@@ -19,6 +21,7 @@ export const AddCustomBlocks: React.FC = () => {
|
|||||||
addFieldRow({
|
addFieldRow({
|
||||||
data: { block1Title: 'Block 1: Prefilled Title', blockType: 'block-1' },
|
data: { block1Title: 'Block 1: Prefilled Title', blockType: 'block-1' },
|
||||||
path: 'customBlocks',
|
path: 'customBlocks',
|
||||||
|
rowIndex: nextIndex,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
type="button"
|
type="button"
|
||||||
@@ -32,6 +35,7 @@ export const AddCustomBlocks: React.FC = () => {
|
|||||||
addFieldRow({
|
addFieldRow({
|
||||||
data: { block2Title: 'Block 2: Prefilled Title', blockType: 'block-2' },
|
data: { block2Title: 'Block 2: Prefilled Title', blockType: 'block-2' },
|
||||||
path: 'customBlocks',
|
path: 'customBlocks',
|
||||||
|
rowIndex: nextIndex,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
type="button"
|
type="button"
|
||||||
@@ -47,12 +51,12 @@ export const AddCustomBlocks: React.FC = () => {
|
|||||||
replaceFieldRow({
|
replaceFieldRow({
|
||||||
data: { block1Title: 'REPLACED BLOCK', blockType: 'block-1' },
|
data: { block1Title: 'REPLACED BLOCK', blockType: 'block-1' },
|
||||||
path: 'customBlocks',
|
path: 'customBlocks',
|
||||||
rowIndex: (Array.isArray(value) ? value.length : 0) - 1,
|
rowIndex: nextIndex - 1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
Replace Block {Array.isArray(value) ? value.length : 0}
|
Replace Block {nextIndex}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import type { CollectionConfig } from '../../../../packages/payload/src/collections/config/types'
|
import type { CollectionConfig } from '../../../../packages/payload/src/collections/config/types'
|
||||||
|
|
||||||
import { CollapsibleLabelComponent } from './LabelComponent'
|
import { CollapsibleLabelComponent } from './LabelComponent'
|
||||||
import { collapsibleFieldsSlug } from './shared'
|
|
||||||
|
export const collapsibleFieldsSlug = 'collapsible-fields'
|
||||||
|
|
||||||
const CollapsibleFields: CollectionConfig = {
|
const CollapsibleFields: CollectionConfig = {
|
||||||
slug: collapsibleFieldsSlug,
|
slug: collapsibleFieldsSlug,
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
export const collapsibleFieldsSlug = 'collapsible-fields'
|
|
||||||
@@ -9,7 +9,7 @@ import wait from '../../packages/payload/src/utilities/wait'
|
|||||||
import { saveDocAndAssert, saveDocHotkeyAndAssert } from '../helpers'
|
import { saveDocAndAssert, saveDocHotkeyAndAssert } from '../helpers'
|
||||||
import { AdminUrlUtil } from '../helpers/adminUrlUtil'
|
import { AdminUrlUtil } from '../helpers/adminUrlUtil'
|
||||||
import { initPayloadE2E } from '../helpers/configHelpers'
|
import { initPayloadE2E } from '../helpers/configHelpers'
|
||||||
import { collapsibleFieldsSlug } from './collections/Collapsible/shared'
|
import { collapsibleFieldsSlug } from './collections/Collapsible'
|
||||||
import { jsonDoc } from './collections/JSON'
|
import { jsonDoc } from './collections/JSON'
|
||||||
import { numberDoc } from './collections/Number'
|
import { numberDoc } from './collections/Number'
|
||||||
import { pointFieldsSlug } from './collections/Point'
|
import { pointFieldsSlug } from './collections/Point'
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
import type { Page } from '@playwright/test'
|
|
||||||
|
|
||||||
import { expect, test } from '@playwright/test'
|
|
||||||
|
|
||||||
import { saveDocAndAssert } from '../helpers'
|
|
||||||
import { AdminUrlUtil } from '../helpers/adminUrlUtil'
|
|
||||||
import { initPayloadTest } from '../helpers/configHelpers'
|
|
||||||
|
|
||||||
const { beforeAll, describe } = test
|
|
||||||
let url: AdminUrlUtil
|
|
||||||
|
|
||||||
const slug = 'nested-fields'
|
|
||||||
|
|
||||||
let page: Page
|
|
||||||
|
|
||||||
describe('Nested Fields', () => {
|
|
||||||
beforeAll(async ({ browser }) => {
|
|
||||||
const { serverURL } = await initPayloadTest({
|
|
||||||
__dirname,
|
|
||||||
init: {
|
|
||||||
local: false,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
url = new AdminUrlUtil(serverURL, slug)
|
|
||||||
|
|
||||||
const context = await browser.newContext()
|
|
||||||
page = await context.newPage()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('should save deeply nested fields', async () => {
|
|
||||||
const assertionValue = 'sample block value'
|
|
||||||
|
|
||||||
await page.goto(url.create)
|
|
||||||
|
|
||||||
await page.locator('#field-array > button').click()
|
|
||||||
await page.locator('#field-array__0__group__namedTab__blocks > button').click()
|
|
||||||
await page.locator('button[title="Block With Field"]').click()
|
|
||||||
|
|
||||||
await page.locator('#field-array__0__group__namedTab__blocks__0__text').fill(assertionValue)
|
|
||||||
|
|
||||||
await saveDocAndAssert(page)
|
|
||||||
|
|
||||||
await expect(page.locator('#field-array__0__group__namedTab__blocks__0__text')).toHaveValue(
|
|
||||||
assertionValue,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
Reference in New Issue
Block a user