Fix profile server selection activation state
Build and Deploy Gateway / build-and-push (push) Successful in 20s
Build and Deploy Gateway / deploy (push) Successful in 7s

This commit is contained in:
2026-08-11 10:11:13 +03:00
parent 17849ffd73
commit 7e4da4bdcf
11 changed files with 60 additions and 62 deletions
@@ -199,11 +199,16 @@ export function createSubscriptionService(dependencies: SubscriptionServiceDepen
if (!profile.servers.some((server) => server.id === serverId)) {
throw new HarborError('SERVER_NOT_FOUND');
}
if (profile.desiredServerId === serverId) return mutationResult(profile);
const selected = { ...profile, desiredServerId: serverId };
if (profile.desiredServerId === serverId && state.desiredProfileId === profile.id) {
return mutationResult(profile);
}
const selected = profile.desiredServerId === serverId
? profile
: { ...profile, desiredServerId: serverId };
dependencies.state.update((current) => ({
...current,
profiles: replaceProfile(current, selected),
desiredProfileId: profile.id,
}));
return mutationResult(selected);
});
+3 -3
View File
@@ -1,7 +1,7 @@
export const HARBOR_VERSIONS = Object.freeze({
macClient: '0.23.11',
gatewayClient: '0.24.11',
gatewayBackend: '0.24.0',
macClient: '0.24.0',
gatewayClient: '0.25.0',
gatewayBackend: '0.25.0',
});
export interface ParsedVersion {
-7
View File
@@ -255,13 +255,6 @@ export function App() {
`${profileId}:${serverId}`,
profileId,
)}
onActivateProfile={(profileId: string) => run(
'profileActivate',
() => api.profiles.activate(profileId, revisionRef.current),
'connection',
profileId,
profileId,
)}
onRefreshProfile={(profileId: string) => run(
'profileRefresh',
() => api.profiles.refresh(profileId, revisionRef.current),
+3 -6
View File
@@ -105,7 +105,6 @@ interface ClientOverviewPageProps {
error: UiError | null;
onAddProfile: (label: string, url: string) => Promise<unknown>;
onSelectProfileServer: (profileId: string, serverId: string) => Promise<unknown>;
onActivateProfile: (profileId: string) => Promise<unknown>;
onRefreshProfile: (profileId: string) => Promise<unknown>;
onForgetProfile: (profileId: string, mode: 'delete' | 'stop-and-delete') => Promise<unknown>;
onApply: (profileId: string, serverId: string) => Promise<unknown>;
@@ -411,7 +410,6 @@ export function ClientOverviewPage({
error,
onAddProfile,
onSelectProfileServer,
onActivateProfile,
onRefreshProfile,
onForgetProfile,
onApply,
@@ -494,7 +492,6 @@ export function ClientOverviewPage({
onAdd: onAddProfile,
onRefresh: onRefreshProfile,
onForget: onForgetProfile,
onActivate: onActivateProfile,
onDismissError,
});
const routingFeature = useRoutingFeature({
@@ -553,7 +550,7 @@ export function ClientOverviewPage({
}, []);
function selectServer(profile: ProfileSnapshot, serverId: string) {
if (connected && !gatewayDirect && state.selection.appliedProfileId === profile.id) {
if (connected && !gatewayDirect) {
onApply(profile.id, serverId);
return;
}
@@ -723,9 +720,9 @@ export function ClientOverviewPage({
profileId={profile.id}
pingServers={actions.pingServers}
servers={profile.servers}
selectedServerId={profile.desiredServerId}
selectedServerId={pickerState.selectedServerId}
disabled={serverApplyBlocked || pickerState.disabled}
prompt={!profile.desiredServerId}
prompt={!pickerState.selectedServerId}
leaving={pickerState.leaving}
revealVersion={pickerState.revealVersion}
anchorServerId={pickerState.anchorServerId}
@@ -32,6 +32,7 @@ interface ServerPickerRenderState {
leaving: boolean;
revealVersion: number;
anchorServerId: string;
selectedServerId: string;
}
interface SubscriptionFeatureOptions {
@@ -45,7 +46,6 @@ interface SubscriptionFeatureOptions {
onAdd: (label: string, url: string) => Promise<unknown>;
onRefresh: (profileId: string) => Promise<unknown>;
onForget: (profileId: string, mode: 'delete' | 'stop-and-delete') => Promise<unknown>;
onActivate: (profileId: string) => Promise<unknown>;
onDismissError: () => void;
}
@@ -64,7 +64,6 @@ export function useSubscriptionFeature({
onAdd,
onRefresh,
onForget,
onActivate,
onDismissError,
}: SubscriptionFeatureOptions) {
const [open, setOpen] = useState(false);
@@ -281,7 +280,6 @@ export function useSubscriptionFeature({
addBlocked: operationBlocked(operations, 'profileAdd'),
refreshBlocked: operationBlocked(operations, 'profileRefresh'),
deleteBlocked: operationBlocked(operations, 'profileDelete'),
activateBlocked: operationBlocked(operations, 'profileActivate'),
toggle: () => setOpen((current) => !current),
close: () => setOpen(false),
showAdd,
@@ -296,7 +294,6 @@ export function useSubscriptionFeature({
cancelDelete: () => setDeleteId(''),
confirmDelete,
refresh,
onActivate,
};
}
@@ -394,8 +391,9 @@ function ProfileGroup({
const refreshing = feature.refreshingIds.includes(profile.id)
|| feature.operations.profileRefresh?.target === profile.id;
const controlsBlocked = operationBlocked(feature.operations, 'profileSelect');
const selectedServer = profileServer(profile, profile.desiredServerId);
const visibleServerId = applied ? feature.selection.appliedServerId : profile.desiredServerId;
const visibleServerId = applied
? feature.selection.appliedServerId
: desired ? profile.desiredServerId : '';
const visibleServer = profileServer(profile, visibleServerId)
|| (applied && feature.selection.appliedServerSnapshot?.id === visibleServerId
? feature.selection.appliedServerSnapshot
@@ -403,7 +401,6 @@ function ProfileGroup({
const visibleServerLocation = [visibleServer?.city, visibleServer?.country]
.filter((value): value is string => typeof value === 'string' && Boolean(value))
.join(' · ');
const canActivate = !applied && feature.selection.desiredProfileId !== profile.id;
const localStatus = profileError?.message || (profile.subscription.status === 'stale'
? `Последнее обновление не удалось. Данные от ${profile.subscription.fetchedAt
? new Date(profile.subscription.fetchedAt).toLocaleString('ru-RU', { dateStyle: 'short', timeStyle: 'short' })
@@ -431,12 +428,7 @@ function ProfileGroup({
<small>{profileDetails}</small>
</span>
</button>
{canActivate ? <button
className="client-profile-activate"
type="button"
disabled={feature.activateBlocked || !selectedServer}
onClick={() => feature.onActivate(profile.id)}
>Сделать активной</button> : <ProfileUsage profile={profile} />}
{(applied || desired) && <ProfileUsage profile={profile} />}
<button
className={`client-profile-refresh${refreshing ? ' is-refreshing' : ''}`}
type="button"
@@ -483,12 +475,13 @@ function ProfileGroup({
inert={!expanded ? true : undefined}
>
<div className="client-profile-body-inner">
{!profile.desiredServerId && <p className="client-profile-server-hint">Выберите сервер этой подписки</p>}
{!visibleServerId && <p className="client-profile-server-hint">Выберите сервер этой подписки</p>}
{renderServerPicker(profile, {
disabled: controlsBlocked,
leaving: false,
revealVersion: feature.revealVersions[profile.id] || 0,
anchorServerId: visibleServer?.id || '',
selectedServerId: visibleServerId,
})}
</div>
</div>}
+10 -13
View File
@@ -149,6 +149,8 @@
}
.client-profile-usage {
grid-column: 2;
justify-self: end;
max-width: 126px;
overflow: hidden;
color: var(--client-muted);
@@ -188,6 +190,14 @@
transition: transform 600ms cubic-bezier(0.16, 1, 0.3, 1);
}
.client-profile-refresh {
grid-column: 3;
}
.client-profile-delete {
grid-column: 4;
}
.client-profile-delete-lid {
transform-origin: center 7px;
transition: transform 260ms cubic-bezier(0.16, 1, 0.3, 1);
@@ -327,7 +337,6 @@
outline-offset: -2px;
}
.client-profile-activate,
.client-profile-add-trigger {
min-height: 36px;
padding: 0;
@@ -365,18 +374,6 @@
}
}
.client-profile-activate {
font: var(--type-micro);
letter-spacing: var(--type-micro-tracking);
text-transform: var(--type-micro-transform);
white-space: nowrap;
}
.client-profile-activate:disabled {
opacity: 0.35;
cursor: not-allowed;
}
.client-profile-add-trigger {
width: 100%;
margin-top: 10px;