fix(frontend): TProxy schema, VLESS+XHTTP flow links, clearable Jalali date picker (#5339, #5322, #5313)

- #5339: accept transportless tunnel/TProxy streamSettings that carry no
  `security` key by adding a transportless branch to SecuritySettingsSchema,
  mirroring NetworkSettingsSchema. Fixes "streamSettings.security Invalid input".
- #5322: emit XTLS Vision `flow` in panel VLESS share links for XHTTP+vlessenc
  via the shared canEnableTlsFlow predicate, so panel links match the form and
  the subscription output.
- #5313: give the Jalali expiry date picker a working clear (X) button
  (remount on clear, since the library reads `value` only on mount) and a blank
  placeholder instead of the library's hardcoded Persian text.
This commit is contained in:
MHSanaei
2026-06-15 17:20:54 +02:00
parent cdaf5f80db
commit f00512d12e
5 changed files with 187 additions and 9 deletions
@@ -1,5 +1,6 @@
.jdp-wrap {
width: 100%;
position: relative;
}
.jdp-wrap > * {
@@ -33,3 +34,38 @@
pointer-events: none;
opacity: 0.6;
}
/* persian-calendar-suite has no allowClear; overlay our own clear button so
the Jalali picker matches the Gregorian AntD DatePicker's X affordance. */
.jdp-wrap .jdp-clear {
position: absolute;
top: 50%;
right: 11px;
transform: translateY(-50%);
z-index: 1;
display: inline-flex;
align-items: center;
justify-content: center;
width: auto;
padding: 0;
border: none;
background: transparent;
cursor: pointer;
font-size: 12px;
line-height: 1;
color: rgba(0, 0, 0, 0.25);
transition: color 0.2s;
}
.jdp-wrap .jdp-clear:hover {
color: rgba(0, 0, 0, 0.45);
}
.jdp-dark .jdp-clear {
color: rgba(255, 255, 255, 0.30);
}
.jdp-dark .jdp-clear:hover,
.jdp-ultra .jdp-clear:hover {
color: rgba(255, 255, 255, 0.45);
}
@@ -1,4 +1,5 @@
import { useMemo } from 'react';
import { useEffect, useMemo, useRef, useState } from 'react';
import { CloseCircleFilled } from '@ant-design/icons';
import { DatePicker } from 'antd';
import dayjs from 'dayjs';
import type { Dayjs } from 'dayjs';
@@ -54,6 +55,10 @@ export default function DateTimePicker({
}: DateTimePickerProps) {
const { datepicker } = useDatepicker();
const { isDark, isUltra } = useTheme();
const jalaliRef = useRef<HTMLDivElement>(null);
// Bumped on clear: persian-calendar-suite reads `value` only on mount, so
// remounting via key is the only way to reflect an externally cleared value.
const [clearNonce, setClearNonce] = useState(0);
const persianTheme = useMemo(() => {
if (isUltra) return ULTRA_DARK_THEME;
@@ -61,10 +66,21 @@ export default function DateTimePicker({
return LIGHT_THEME;
}, [isDark, isUltra]);
// The library hardcodes a Persian placeholder and exposes no working prop to
// override it, so clear it (or apply the caller's) on the input directly so
// the empty field shows no leftover Persian text. No dep array: re-apply
// after every render (incl. clear-remounts).
useEffect(() => {
if (datepicker !== 'jalalian') return;
const input = jalaliRef.current?.querySelector('input');
if (input) input.placeholder = placeholder;
});
if (datepicker === 'jalalian') {
return (
<div className={`jdp-wrap${isDark ? ' jdp-dark' : ''}${isUltra ? ' jdp-ultra' : ''}${disabled ? ' jdp-disabled' : ''}`}>
<div ref={jalaliRef} className={`jdp-wrap${isDark ? ' jdp-dark' : ''}${isUltra ? ' jdp-ultra' : ''}${disabled ? ' jdp-disabled' : ''}`}>
<PersianDateTimePicker
key={clearNonce}
value={value ? value.valueOf() : null}
onChange={(next: number | string | null) => {
if (next == null || next === '') {
@@ -80,6 +96,21 @@ export default function DateTimePicker({
rtlCalendar
theme={persianTheme}
/>
{value && !disabled && (
<button
type="button"
className="jdp-clear"
aria-label="clear"
onMouseDown={(e) => e.preventDefault()}
onClick={(e) => {
e.stopPropagation();
onChange(null);
setClearNonce((n) => n + 1);
}}
>
<CloseCircleFilled />
</button>
)}
</div>
);
}