fix: correct dashboard displaying

This commit is contained in:
divocat
2025-10-07 23:33:57 +03:00
parent a45ab62885
commit 9a46d731c9
7 changed files with 117 additions and 41 deletions

View File

@@ -8,3 +8,4 @@ export * from './getProxyUrlName';
export * from './onMount'; export * from './onMount';
export * from './getClashApiUrl'; export * from './getClashApiUrl';
export * from './splitProxyString'; export * from './splitProxyString';
export * from './preserveScrollForPage';

View File

@@ -0,0 +1,9 @@
export function preserveScrollForPage(renderFn: () => void) {
const scrollY = window.scrollY;
renderFn();
requestAnimationFrame(() => {
window.scrollTo({ top: scrollY });
});
}

View File

@@ -62,9 +62,10 @@ export async function getDashboardSections(): Promise<IGetDashboardSectionsRespo
); );
const parsedOutbound = JSON.parse(section.outbound_json); const parsedOutbound = JSON.parse(section.outbound_json);
const parsedTag = parsedOutbound?.tag ? decodeURIComponent(parsedOutbound?.tag) : undefined; const parsedTag = parsedOutbound?.tag
const proxyDisplayName = parsedTag || outbound?.value?.name || '' ? decodeURIComponent(parsedOutbound?.tag)
: undefined;
const proxyDisplayName = parsedTag || outbound?.value?.name || '';
return { return {
withTagSelect: false, withTagSelect: false,

View File

@@ -3,7 +3,11 @@ import {
getPodkopStatus, getPodkopStatus,
getSingboxStatus, getSingboxStatus,
} from '../../methods'; } from '../../methods';
import { getClashWsUrl, onMount } from '../../../helpers'; import {
getClashWsUrl,
onMount,
preserveScrollForPage,
} from '../../../helpers';
import { import {
triggerLatencyGroupTest, triggerLatencyGroupTest,
triggerLatencyProxyTest, triggerLatencyProxyTest,
@@ -31,6 +35,7 @@ async function fetchDashboardSections() {
store.set({ store.set({
sectionsWidget: { sectionsWidget: {
latencyFetching: false,
loading: false, loading: false,
failed: !success, failed: !success,
data, data,
@@ -130,25 +135,41 @@ async function handleChooseOutbound(selector: string, tag: string) {
} }
async function handleTestGroupLatency(tag: string) { async function handleTestGroupLatency(tag: string) {
store.set({
sectionsWidget: {
...store.get().sectionsWidget,
latencyFetching: true,
},
});
await triggerLatencyGroupTest(tag); await triggerLatencyGroupTest(tag);
await fetchDashboardSections(); await fetchDashboardSections();
store.set({
sectionsWidget: {
...store.get().sectionsWidget,
latencyFetching: false,
},
});
} }
async function handleTestProxyLatency(tag: string) { async function handleTestProxyLatency(tag: string) {
store.set({
sectionsWidget: {
...store.get().sectionsWidget,
latencyFetching: true,
},
});
await triggerLatencyProxyTest(tag); await triggerLatencyProxyTest(tag);
await fetchDashboardSections(); await fetchDashboardSections();
}
function replaceTestLatencyButtonsWithSkeleton() { store.set({
document sectionsWidget: {
.querySelectorAll('.dashboard-sections-grid-item-test-latency') ...store.get().sectionsWidget,
.forEach((el) => { latencyFetching: false,
const newDiv = document.createElement('div'); },
newDiv.className = 'skeleton'; });
newDiv.style.width = '99px';
newDiv.style.height = '28px';
el.replaceWith(newDiv);
});
} }
// Renderer // Renderer
@@ -170,8 +191,12 @@ async function renderSectionsWidget() {
}, },
onTestLatency: () => {}, onTestLatency: () => {},
onChooseOutbound: () => {}, onChooseOutbound: () => {},
latencyFetching: sectionsWidget.latencyFetching,
});
return preserveScrollForPage(() => {
container!.replaceChildren(renderedWidget);
}); });
return container!.replaceChildren(renderedWidget);
} }
const renderedWidgets = sectionsWidget.data.map((section) => const renderedWidgets = sectionsWidget.data.map((section) =>
@@ -179,9 +204,8 @@ async function renderSectionsWidget() {
loading: sectionsWidget.loading, loading: sectionsWidget.loading,
failed: sectionsWidget.failed, failed: sectionsWidget.failed,
section, section,
latencyFetching: sectionsWidget.latencyFetching,
onTestLatency: (tag) => { onTestLatency: (tag) => {
replaceTestLatencyButtonsWithSkeleton();
if (section.withTagSelect) { if (section.withTagSelect) {
return handleTestGroupLatency(tag); return handleTestGroupLatency(tag);
} }
@@ -194,7 +218,9 @@ async function renderSectionsWidget() {
}), }),
); );
return container!.replaceChildren(...renderedWidgets); return preserveScrollForPage(() => {
container!.replaceChildren(...renderedWidgets);
});
} }
async function renderBandwidthWidget() { async function renderBandwidthWidget() {

View File

@@ -6,6 +6,7 @@ interface IRenderSectionsProps {
section: Podkop.OutboundGroup; section: Podkop.OutboundGroup;
onTestLatency: (tag: string) => void; onTestLatency: (tag: string) => void;
onChooseOutbound: (selector: string, tag: string) => void; onChooseOutbound: (selector: string, tag: string) => void;
latencyFetching: boolean;
} }
function renderFailedState() { function renderFailedState() {
@@ -31,6 +32,7 @@ export function renderDefaultState({
section, section,
onChooseOutbound, onChooseOutbound,
onTestLatency, onTestLatency,
latencyFetching,
}: IRenderSectionsProps) { }: IRenderSectionsProps) {
function testLatency() { function testLatency() {
if (section.withTagSelect) { if (section.withTagSelect) {
@@ -95,14 +97,16 @@ export function renderDefaultState({
}, },
section.displayName, section.displayName,
), ),
E( latencyFetching
'button', ? E('div', { class: 'skeleton', style: 'width: 99px; height: 28px' })
{ : E(
class: 'btn dashboard-sections-grid-item-test-latency', 'button',
click: () => testLatency(), {
}, class: 'btn dashboard-sections-grid-item-test-latency',
_('Test latency'), click: () => testLatency(),
), },
_('Test latency'),
),
]), ]),
E( E(
'div', 'div',

View File

@@ -141,6 +141,7 @@ export interface StoreType {
loading: boolean; loading: boolean;
failed: boolean; failed: boolean;
data: Podkop.OutboundGroup[]; data: Podkop.OutboundGroup[];
latencyFetching: boolean;
}; };
} }
@@ -172,6 +173,7 @@ const initialStore: StoreType = {
sectionsWidget: { sectionsWidget: {
loading: true, loading: true,
failed: false, failed: false,
latencyFetching: false,
data: [], data: [],
}, },
}; };

View File

@@ -776,6 +776,15 @@ function splitProxyString(str) {
return str.split("\n").map((line) => line.trim()).filter((line) => !line.startsWith("//")).filter(Boolean); return str.split("\n").map((line) => line.trim()).filter((line) => !line.startsWith("//")).filter(Boolean);
} }
// src/helpers/preserveScrollForPage.ts
function preserveScrollForPage(renderFn) {
const scrollY = window.scrollY;
renderFn();
requestAnimationFrame(() => {
window.scrollTo({ top: scrollY });
});
}
// src/clash/methods/createBaseApiRequest.ts // src/clash/methods/createBaseApiRequest.ts
async function createBaseApiRequest(fetchFn) { async function createBaseApiRequest(fetchFn) {
try { try {
@@ -1210,6 +1219,7 @@ var initialStore = {
sectionsWidget: { sectionsWidget: {
loading: true, loading: true,
failed: false, failed: false,
latencyFetching: false,
data: [] data: []
} }
}; };
@@ -1248,7 +1258,8 @@ function renderLoadingState() {
function renderDefaultState({ function renderDefaultState({
section, section,
onChooseOutbound, onChooseOutbound,
onTestLatency onTestLatency,
latencyFetching
}) { }) {
function testLatency() { function testLatency() {
if (section.withTagSelect) { if (section.withTagSelect) {
@@ -1304,7 +1315,7 @@ function renderDefaultState({
}, },
section.displayName section.displayName
), ),
E( latencyFetching ? E("div", { class: "skeleton", style: "width: 99px; height: 28px" }) : E(
"button", "button",
{ {
class: "btn dashboard-sections-grid-item-test-latency", class: "btn dashboard-sections-grid-item-test-latency",
@@ -1573,6 +1584,7 @@ async function fetchDashboardSections() {
const { data, success } = await getDashboardSections(); const { data, success } = await getDashboardSections();
store.set({ store.set({
sectionsWidget: { sectionsWidget: {
latencyFetching: false,
loading: false, loading: false,
failed: !success, failed: !success,
data data
@@ -1662,20 +1674,35 @@ async function handleChooseOutbound(selector, tag) {
await fetchDashboardSections(); await fetchDashboardSections();
} }
async function handleTestGroupLatency(tag) { async function handleTestGroupLatency(tag) {
store.set({
sectionsWidget: {
...store.get().sectionsWidget,
latencyFetching: true
}
});
await triggerLatencyGroupTest(tag); await triggerLatencyGroupTest(tag);
await fetchDashboardSections(); await fetchDashboardSections();
store.set({
sectionsWidget: {
...store.get().sectionsWidget,
latencyFetching: false
}
});
} }
async function handleTestProxyLatency(tag) { async function handleTestProxyLatency(tag) {
store.set({
sectionsWidget: {
...store.get().sectionsWidget,
latencyFetching: true
}
});
await triggerLatencyProxyTest(tag); await triggerLatencyProxyTest(tag);
await fetchDashboardSections(); await fetchDashboardSections();
} store.set({
function replaceTestLatencyButtonsWithSkeleton() { sectionsWidget: {
document.querySelectorAll(".dashboard-sections-grid-item-test-latency").forEach((el) => { ...store.get().sectionsWidget,
const newDiv = document.createElement("div"); latencyFetching: false
newDiv.className = "skeleton"; }
newDiv.style.width = "99px";
newDiv.style.height = "28px";
el.replaceWith(newDiv);
}); });
} }
async function renderSectionsWidget() { async function renderSectionsWidget() {
@@ -1695,17 +1722,20 @@ async function renderSectionsWidget() {
onTestLatency: () => { onTestLatency: () => {
}, },
onChooseOutbound: () => { onChooseOutbound: () => {
} },
latencyFetching: sectionsWidget.latencyFetching
});
return preserveScrollForPage(() => {
container.replaceChildren(renderedWidget);
}); });
return container.replaceChildren(renderedWidget);
} }
const renderedWidgets = sectionsWidget.data.map( const renderedWidgets = sectionsWidget.data.map(
(section) => renderSections({ (section) => renderSections({
loading: sectionsWidget.loading, loading: sectionsWidget.loading,
failed: sectionsWidget.failed, failed: sectionsWidget.failed,
section, section,
latencyFetching: sectionsWidget.latencyFetching,
onTestLatency: (tag) => { onTestLatency: (tag) => {
replaceTestLatencyButtonsWithSkeleton();
if (section.withTagSelect) { if (section.withTagSelect) {
return handleTestGroupLatency(tag); return handleTestGroupLatency(tag);
} }
@@ -1716,7 +1746,9 @@ async function renderSectionsWidget() {
} }
}) })
); );
return container.replaceChildren(...renderedWidgets); return preserveScrollForPage(() => {
container.replaceChildren(...renderedWidgets);
});
} }
async function renderBandwidthWidget() { async function renderBandwidthWidget() {
console.log("renderBandwidthWidget"); console.log("renderBandwidthWidget");
@@ -1906,6 +1938,7 @@ return baseclass.extend({
maskIP, maskIP,
onMount, onMount,
parseValueList, parseValueList,
preserveScrollForPage,
renderDashboard, renderDashboard,
splitProxyString, splitProxyString,
triggerLatencyGroupTest, triggerLatencyGroupTest,