forked from boos/BoosAPI
fix: address code review findings
- fix(api): use effectiveTokenId in route channel insert to match duplicate detection logic - fix(css): rename keyframes to kebab-case (filter-bar-slide-down/up) - fix(web): wrap "保存中..." text with tr() in ManualRoutePanel - fix(web): use ref for fresh cache check and add error handling in useRouteChannels
This commit is contained in:
@@ -539,7 +539,7 @@ export async function tokensRoutes(app: FastifyInstance) {
|
|||||||
await db.insert(schema.routeChannels).values({
|
await db.insert(schema.routeChannels).values({
|
||||||
routeId,
|
routeId,
|
||||||
accountId: item.accountId,
|
accountId: item.accountId,
|
||||||
tokenId: item.tokenId,
|
tokenId: effectiveTokenId,
|
||||||
sourceModel: sourceModel || null,
|
sourceModel: sourceModel || null,
|
||||||
priority: 0,
|
priority: 0,
|
||||||
weight: 10,
|
weight: 10,
|
||||||
|
|||||||
+4
-4
@@ -4101,19 +4101,19 @@ textarea {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
animation: filterBarSlideDown 0.22s cubic-bezier(0.22, 1, 0.36, 1);
|
animation: filter-bar-slide-down 0.22s cubic-bezier(0.22, 1, 0.36, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.route-filter-bar-expanded.is-closing {
|
.route-filter-bar-expanded.is-closing {
|
||||||
animation: filterBarSlideUp 0.18s ease forwards;
|
animation: filter-bar-slide-up 0.18s ease forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes filterBarSlideDown {
|
@keyframes filter-bar-slide-down {
|
||||||
from { opacity: 0; max-height: 0; transform: translateY(-4px); }
|
from { opacity: 0; max-height: 0; transform: translateY(-4px); }
|
||||||
to { opacity: 1; max-height: 600px; transform: translateY(0); }
|
to { opacity: 1; max-height: 600px; transform: translateY(0); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes filterBarSlideUp {
|
@keyframes filter-bar-slide-up {
|
||||||
from { opacity: 1; max-height: 600px; }
|
from { opacity: 1; max-height: 600px; }
|
||||||
to { opacity: 0; max-height: 0; overflow: hidden; }
|
to { opacity: 0; max-height: 0; overflow: hidden; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ export default function ManualRoutePanel({
|
|||||||
className="spinner spinner-sm"
|
className="spinner spinner-sm"
|
||||||
style={{ borderTopColor: 'white', borderColor: 'rgba(255,255,255,0.3)' }}
|
style={{ borderTopColor: 'white', borderColor: 'rgba(255,255,255,0.3)' }}
|
||||||
/>{' '}
|
/>{' '}
|
||||||
保存中...
|
{tr('保存中...')}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
tr(editingRouteId ? '保存群组' : '创建群组')
|
tr(editingRouteId ? '保存群组' : '创建群组')
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback, useRef } from 'react';
|
||||||
import { api } from '../../api.js';
|
import { api } from '../../api.js';
|
||||||
import { normalizeChannels } from './utils.js';
|
import { normalizeChannels } from './utils.js';
|
||||||
import type { RouteChannel } from './types.js';
|
import type { RouteChannel } from './types.js';
|
||||||
@@ -6,19 +6,24 @@ import type { RouteChannel } from './types.js';
|
|||||||
export function useRouteChannels() {
|
export function useRouteChannels() {
|
||||||
const [channelsByRouteId, setChannelsByRouteId] = useState<Record<number, RouteChannel[]>>({});
|
const [channelsByRouteId, setChannelsByRouteId] = useState<Record<number, RouteChannel[]>>({});
|
||||||
const [loadingChannelsByRouteId, setLoadingChannelsByRouteId] = useState<Record<number, boolean>>({});
|
const [loadingChannelsByRouteId, setLoadingChannelsByRouteId] = useState<Record<number, boolean>>({});
|
||||||
|
const channelsByRouteIdRef = useRef(channelsByRouteId);
|
||||||
|
channelsByRouteIdRef.current = channelsByRouteId;
|
||||||
|
|
||||||
const loadChannels = useCallback(async (routeId: number, force = false) => {
|
const loadChannels = useCallback(async (routeId: number, force = false) => {
|
||||||
if (!force && channelsByRouteId[routeId]) return channelsByRouteId[routeId];
|
if (!force && channelsByRouteIdRef.current[routeId]) return channelsByRouteIdRef.current[routeId];
|
||||||
setLoadingChannelsByRouteId((prev) => ({ ...prev, [routeId]: true }));
|
setLoadingChannelsByRouteId((prev) => ({ ...prev, [routeId]: true }));
|
||||||
try {
|
try {
|
||||||
const channels = await api.getRouteChannels(routeId);
|
const channels = await api.getRouteChannels(routeId);
|
||||||
const sorted = normalizeChannels(channels || []);
|
const sorted = normalizeChannels(channels || []);
|
||||||
setChannelsByRouteId((prev) => ({ ...prev, [routeId]: sorted }));
|
setChannelsByRouteId((prev) => ({ ...prev, [routeId]: sorted }));
|
||||||
return sorted;
|
return sorted;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to load channels for route ${routeId}:`, error);
|
||||||
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
setLoadingChannelsByRouteId((prev) => ({ ...prev, [routeId]: false }));
|
setLoadingChannelsByRouteId((prev) => ({ ...prev, [routeId]: false }));
|
||||||
}
|
}
|
||||||
}, [channelsByRouteId]);
|
}, []);
|
||||||
|
|
||||||
const invalidateChannels = useCallback((routeId: number) => {
|
const invalidateChannels = useCallback((routeId: number) => {
|
||||||
setChannelsByRouteId((prev) => {
|
setChannelsByRouteId((prev) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user