fix(downloads): harden duplicate resolution and row controls

This commit is contained in:
NimBold
2026-07-29 18:28:46 +03:30
parent 546be23c91
commit 29bbb009b5
17 changed files with 248 additions and 89 deletions
+9
View File
@@ -4,6 +4,7 @@ import {
commonMediaFormatsForRows,
canSubmitMetadataRows,
commonMediaQualitiesForRows,
durableDownloadUrl,
mediaFormatSelectorForRow,
mediaFileNameForSelectedFormat,
mediaFormatForFormat,
@@ -35,6 +36,14 @@ const row = (
});
describe('add download metadata workflow', () => {
it('keeps the stable source URL instead of persisting a resolved redirect', () => {
const sourceUrl = 'https://github.com/example/project/releases/download/v1/file.zip';
const signedRedirect = 'https://release-assets.githubusercontent.com/github-production-release-asset/asset?se=2099-01-01T00%3A00%3A00Z&sig=redacted';
expect(durableDownloadUrl(sourceUrl)).toBe(sourceUrl);
expect(durableDownloadUrl(sourceUrl)).not.toBe(signedRedirect);
});
it('preserves rows by normalized source URL and creates only new rows', () => {
const existing = row({ file: 'server-name.zip' });
let nextId = 0;
+7
View File
@@ -54,6 +54,13 @@ export interface AddDownloadDraftRow {
selected?: boolean;
}
/**
* Redirect targets are request-time details, not durable download identity.
* Providers such as GitHub sign those targets with short-lived credentials,
* so retries must start from the source URL and resolve a fresh target.
*/
export const durableDownloadUrl = (sourceUrl: string): string => sourceUrl.trim();
const ALLOWED_SCHEMES = new Set(['http:', 'https:', 'ftp:', 'sftp:']);
type ParsedInput = {
+12 -1
View File
@@ -73,7 +73,18 @@ describe('download table column preferences', () => {
800
)).toMatchObject({
right: 8,
height: 30,
height: 28,
visibility: 'visible',
});
expect(getDownloadActionPosition(
{ top: 40, right: 400, bottom: 72, left: 100 },
viewport,
viewport,
800,
16
)).toMatchObject({
right: 416,
visibility: 'visible',
});
+8 -5
View File
@@ -14,11 +14,10 @@ export const DEFAULT_COLUMN_ORDER = [
export const DEFAULT_COLUMN_WIDTHS = [340, 100, 220, 100, 80, 170] as const;
export const COLUMN_MINIMUMS = [160, 58, 92, 58, 48, 144] as const;
// Width of the compact action rail shown while hovering or focusing a row.
export const DOWNLOAD_ACTIONS_COLUMN_WIDTH = 64;
// Keep the viewport-anchored rail clear of the window edge and scrollbar.
export const DOWNLOAD_ACTIONS_VIEWPORT_INSET = 8;
export const DOWNLOAD_ACTIONS_MAX_HEIGHT = 30;
// Match the 28px master-control group height.
export const DOWNLOAD_ACTIONS_MAX_HEIGHT = 28;
export const DOWNLOAD_ACTIONS_VIEWPORT_TOLERANCE = 1;
export interface DownloadActionViewportRect {
@@ -40,7 +39,8 @@ export const getDownloadActionPosition = (
rowRect: DownloadActionViewportRect,
horizontalViewportRect: DownloadActionViewportRect,
verticalViewportRect: DownloadActionViewportRect,
windowWidth: number
windowWidth: number,
actionInset = DOWNLOAD_ACTIONS_VIEWPORT_INSET
): DownloadActionPosition => {
const visibleTop = Math.max(rowRect.top, verticalViewportRect.top);
const visibleBottom = Math.min(rowRect.bottom, verticalViewportRect.bottom);
@@ -52,10 +52,13 @@ export const getDownloadActionPosition = (
visibleWidth > DOWNLOAD_ACTIONS_VIEWPORT_TOLERANCE;
const actionHeight = Math.min(DOWNLOAD_ACTIONS_MAX_HEIGHT, rowRect.bottom - rowRect.top, visibleHeight);
const actionRightEdge = Math.min(rowRect.right, horizontalViewportRect.right);
const safeActionInset = Number.isFinite(actionInset)
? Math.max(0, actionInset)
: DOWNLOAD_ACTIONS_VIEWPORT_INSET;
return {
top: visibleTop + Math.max(0, (visibleHeight - actionHeight) / 2),
right: Math.max(0, windowWidth - actionRightEdge) + DOWNLOAD_ACTIONS_VIEWPORT_INSET,
right: Math.max(0, windowWidth - actionRightEdge) + safeActionInset,
height: actionHeight,
overflow: actionHeight < rowRect.bottom - rowRect.top ? 'hidden' : 'visible',
visibility: isVisible ? 'visible' : 'hidden',