mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-18 23:45:51 +08:00
feat: global variables
This commit is contained in:
parent
985651454a
commit
c2b8beffac
@ -0,0 +1,3 @@
|
|||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.23814 1.33333H9.76188C10.4844 1.33332 11.0672 1.33332 11.5391 1.37187C12.025 1.41157 12.4518 1.49545 12.8466 1.69664C13.4739 2.01622 13.9838 2.52615 14.3034 3.15336C14.5046 3.54822 14.5884 3.97501 14.6281 4.46091C14.6667 4.93283 14.6667 5.51559 14.6667 6.23811V9.76188C14.6667 10.4844 14.6667 11.0672 14.6281 11.5391C14.5884 12.025 14.5046 12.4518 14.3034 12.8466C13.9838 13.4738 13.4739 13.9838 12.8466 14.3033C12.4518 14.5045 12.025 14.5884 11.5391 14.6281C11.0672 14.6667 10.4844 14.6667 9.7619 14.6667H6.23812C5.51561 14.6667 4.93284 14.6667 4.46093 14.6281C3.97503 14.5884 3.54824 14.5045 3.15338 14.3033C2.52617 13.9838 2.01623 13.4738 1.69666 12.8466C1.49546 12.4518 1.41159 12.025 1.37189 11.5391C1.33333 11.0672 1.33334 10.4844 1.33334 9.76187V6.23812C1.33334 5.5156 1.33333 4.93283 1.37189 4.46091C1.41159 3.97501 1.49546 3.54822 1.69666 3.15336C2.01623 2.52615 2.52617 2.01622 3.15338 1.69664C3.54824 1.49545 3.97503 1.41157 4.46093 1.37187C4.93285 1.33332 5.51561 1.33332 6.23814 1.33333ZM4.5695 2.70078C4.16606 2.73374 3.93427 2.79519 3.7587 2.88465C3.38237 3.0764 3.07641 3.38236 2.88466 3.75868C2.79521 3.93425 2.73376 4.16604 2.70079 4.56949C2.6672 4.98072 2.66668 5.50892 2.66668 6.26666V9.73333C2.66668 10.4911 2.6672 11.0193 2.70079 11.4305C2.73376 11.8339 2.79521 12.0657 2.88466 12.2413C3.07641 12.6176 3.38237 12.9236 3.7587 13.1153C3.93427 13.2048 4.16606 13.2662 4.5695 13.2992C4.98073 13.3328 5.50894 13.3333 6.26668 13.3333H9.73334C10.4911 13.3333 11.0193 13.3328 11.4305 13.2992C11.834 13.2662 12.0658 13.2048 12.2413 13.1153C12.6176 12.9236 12.9236 12.6176 13.1154 12.2413C13.2048 12.0657 13.2663 11.8339 13.2992 11.4305C13.3328 11.0193 13.3333 10.4911 13.3333 9.73333V6.26666C13.3333 5.50892 13.3328 4.98072 13.2992 4.56949C13.2663 4.16604 13.2048 3.93425 13.1154 3.75868C12.9236 3.38236 12.6176 3.0764 12.2413 2.88465C12.0658 2.79519 11.834 2.73374 11.4305 2.70078C11.0193 2.66718 10.4911 2.66666 9.73334 2.66666H6.26668C5.50894 2.66666 4.98073 2.66718 4.5695 2.70078ZM5.08339 5.33333C5.08339 4.96514 5.38187 4.66666 5.75006 4.66666H6.68433C7.324 4.66666 7.87606 5.09677 8.04724 5.70542L8.30138 6.60902L9.2915 5.43554C9.7018 4.94926 10.3035 4.66666 10.9399 4.66666H11C11.3682 4.66666 11.6667 4.96514 11.6667 5.33333C11.6667 5.70152 11.3682 5.99999 11 5.99999H10.9399C10.7005 5.99999 10.4702 6.10616 10.3106 6.29537L8.73751 8.15972L9.23641 9.93357C9.24921 9.97909 9.28574 10 9.31579 10H10.2501C10.6182 10 10.9167 10.2985 10.9167 10.6667C10.9167 11.0349 10.6182 11.3333 10.2501 11.3333H9.31579C8.67612 11.3333 8.12406 10.9032 7.95288 10.2946L7.69871 9.39088L6.70852 10.5644C6.29822 11.0507 5.6965 11.3333 5.06011 11.3333H5.00001C4.63182 11.3333 4.33334 11.0349 4.33334 10.6667C4.33334 10.2985 4.63182 10 5.00001 10H5.06011C5.29949 10 5.52982 9.89383 5.68946 9.70462L7.26258 7.84019L6.76371 6.06642C6.75091 6.0209 6.71438 5.99999 6.68433 5.99999H5.75006C5.38187 5.99999 5.08339 5.70152 5.08339 5.33333Z" fill="#354052"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.0 KiB |
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"icon": {
|
||||||
|
"type": "element",
|
||||||
|
"isRootNode": true,
|
||||||
|
"name": "svg",
|
||||||
|
"attributes": {
|
||||||
|
"width": "16",
|
||||||
|
"height": "16",
|
||||||
|
"viewBox": "0 0 16 16",
|
||||||
|
"fill": "none",
|
||||||
|
"xmlns": "http://www.w3.org/2000/svg"
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"name": "path",
|
||||||
|
"attributes": {
|
||||||
|
"fill-rule": "evenodd",
|
||||||
|
"clip-rule": "evenodd",
|
||||||
|
"d": "M6.23814 1.33333H9.76188C10.4844 1.33332 11.0672 1.33332 11.5391 1.37187C12.025 1.41157 12.4518 1.49545 12.8466 1.69664C13.4739 2.01622 13.9838 2.52615 14.3034 3.15336C14.5046 3.54822 14.5884 3.97501 14.6281 4.46091C14.6667 4.93283 14.6667 5.51559 14.6667 6.23811V9.76188C14.6667 10.4844 14.6667 11.0672 14.6281 11.5391C14.5884 12.025 14.5046 12.4518 14.3034 12.8466C13.9838 13.4738 13.4739 13.9838 12.8466 14.3033C12.4518 14.5045 12.025 14.5884 11.5391 14.6281C11.0672 14.6667 10.4844 14.6667 9.7619 14.6667H6.23812C5.51561 14.6667 4.93284 14.6667 4.46093 14.6281C3.97503 14.5884 3.54824 14.5045 3.15338 14.3033C2.52617 13.9838 2.01623 13.4738 1.69666 12.8466C1.49546 12.4518 1.41159 12.025 1.37189 11.5391C1.33333 11.0672 1.33334 10.4844 1.33334 9.76187V6.23812C1.33334 5.5156 1.33333 4.93283 1.37189 4.46091C1.41159 3.97501 1.49546 3.54822 1.69666 3.15336C2.01623 2.52615 2.52617 2.01622 3.15338 1.69664C3.54824 1.49545 3.97503 1.41157 4.46093 1.37187C4.93285 1.33332 5.51561 1.33332 6.23814 1.33333ZM4.5695 2.70078C4.16606 2.73374 3.93427 2.79519 3.7587 2.88465C3.38237 3.0764 3.07641 3.38236 2.88466 3.75868C2.79521 3.93425 2.73376 4.16604 2.70079 4.56949C2.6672 4.98072 2.66668 5.50892 2.66668 6.26666V9.73333C2.66668 10.4911 2.6672 11.0193 2.70079 11.4305C2.73376 11.8339 2.79521 12.0657 2.88466 12.2413C3.07641 12.6176 3.38237 12.9236 3.7587 13.1153C3.93427 13.2048 4.16606 13.2662 4.5695 13.2992C4.98073 13.3328 5.50894 13.3333 6.26668 13.3333H9.73334C10.4911 13.3333 11.0193 13.3328 11.4305 13.2992C11.834 13.2662 12.0658 13.2048 12.2413 13.1153C12.6176 12.9236 12.9236 12.6176 13.1154 12.2413C13.2048 12.0657 13.2663 11.8339 13.2992 11.4305C13.3328 11.0193 13.3333 10.4911 13.3333 9.73333V6.26666C13.3333 5.50892 13.3328 4.98072 13.2992 4.56949C13.2663 4.16604 13.2048 3.93425 13.1154 3.75868C12.9236 3.38236 12.6176 3.0764 12.2413 2.88465C12.0658 2.79519 11.834 2.73374 11.4305 2.70078C11.0193 2.66718 10.4911 2.66666 9.73334 2.66666H6.26668C5.50894 2.66666 4.98073 2.66718 4.5695 2.70078ZM5.08339 5.33333C5.08339 4.96514 5.38187 4.66666 5.75006 4.66666H6.68433C7.324 4.66666 7.87606 5.09677 8.04724 5.70542L8.30138 6.60902L9.2915 5.43554C9.7018 4.94926 10.3035 4.66666 10.9399 4.66666H11C11.3682 4.66666 11.6667 4.96514 11.6667 5.33333C11.6667 5.70152 11.3682 5.99999 11 5.99999H10.9399C10.7005 5.99999 10.4702 6.10616 10.3106 6.29537L8.73751 8.15972L9.23641 9.93357C9.24921 9.97909 9.28574 10 9.31579 10H10.2501C10.6182 10 10.9167 10.2985 10.9167 10.6667C10.9167 11.0349 10.6182 11.3333 10.2501 11.3333H9.31579C8.67612 11.3333 8.12406 10.9032 7.95288 10.2946L7.69871 9.39088L6.70852 10.5644C6.29822 11.0507 5.6965 11.3333 5.06011 11.3333H5.00001C4.63182 11.3333 4.33334 11.0349 4.33334 10.6667C4.33334 10.2985 4.63182 10 5.00001 10H5.06011C5.29949 10 5.52982 9.89383 5.68946 9.70462L7.26258 7.84019L6.76371 6.06642C6.75091 6.0209 6.71438 5.99999 6.68433 5.99999H5.75006C5.38187 5.99999 5.08339 5.70152 5.08339 5.33333Z",
|
||||||
|
"fill": "currentColor"
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"name": "GlobalVariable"
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
// GENERATE BY script
|
||||||
|
// DON NOT EDIT IT MANUALLY
|
||||||
|
|
||||||
|
import * as React from 'react'
|
||||||
|
import data from './GlobalVariable.json'
|
||||||
|
import IconBase from '@/app/components/base/icons/IconBase'
|
||||||
|
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
|
||||||
|
|
||||||
|
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
|
||||||
|
props,
|
||||||
|
ref,
|
||||||
|
) => <IconBase {...props} ref={ref} data={data as IconData} />)
|
||||||
|
|
||||||
|
Icon.displayName = 'GlobalVariable'
|
||||||
|
|
||||||
|
export default Icon
|
@ -5,6 +5,7 @@ export { default as DragHandle } from './DragHandle'
|
|||||||
export { default as Env } from './Env'
|
export { default as Env } from './Env'
|
||||||
export { default as Exchange02 } from './Exchange02'
|
export { default as Exchange02 } from './Exchange02'
|
||||||
export { default as FileCode } from './FileCode'
|
export { default as FileCode } from './FileCode'
|
||||||
|
export { default as GlobalVariable } from './GlobalVariable'
|
||||||
export { default as Icon3Dots } from './Icon3Dots'
|
export { default as Icon3Dots } from './Icon3Dots'
|
||||||
export { default as LongArrowLeft } from './LongArrowLeft'
|
export { default as LongArrowLeft } from './LongArrowLeft'
|
||||||
export { default as LongArrowRight } from './LongArrowRight'
|
export { default as LongArrowRight } from './LongArrowRight'
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
import { memo } from 'react'
|
||||||
|
import Button from '@/app/components/base/button'
|
||||||
|
import { GlobalVariable } from '@/app/components/base/icons/src/vender/line/others'
|
||||||
|
import { useStore } from '@/app/components/workflow/store'
|
||||||
|
|
||||||
|
const GlobalVariableButton = ({ disabled }: { disabled: boolean }) => {
|
||||||
|
const setShowPanel = useStore(s => s.setShowGlobalVariablePanel)
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
setShowPanel(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button className='p-2' disabled={disabled} onClick={handleClick}>
|
||||||
|
<GlobalVariable className='w-4 h-4 text-components-button-secondary-text' />
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(GlobalVariableButton)
|
@ -168,6 +168,7 @@ const Header: FC = () => {
|
|||||||
{
|
{
|
||||||
normal && (
|
normal && (
|
||||||
<div className='flex items-center gap-2'>
|
<div className='flex items-center gap-2'>
|
||||||
|
{/* <GlobalVariableButton disabled={nodesReadOnly} /> */}
|
||||||
{isChatMode && <ChatVariableButton disabled={nodesReadOnly} />}
|
{isChatMode && <ChatVariableButton disabled={nodesReadOnly} />}
|
||||||
<EnvButton disabled={nodesReadOnly} />
|
<EnvButton disabled={nodesReadOnly} />
|
||||||
<div className='w-[1px] h-3.5 bg-gray-200'></div>
|
<div className='w-[1px] h-3.5 bg-gray-200'></div>
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
import {
|
||||||
|
memo,
|
||||||
|
} from 'react'
|
||||||
|
|
||||||
|
import { RiCloseLine } from '@remixicon/react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import type { GlobalVariable } from '../../types'
|
||||||
|
import Item from './item'
|
||||||
|
import { useStore } from '@/app/components/workflow/store'
|
||||||
|
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
|
const Panel = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const setShowPanel = useStore(s => s.setShowGlobalVariablePanel)
|
||||||
|
|
||||||
|
const globalVariableList: GlobalVariable[] = [
|
||||||
|
{
|
||||||
|
name: 'conversation_id',
|
||||||
|
value_type: 'string',
|
||||||
|
description: 'conversation id',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'relative flex flex-col w-[420px] bg-components-panel-bg-alt rounded-l-2xl h-full border border-components-panel-border',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className='shrink-0 flex items-center justify-between p-4 pb-0 text-text-primary system-xl-semibold'>
|
||||||
|
Global Variables(Current not show)
|
||||||
|
<div className='flex items-center'>
|
||||||
|
<div
|
||||||
|
className='flex items-center justify-center w-6 h-6 cursor-pointer'
|
||||||
|
onClick={() => setShowPanel(false)}
|
||||||
|
>
|
||||||
|
<RiCloseLine className='w-4 h-4 text-text-tertiary' />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='shrink-0 py-1 px-4 system-sm-regular text-text-tertiary'>...</div>
|
||||||
|
|
||||||
|
<div className='grow px-4 rounded-b-2xl overflow-y-auto'>
|
||||||
|
{globalVariableList.map(item => (
|
||||||
|
<Item
|
||||||
|
key={item.name}
|
||||||
|
payload={item}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(Panel)
|
@ -0,0 +1,30 @@
|
|||||||
|
import { memo } from 'react'
|
||||||
|
import { capitalize } from 'lodash-es'
|
||||||
|
import { Env } from '@/app/components/base/icons/src/vender/line/others'
|
||||||
|
import type { GlobalVariable } from '@/app/components/workflow/types'
|
||||||
|
import cn from '@/utils/classnames'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
payload: GlobalVariable
|
||||||
|
}
|
||||||
|
|
||||||
|
const Item = ({
|
||||||
|
payload,
|
||||||
|
}: Props) => {
|
||||||
|
return (
|
||||||
|
<div className={cn(
|
||||||
|
'mb-1 px-2.5 py-2 bg-components-panel-on-panel-item-bg radius-md border border-components-panel-border-subtle shadow-xs hover:bg-components-panel-on-panel-item-bg-hover',
|
||||||
|
)}>
|
||||||
|
<div className='flex items-center justify-between'>
|
||||||
|
<div className='grow flex gap-1 items-center'>
|
||||||
|
<Env className='w-4 h-4 text-util-colors-violet-violet-600' />
|
||||||
|
<div className='text-text-primary system-sm-medium'>{payload.name}</div>
|
||||||
|
<div className='text-text-tertiary system-xs-medium'>{capitalize(payload.value_type)}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='text-text-tertiary system-xs-regular truncate'>{payload.description}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(Item)
|
@ -14,6 +14,7 @@ import WorkflowPreview from './workflow-preview'
|
|||||||
import ChatRecord from './chat-record'
|
import ChatRecord from './chat-record'
|
||||||
import ChatVariablePanel from './chat-variable-panel'
|
import ChatVariablePanel from './chat-variable-panel'
|
||||||
import EnvPanel from './env-panel'
|
import EnvPanel from './env-panel'
|
||||||
|
import GlobalVariablePanel from './global-variable-panel'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||||
import MessageLogModal from '@/app/components/base/message-log-modal'
|
import MessageLogModal from '@/app/components/base/message-log-modal'
|
||||||
@ -26,6 +27,7 @@ const Panel: FC = () => {
|
|||||||
const showDebugAndPreviewPanel = useStore(s => s.showDebugAndPreviewPanel)
|
const showDebugAndPreviewPanel = useStore(s => s.showDebugAndPreviewPanel)
|
||||||
const showEnvPanel = useStore(s => s.showEnvPanel)
|
const showEnvPanel = useStore(s => s.showEnvPanel)
|
||||||
const showChatVariablePanel = useStore(s => s.showChatVariablePanel)
|
const showChatVariablePanel = useStore(s => s.showChatVariablePanel)
|
||||||
|
const showGlobalVariablePanel = useStore(s => s.showGlobalVariablePanel)
|
||||||
const isRestoring = useStore(s => s.isRestoring)
|
const isRestoring = useStore(s => s.isRestoring)
|
||||||
const { currentLogItem, setCurrentLogItem, showMessageLogModal, setShowMessageLogModal, currentLogModalActiveTab } = useAppStore(useShallow(state => ({
|
const { currentLogItem, setCurrentLogItem, showMessageLogModal, setShowMessageLogModal, currentLogModalActiveTab } = useAppStore(useShallow(state => ({
|
||||||
currentLogItem: state.currentLogItem,
|
currentLogItem: state.currentLogItem,
|
||||||
@ -90,6 +92,11 @@ const Panel: FC = () => {
|
|||||||
<ChatVariablePanel />
|
<ChatVariablePanel />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
showGlobalVariablePanel && (
|
||||||
|
<GlobalVariablePanel />
|
||||||
|
)
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -109,6 +109,8 @@ type Shape = {
|
|||||||
setEnvSecrets: (envSecrets: Record<string, string>) => void
|
setEnvSecrets: (envSecrets: Record<string, string>) => void
|
||||||
showChatVariablePanel: boolean
|
showChatVariablePanel: boolean
|
||||||
setShowChatVariablePanel: (showChatVariablePanel: boolean) => void
|
setShowChatVariablePanel: (showChatVariablePanel: boolean) => void
|
||||||
|
showGlobalVariablePanel: boolean
|
||||||
|
setShowGlobalVariablePanel: (showGlobalVariablePanel: boolean) => void
|
||||||
conversationVariables: ConversationVariable[]
|
conversationVariables: ConversationVariable[]
|
||||||
setConversationVariables: (conversationVariables: ConversationVariable[]) => void
|
setConversationVariables: (conversationVariables: ConversationVariable[]) => void
|
||||||
selection: null | { x1: number; y1: number; x2: number; y2: number }
|
selection: null | { x1: number; y1: number; x2: number; y2: number }
|
||||||
@ -165,6 +167,12 @@ type Shape = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const createWorkflowStore = () => {
|
export const createWorkflowStore = () => {
|
||||||
|
const hideAllPanel = {
|
||||||
|
showDebugAndPreviewPanel: false,
|
||||||
|
showEnvPanel: false,
|
||||||
|
showChatVariablePanel: false,
|
||||||
|
showGlobalVariablePanel: false,
|
||||||
|
}
|
||||||
return createStore<Shape>(set => ({
|
return createStore<Shape>(set => ({
|
||||||
appId: '',
|
appId: '',
|
||||||
panelWidth: localStorage.getItem('workflow-node-panel-width') ? parseFloat(localStorage.getItem('workflow-node-panel-width')!) : 420,
|
panelWidth: localStorage.getItem('workflow-node-panel-width') ? parseFloat(localStorage.getItem('workflow-node-panel-width')!) : 420,
|
||||||
@ -225,6 +233,13 @@ export const createWorkflowStore = () => {
|
|||||||
setEnvSecrets: envSecrets => set(() => ({ envSecrets })),
|
setEnvSecrets: envSecrets => set(() => ({ envSecrets })),
|
||||||
showChatVariablePanel: false,
|
showChatVariablePanel: false,
|
||||||
setShowChatVariablePanel: showChatVariablePanel => set(() => ({ showChatVariablePanel })),
|
setShowChatVariablePanel: showChatVariablePanel => set(() => ({ showChatVariablePanel })),
|
||||||
|
showGlobalVariablePanel: false,
|
||||||
|
setShowGlobalVariablePanel: showGlobalVariablePanel => set(() => {
|
||||||
|
if (showGlobalVariablePanel)
|
||||||
|
return { ...hideAllPanel, showGlobalVariablePanel: true }
|
||||||
|
else
|
||||||
|
return { showGlobalVariablePanel: false }
|
||||||
|
}),
|
||||||
conversationVariables: [],
|
conversationVariables: [],
|
||||||
setConversationVariables: conversationVariables => set(() => ({ conversationVariables })),
|
setConversationVariables: conversationVariables => set(() => ({ conversationVariables })),
|
||||||
selection: null,
|
selection: null,
|
||||||
|
@ -126,6 +126,12 @@ export type ConversationVariable = {
|
|||||||
description: string
|
description: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type GlobalVariable = {
|
||||||
|
name: string
|
||||||
|
value_type: 'string' | 'number'
|
||||||
|
description: string
|
||||||
|
}
|
||||||
|
|
||||||
export type VariableWithValue = {
|
export type VariableWithValue = {
|
||||||
key: string
|
key: string
|
||||||
value: string
|
value: string
|
||||||
|
Loading…
x
Reference in New Issue
Block a user