mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-24 16:02:55 +08:00
fix: address review feedback on error details
- Remove tautological hasErrorDetails function - Remove dead CSS classes (.error-detail-row, .error-detail-label, .error-detail-value) - Remove unused i18n keys (error.details.type/statusCode/provider/retryable/retries/responseBody) - Remove all error.details.* keys from packages/app/ (web app doesn't use ErrorDetails) - Wire ErrorDetails collapsible into ErrorDisplay fallback Card
This commit is contained in:
@@ -731,12 +731,4 @@ export const dict = {
|
||||
"workspace.reset.archived.one": "ستتم أرشفة جلسة واحدة.",
|
||||
"workspace.reset.archived.many": "ستتم أرشفة {{count}} جلسات.",
|
||||
"workspace.reset.note": "سيؤدي هذا إلى إعادة تعيين مساحة العمل لتتطابق مع الفرع الافتراضي.",
|
||||
|
||||
"error.details.show": "التفاصيل",
|
||||
"error.details.type": "النوع",
|
||||
"error.details.statusCode": "رمز الحالة",
|
||||
"error.details.provider": "المزود",
|
||||
"error.details.retryable": "قابل لإعادة المحاولة",
|
||||
"error.details.retries": "المحاولات",
|
||||
"error.details.responseBody": "الاستجابة",
|
||||
}
|
||||
|
||||
@@ -740,12 +740,4 @@ export const dict = {
|
||||
"workspace.reset.archived.one": "1 sessão será arquivada.",
|
||||
"workspace.reset.archived.many": "{{count}} sessões serão arquivadas.",
|
||||
"workspace.reset.note": "Isso redefinirá o espaço de trabalho para corresponder ao branch padrão.",
|
||||
|
||||
"error.details.show": "Detalhes",
|
||||
"error.details.type": "Tipo",
|
||||
"error.details.statusCode": "Código de status",
|
||||
"error.details.provider": "Provedor",
|
||||
"error.details.retryable": "Repetível",
|
||||
"error.details.retries": "Tentativas",
|
||||
"error.details.responseBody": "Resposta",
|
||||
}
|
||||
|
||||
@@ -817,12 +817,4 @@ export const dict = {
|
||||
"workspace.reset.archived.one": "1 sesija će biti arhivirana.",
|
||||
"workspace.reset.archived.many": "Biće arhivirano {{count}} sesija.",
|
||||
"workspace.reset.note": "Ovo će resetovati radni prostor da odgovara podrazumijevanoj grani.",
|
||||
|
||||
"error.details.show": "Detalji",
|
||||
"error.details.type": "Tip",
|
||||
"error.details.statusCode": "Statusni kod",
|
||||
"error.details.provider": "Provajder",
|
||||
"error.details.retryable": "Ponovljivo",
|
||||
"error.details.retries": "Pokušaji",
|
||||
"error.details.responseBody": "Odgovor",
|
||||
}
|
||||
|
||||
@@ -811,12 +811,4 @@ export const dict = {
|
||||
"workspace.reset.archived.one": "1 session vil blive arkiveret.",
|
||||
"workspace.reset.archived.many": "{{count}} sessioner vil blive arkiveret.",
|
||||
"workspace.reset.note": "Dette vil nulstille arbejdsområdet til at matche hovedgrenen.",
|
||||
|
||||
"error.details.show": "Detaljer",
|
||||
"error.details.type": "Type",
|
||||
"error.details.statusCode": "Statuskode",
|
||||
"error.details.provider": "Udbyder",
|
||||
"error.details.retryable": "Kan forsøges igen",
|
||||
"error.details.retries": "Forsøg",
|
||||
"error.details.responseBody": "Svar",
|
||||
}
|
||||
|
||||
@@ -749,12 +749,4 @@ export const dict = {
|
||||
"workspace.reset.archived.one": "1 Sitzung wird archiviert.",
|
||||
"workspace.reset.archived.many": "{{count}} Sitzungen werden archiviert.",
|
||||
"workspace.reset.note": "Dadurch wird der Arbeitsbereich auf den Standard-Branch zurückgesetzt.",
|
||||
|
||||
"error.details.show": "Details",
|
||||
"error.details.type": "Typ",
|
||||
"error.details.statusCode": "Statuscode",
|
||||
"error.details.provider": "Anbieter",
|
||||
"error.details.retryable": "Wiederholbar",
|
||||
"error.details.retries": "Versuche",
|
||||
"error.details.responseBody": "Antwort",
|
||||
} satisfies Partial<Record<Keys, string>>
|
||||
|
||||
@@ -825,13 +825,4 @@ export const dict = {
|
||||
"workspace.reset.archived.one": "1 session will be archived.",
|
||||
"workspace.reset.archived.many": "{{count}} sessions will be archived.",
|
||||
"workspace.reset.note": "This will reset the workspace to match the default branch.",
|
||||
|
||||
// error details
|
||||
"error.details.show": "Details",
|
||||
"error.details.type": "Type",
|
||||
"error.details.statusCode": "Status code",
|
||||
"error.details.provider": "Provider",
|
||||
"error.details.retryable": "Retryable",
|
||||
"error.details.retries": "Retries",
|
||||
"error.details.responseBody": "Response",
|
||||
}
|
||||
|
||||
@@ -823,12 +823,4 @@ export const dict = {
|
||||
"workspace.reset.archived.one": "1 sesión será archivada.",
|
||||
"workspace.reset.archived.many": "{{count}} sesiones serán archivadas.",
|
||||
"workspace.reset.note": "Esto restablecerá el espacio de trabajo para coincidir con la rama predeterminada.",
|
||||
|
||||
"error.details.show": "Detalles",
|
||||
"error.details.type": "Tipo",
|
||||
"error.details.statusCode": "Código de estado",
|
||||
"error.details.provider": "Proveedor",
|
||||
"error.details.retryable": "Reintentable",
|
||||
"error.details.retries": "Reintentos",
|
||||
"error.details.responseBody": "Respuesta",
|
||||
}
|
||||
|
||||
@@ -747,12 +747,4 @@ export const dict = {
|
||||
"workspace.reset.archived.one": "1 session sera archivée.",
|
||||
"workspace.reset.archived.many": "{{count}} sessions seront archivées.",
|
||||
"workspace.reset.note": "Cela réinitialisera l'espace de travail pour correspondre à la branche par défaut.",
|
||||
|
||||
"error.details.show": "Détails",
|
||||
"error.details.type": "Type",
|
||||
"error.details.statusCode": "Code de statut",
|
||||
"error.details.provider": "Fournisseur",
|
||||
"error.details.retryable": "Réessayable",
|
||||
"error.details.retries": "Tentatives",
|
||||
"error.details.responseBody": "Réponse",
|
||||
}
|
||||
|
||||
@@ -736,12 +736,4 @@ export const dict = {
|
||||
"workspace.reset.archived.one": "1つのセッションがアーカイブされます。",
|
||||
"workspace.reset.archived.many": "{{count}}個のセッションがアーカイブされます。",
|
||||
"workspace.reset.note": "これにより、ワークスペースはデフォルトブランチと一致するようにリセットされます。",
|
||||
|
||||
"error.details.show": "詳細",
|
||||
"error.details.type": "タイプ",
|
||||
"error.details.statusCode": "ステータスコード",
|
||||
"error.details.provider": "プロバイダー",
|
||||
"error.details.retryable": "リトライ可能",
|
||||
"error.details.retries": "リトライ回数",
|
||||
"error.details.responseBody": "レスポンス",
|
||||
}
|
||||
|
||||
@@ -736,12 +736,4 @@ export const dict = {
|
||||
"workspace.reset.archived.one": "1개의 세션이 보관됩니다.",
|
||||
"workspace.reset.archived.many": "{{count}}개의 세션이 보관됩니다.",
|
||||
"workspace.reset.note": "이 작업은 작업 공간을 기본 브랜치와 일치하도록 재설정합니다.",
|
||||
|
||||
"error.details.show": "상세 정보",
|
||||
"error.details.type": "유형",
|
||||
"error.details.statusCode": "상태 코드",
|
||||
"error.details.provider": "제공자",
|
||||
"error.details.retryable": "재시도 가능",
|
||||
"error.details.retries": "재시도 횟수",
|
||||
"error.details.responseBody": "응답",
|
||||
}
|
||||
|
||||
@@ -738,12 +738,4 @@ export const dict = {
|
||||
"workspace.reset.archived.one": "1 sesja zostanie zarchiwizowana.",
|
||||
"workspace.reset.archived.many": "{{count}} sesji zostanie zarchiwizowanych.",
|
||||
"workspace.reset.note": "To zresetuje przestrzeń roboczą, aby odpowiadała domyślnej gałęzi.",
|
||||
|
||||
"error.details.show": "Szczegóły",
|
||||
"error.details.type": "Typ",
|
||||
"error.details.statusCode": "Kod statusu",
|
||||
"error.details.provider": "Dostawca",
|
||||
"error.details.retryable": "Możliwa ponowna próba",
|
||||
"error.details.retries": "Próby",
|
||||
"error.details.responseBody": "Odpowiedź",
|
||||
}
|
||||
|
||||
@@ -819,12 +819,4 @@ export const dict = {
|
||||
"workspace.reset.archived.one": "1 сессия будет архивирована.",
|
||||
"workspace.reset.archived.many": "{{count}} сессий будет архивировано.",
|
||||
"workspace.reset.note": "Рабочее пространство будет сброшено в соответствие с веткой по умолчанию.",
|
||||
|
||||
"error.details.show": "Подробности",
|
||||
"error.details.type": "Тип",
|
||||
"error.details.statusCode": "Код статуса",
|
||||
"error.details.provider": "Провайдер",
|
||||
"error.details.retryable": "Повторяемый",
|
||||
"error.details.retries": "Попытки",
|
||||
"error.details.responseBody": "Ответ",
|
||||
}
|
||||
|
||||
@@ -808,12 +808,4 @@ export const dict = {
|
||||
"workspace.reset.archived.one": "1 เซสชันจะถูกจัดเก็บ",
|
||||
"workspace.reset.archived.many": "{{count}} เซสชันจะถูกจัดเก็บ",
|
||||
"workspace.reset.note": "สิ่งนี้จะรีเซ็ตพื้นที่ทำงานให้ตรงกับสาขาเริ่มต้น",
|
||||
|
||||
"error.details.show": "รายละเอียด",
|
||||
"error.details.type": "ประเภท",
|
||||
"error.details.statusCode": "รหัสสถานะ",
|
||||
"error.details.provider": "ผู้ให้บริการ",
|
||||
"error.details.retryable": "ลองใหม่ได้",
|
||||
"error.details.retries": "จำนวนครั้งที่ลอง",
|
||||
"error.details.responseBody": "การตอบกลับ",
|
||||
}
|
||||
|
||||
@@ -807,12 +807,4 @@ export const dict = {
|
||||
"workspace.reset.archived.one": "将归档 1 个会话。",
|
||||
"workspace.reset.archived.many": "将归档 {{count}} 个会话。",
|
||||
"workspace.reset.note": "这将把工作区重置为与默认分支一致。",
|
||||
|
||||
"error.details.show": "详细信息",
|
||||
"error.details.type": "类型",
|
||||
"error.details.statusCode": "状态码",
|
||||
"error.details.provider": "提供商",
|
||||
"error.details.retryable": "可重试",
|
||||
"error.details.retries": "重试次数",
|
||||
"error.details.responseBody": "响应",
|
||||
} satisfies Partial<Record<Keys, string>>
|
||||
|
||||
@@ -801,12 +801,4 @@ export const dict = {
|
||||
"workspace.reset.archived.one": "將封存 1 個工作階段。",
|
||||
"workspace.reset.archived.many": "將封存 {{count}} 個工作階段。",
|
||||
"workspace.reset.note": "這將把工作區重設為與預設分支一致。",
|
||||
|
||||
"error.details.show": "詳細資訊",
|
||||
"error.details.type": "類型",
|
||||
"error.details.statusCode": "狀態碼",
|
||||
"error.details.provider": "提供者",
|
||||
"error.details.retryable": "可重試",
|
||||
"error.details.retries": "重試次數",
|
||||
"error.details.responseBody": "回應",
|
||||
} satisfies Partial<Record<Keys, string>>
|
||||
|
||||
@@ -27,23 +27,6 @@
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.error-detail-row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.error-detail-label {
|
||||
font-weight: var(--font-weight-medium, 500);
|
||||
white-space: nowrap;
|
||||
min-width: 70px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.error-detail-value {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.error-detail-pre {
|
||||
margin: 0;
|
||||
max-height: 120px;
|
||||
|
||||
@@ -2,11 +2,6 @@ import type { AssistantMessage } from "@kilocode/sdk/v2/client"
|
||||
|
||||
type ErrorType = NonNullable<AssistantMessage["error"]>
|
||||
|
||||
export function hasErrorDetails(error: ErrorType | undefined): boolean {
|
||||
if (!error) return false
|
||||
return true
|
||||
}
|
||||
|
||||
interface ErrorDetailsProps {
|
||||
error: ErrorType
|
||||
}
|
||||
|
||||
@@ -5,19 +5,12 @@ import type { UiI18nKey, UiI18nParams } from "@opencode-ai/ui/context/i18n"
|
||||
import { I18nProvider } from "@opencode-ai/ui/context/i18n"
|
||||
import { Card } from "@opencode-ai/ui/card"
|
||||
import { Collapsible } from "@opencode-ai/ui/collapsible"
|
||||
import { ErrorDetails, hasErrorDetails } from "../components/error-details"
|
||||
import { Show } from "solid-js"
|
||||
import { ErrorDetails } from "../components/error-details"
|
||||
|
||||
type ErrorType = NonNullable<AssistantMessage["error"]>
|
||||
|
||||
const labels: Record<string, string> = {
|
||||
"error.details.show": "Details",
|
||||
"error.details.type": "Type",
|
||||
"error.details.statusCode": "Status code",
|
||||
"error.details.provider": "Provider",
|
||||
"error.details.retryable": "Retryable",
|
||||
"error.details.retries": "Retries",
|
||||
"error.details.responseBody": "Response",
|
||||
}
|
||||
|
||||
const i18nValue = {
|
||||
@@ -36,17 +29,15 @@ function ErrorCard(props: { error: ErrorType; defaultOpen?: boolean }) {
|
||||
<div style={{ width: "600px" }}>
|
||||
<Card variant="error" class="error-card">
|
||||
<div>{message}</div>
|
||||
<Show when={hasErrorDetails(props.error)}>
|
||||
<Collapsible variant="ghost" defaultOpen={props.defaultOpen}>
|
||||
<Collapsible.Trigger class="error-details-trigger">
|
||||
<span>Details</span>
|
||||
<Collapsible.Arrow />
|
||||
</Collapsible.Trigger>
|
||||
<Collapsible.Content>
|
||||
<ErrorDetails error={props.error} />
|
||||
</Collapsible.Content>
|
||||
</Collapsible>
|
||||
</Show>
|
||||
<Collapsible variant="ghost" defaultOpen={props.defaultOpen}>
|
||||
<Collapsible.Trigger class="error-details-trigger">
|
||||
<span>Details</span>
|
||||
<Collapsible.Arrow />
|
||||
</Collapsible.Trigger>
|
||||
<Collapsible.Content>
|
||||
<ErrorDetails error={props.error} />
|
||||
</Collapsible.Content>
|
||||
</Collapsible>
|
||||
</Card>
|
||||
</div>
|
||||
</I18nProvider>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { Component, createMemo, Switch, Match } from "solid-js"
|
||||
import { Card } from "@kilocode/kilo-ui/card"
|
||||
import { Collapsible } from "@kilocode/kilo-ui/collapsible"
|
||||
import { ErrorDetails } from "@kilocode/kilo-ui/error-details"
|
||||
import { Button } from "@kilocode/kilo-ui/button"
|
||||
import type { AssistantMessage } from "@kilocode/sdk/v2"
|
||||
import { useLanguage } from "../../context/language"
|
||||
@@ -31,6 +33,15 @@ export const ErrorDisplay: Component<ErrorDisplayProps> = (props) => {
|
||||
fallback={
|
||||
<Card variant="error" class="error-card">
|
||||
{errorText()}
|
||||
<Collapsible variant="ghost">
|
||||
<Collapsible.Trigger class="error-details-trigger">
|
||||
<span>{t("error.details.show")}</span>
|
||||
<Collapsible.Arrow />
|
||||
</Collapsible.Trigger>
|
||||
<Collapsible.Content>
|
||||
<ErrorDetails error={props.error} />
|
||||
</Collapsible.Content>
|
||||
</Collapsible>
|
||||
</Card>
|
||||
}
|
||||
>
|
||||
|
||||
@@ -1042,10 +1042,4 @@ export const dict = {
|
||||
// legacy-migration end
|
||||
|
||||
"error.details.show": "التفاصيل",
|
||||
"error.details.type": "النوع",
|
||||
"error.details.statusCode": "رمز الحالة",
|
||||
"error.details.provider": "المزود",
|
||||
"error.details.retryable": "قابل لإعادة المحاولة",
|
||||
"error.details.retries": "المحاولات",
|
||||
"error.details.responseBody": "الاستجابة",
|
||||
}
|
||||
|
||||
@@ -1056,10 +1056,4 @@ export const dict = {
|
||||
// legacy-migration end
|
||||
|
||||
"error.details.show": "Detalhes",
|
||||
"error.details.type": "Tipo",
|
||||
"error.details.statusCode": "Código de status",
|
||||
"error.details.provider": "Provedor",
|
||||
"error.details.retryable": "Repetível",
|
||||
"error.details.retries": "Tentativas",
|
||||
"error.details.responseBody": "Resposta",
|
||||
}
|
||||
|
||||
@@ -1055,10 +1055,4 @@ export const dict = {
|
||||
// legacy-migration end
|
||||
|
||||
"error.details.show": "Detalji",
|
||||
"error.details.type": "Tip",
|
||||
"error.details.statusCode": "Statusni kod",
|
||||
"error.details.provider": "Provajder",
|
||||
"error.details.retryable": "Ponovljivo",
|
||||
"error.details.retries": "Pokušaji",
|
||||
"error.details.responseBody": "Odgovor",
|
||||
}
|
||||
|
||||
@@ -1052,10 +1052,4 @@ export const dict = {
|
||||
// legacy-migration end
|
||||
|
||||
"error.details.show": "Detaljer",
|
||||
"error.details.type": "Type",
|
||||
"error.details.statusCode": "Statuskode",
|
||||
"error.details.provider": "Udbyder",
|
||||
"error.details.retryable": "Kan forsøges igen",
|
||||
"error.details.retries": "Forsøg",
|
||||
"error.details.responseBody": "Svar",
|
||||
}
|
||||
|
||||
@@ -1070,10 +1070,4 @@ export const dict = {
|
||||
// legacy-migration end
|
||||
|
||||
"error.details.show": "Details",
|
||||
"error.details.type": "Typ",
|
||||
"error.details.statusCode": "Statuscode",
|
||||
"error.details.provider": "Anbieter",
|
||||
"error.details.retryable": "Wiederholbar",
|
||||
"error.details.retries": "Versuche",
|
||||
"error.details.responseBody": "Antwort",
|
||||
} satisfies Partial<Record<Keys, string>>
|
||||
|
||||
@@ -1071,12 +1071,5 @@ export const dict = {
|
||||
"migration.complete.done": "Done",
|
||||
// legacy-migration end
|
||||
|
||||
// error details
|
||||
"error.details.show": "Details",
|
||||
"error.details.type": "Type",
|
||||
"error.details.statusCode": "Status code",
|
||||
"error.details.provider": "Provider",
|
||||
"error.details.retryable": "Retryable",
|
||||
"error.details.retries": "Retries",
|
||||
"error.details.responseBody": "Response",
|
||||
}
|
||||
|
||||
@@ -1059,10 +1059,4 @@ export const dict = {
|
||||
// legacy-migration end
|
||||
|
||||
"error.details.show": "Detalles",
|
||||
"error.details.type": "Tipo",
|
||||
"error.details.statusCode": "Código de estado",
|
||||
"error.details.provider": "Proveedor",
|
||||
"error.details.retryable": "Reintentable",
|
||||
"error.details.retries": "Reintentos",
|
||||
"error.details.responseBody": "Respuesta",
|
||||
}
|
||||
|
||||
@@ -1068,10 +1068,4 @@ export const dict = {
|
||||
// legacy-migration end
|
||||
|
||||
"error.details.show": "Détails",
|
||||
"error.details.type": "Type",
|
||||
"error.details.statusCode": "Code de statut",
|
||||
"error.details.provider": "Fournisseur",
|
||||
"error.details.retryable": "Réessayable",
|
||||
"error.details.retries": "Tentatives",
|
||||
"error.details.responseBody": "Réponse",
|
||||
}
|
||||
|
||||
@@ -1050,10 +1050,4 @@ export const dict = {
|
||||
// legacy-migration end
|
||||
|
||||
"error.details.show": "詳細",
|
||||
"error.details.type": "タイプ",
|
||||
"error.details.statusCode": "ステータスコード",
|
||||
"error.details.provider": "プロバイダー",
|
||||
"error.details.retryable": "リトライ可能",
|
||||
"error.details.retries": "リトライ回数",
|
||||
"error.details.responseBody": "レスポンス",
|
||||
}
|
||||
|
||||
@@ -1047,10 +1047,4 @@ export const dict = {
|
||||
// legacy-migration end
|
||||
|
||||
"error.details.show": "상세 정보",
|
||||
"error.details.type": "유형",
|
||||
"error.details.statusCode": "상태 코드",
|
||||
"error.details.provider": "제공자",
|
||||
"error.details.retryable": "재시도 가능",
|
||||
"error.details.retries": "재시도 횟수",
|
||||
"error.details.responseBody": "응답",
|
||||
}
|
||||
|
||||
@@ -1053,10 +1053,4 @@ export const dict = {
|
||||
// legacy-migration end
|
||||
|
||||
"error.details.show": "Szczegóły",
|
||||
"error.details.type": "Typ",
|
||||
"error.details.statusCode": "Kod statusu",
|
||||
"error.details.provider": "Dostawca",
|
||||
"error.details.retryable": "Możliwa ponowna próba",
|
||||
"error.details.retries": "Próby",
|
||||
"error.details.responseBody": "Odpowiedź",
|
||||
}
|
||||
|
||||
@@ -1055,10 +1055,4 @@ export const dict = {
|
||||
// legacy-migration end
|
||||
|
||||
"error.details.show": "Подробности",
|
||||
"error.details.type": "Тип",
|
||||
"error.details.statusCode": "Код статуса",
|
||||
"error.details.provider": "Провайдер",
|
||||
"error.details.retryable": "Повторяемый",
|
||||
"error.details.retries": "Попытки",
|
||||
"error.details.responseBody": "Ответ",
|
||||
}
|
||||
|
||||
@@ -1043,10 +1043,4 @@ export const dict = {
|
||||
// legacy-migration end
|
||||
|
||||
"error.details.show": "รายละเอียด",
|
||||
"error.details.type": "ประเภท",
|
||||
"error.details.statusCode": "รหัสสถานะ",
|
||||
"error.details.provider": "ผู้ให้บริการ",
|
||||
"error.details.retryable": "ลองใหม่ได้",
|
||||
"error.details.retries": "จำนวนครั้งที่ลอง",
|
||||
"error.details.responseBody": "การตอบกลับ",
|
||||
}
|
||||
|
||||
@@ -1035,10 +1035,4 @@ export const dict = {
|
||||
// legacy-migration end
|
||||
|
||||
"error.details.show": "详细信息",
|
||||
"error.details.type": "类型",
|
||||
"error.details.statusCode": "状态码",
|
||||
"error.details.provider": "提供商",
|
||||
"error.details.retryable": "可重试",
|
||||
"error.details.retries": "重试次数",
|
||||
"error.details.responseBody": "响应",
|
||||
} satisfies Partial<Record<Keys, string>>
|
||||
|
||||
@@ -1036,10 +1036,4 @@ export const dict = {
|
||||
// legacy-migration end
|
||||
|
||||
"error.details.show": "詳細資訊",
|
||||
"error.details.type": "類型",
|
||||
"error.details.statusCode": "狀態碼",
|
||||
"error.details.provider": "提供者",
|
||||
"error.details.retryable": "可重試",
|
||||
"error.details.retries": "重試次數",
|
||||
"error.details.responseBody": "回應",
|
||||
} satisfies Partial<Record<Keys, string>>
|
||||
|
||||
Reference in New Issue
Block a user