mirror of
https://github.com/JetSprow/J-Board-Lite.git
synced 2026-05-01 01:14:10 +05:30
feat: split network recommendation toggles
This commit is contained in:
@@ -30,7 +30,14 @@ export const metadata: Metadata = {
|
||||
|
||||
export default async function StorePage() {
|
||||
const session = await getActiveSession();
|
||||
const { plans, availabilityMap, pendingOrder, networkInsightsEnabled, latencyRecommendations } = await getStorePageData(session?.user.id);
|
||||
const {
|
||||
plans,
|
||||
availabilityMap,
|
||||
pendingOrder,
|
||||
networkRecommendationsEnabled,
|
||||
networkInsightsEnabled,
|
||||
latencyRecommendations,
|
||||
} = await getStorePageData(session?.user.id);
|
||||
const proxyPlans = getProxyPlans(plans);
|
||||
const streamingPlans = getStreamingPlans(plans);
|
||||
const proxyCards = sortPlansForDisplay(proxyPlans.map((plan) => toProxyPlanCard(plan, availabilityMap.get(plan.id))));
|
||||
@@ -77,7 +84,7 @@ export default async function StorePage() {
|
||||
|
||||
<PendingOrderBanner order={pendingOrder} />
|
||||
|
||||
{networkInsightsEnabled && proxyCards.length > 0 && (
|
||||
{networkRecommendationsEnabled && proxyCards.length > 0 && (
|
||||
<StoreLatencyRecommendations initialItems={latencyRecommendations} />
|
||||
)}
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Gauge, Network, Search, Sparkles } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { StorePlanHeader } from "./plan-card-parts";
|
||||
import { PlanAvailabilityBadges } from "./plan-availability-badges";
|
||||
import { ProxyDetailDialog } from "./proxy-detail-dialog";
|
||||
import { OPEN_PROXY_PLAN_EVENT } from "./store-latency-recommendations";
|
||||
import type { ProxyPlan } from "./proxy-plan-types";
|
||||
|
||||
interface Props {
|
||||
@@ -19,6 +20,18 @@ export function ProxyPlanCard({ plan, networkInsightsEnabled }: Props) {
|
||||
const isFixedPackage = plan.pricingMode === "FIXED_PACKAGE";
|
||||
const displayPrice = isFixedPackage ? (plan.fixedPrice ?? 0) : plan.pricePerGb;
|
||||
|
||||
useEffect(() => {
|
||||
function handleOpen(event: Event) {
|
||||
if (!(event instanceof CustomEvent)) return;
|
||||
if (event.detail?.planId === plan.id) {
|
||||
setDialogOpen(true);
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener(OPEN_PROXY_PLAN_EVENT, handleOpen);
|
||||
return () => window.removeEventListener(OPEN_PROXY_PLAN_EVENT, handleOpen);
|
||||
}, [plan.id]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<article
|
||||
|
||||
@@ -28,7 +28,7 @@ export async function getStorePageData(userId?: string) {
|
||||
})
|
||||
: null,
|
||||
]);
|
||||
const latencyRecommendations = config.networkInsightsEnabled
|
||||
const latencyRecommendations = config.networkRecommendationsEnabled
|
||||
? await getLatencyRecommendations()
|
||||
: [];
|
||||
|
||||
@@ -43,6 +43,7 @@ export async function getStorePageData(userId?: string) {
|
||||
return {
|
||||
plans,
|
||||
availabilityMap,
|
||||
networkRecommendationsEnabled: config.networkRecommendationsEnabled,
|
||||
networkInsightsEnabled: config.networkInsightsEnabled,
|
||||
latencyRecommendations,
|
||||
pendingOrder: pendingOrder
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Activity, Clock3, RadioTower, RefreshCw, Sparkles } from "lucide-react";
|
||||
import { fetchJson } from "@/lib/fetch-json";
|
||||
@@ -18,6 +17,7 @@ interface RecommendationPayload {
|
||||
}
|
||||
|
||||
const REFRESH_INTERVAL_MS = 5 * 60 * 1000;
|
||||
export const OPEN_PROXY_PLAN_EVENT = "jboard:open-proxy-plan";
|
||||
|
||||
function formatTime(value: string | null) {
|
||||
if (!value) return "等待刷新";
|
||||
@@ -34,6 +34,14 @@ function getLatencyTone(latencyMs?: number) {
|
||||
return "border-amber-500/20 bg-amber-500/10 text-amber-700 dark:text-amber-300";
|
||||
}
|
||||
|
||||
function openRecommendedPlan(planId: string) {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent(OPEN_PROXY_PLAN_EVENT, {
|
||||
detail: { planId },
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export function StoreLatencyRecommendations({
|
||||
initialItems,
|
||||
}: {
|
||||
@@ -96,10 +104,16 @@ export function StoreLatencyRecommendations({
|
||||
{RECOMMENDATION_CARRIERS.map((carrier) => {
|
||||
const item = itemMap.get(carrier);
|
||||
return (
|
||||
<div
|
||||
<button
|
||||
key={carrier}
|
||||
type="button"
|
||||
disabled={!item}
|
||||
onClick={() => {
|
||||
if (item) openRecommendedPlan(item.planId);
|
||||
}}
|
||||
className={cn(
|
||||
"rounded-xl border p-4 transition-colors duration-200",
|
||||
"rounded-xl border p-4 text-left transition-colors duration-200 focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20 disabled:cursor-default",
|
||||
item && "hover:border-primary/25 hover:bg-primary/7",
|
||||
getLatencyTone(item?.latencyMs),
|
||||
)}
|
||||
>
|
||||
@@ -120,17 +134,16 @@ export function StoreLatencyRecommendations({
|
||||
<p className="text-lg font-semibold tracking-[-0.04em] text-foreground">{item.nodeName}</p>
|
||||
<p className="mt-1 truncate text-xs text-muted-foreground">{item.planName}</p>
|
||||
</div>
|
||||
<Link
|
||||
href={`#plan-${item.planId}`}
|
||||
<span
|
||||
className="inline-flex items-center gap-1.5 text-xs font-semibold text-primary hover:underline"
|
||||
>
|
||||
<Activity className="size-3.5" /> 查看套餐
|
||||
</Link>
|
||||
<Activity className="size-3.5" /> 查看详情与购买
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<p className="mt-4 text-sm leading-6 text-muted-foreground">正在采集这个运营商的延迟数据。</p>
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user