feat(downloads): one-click Share + downloadable-by-default links (E7 S3)
D2-A: the library row's Share button becomes a single action — quickShareLink() ensures a public link (minting on demand), copies it to the clipboard, and toasts with an "Open in new tab" action. A copy failure still toasts (warning) with that action so the link is never a dead end. Advanced controls (password/expiry/revoke/ user-ACL) stay in the existing ShareDialog, reached via a new "Manage sharing" row button. D1 (forward-only): CreateLinkIn.allow_download defaults to true, so a newly minted link is downloadable by default. The default lives in the API, not a migration — the download_links column default stays false, so no existing link flips. The ShareDialog create-default is flipped to match. Tests: TestCreateLinkDefault (default true; explicit false honored); 4 vitest cases for quickShareLink (mint-vs-reuse, copy ok/fail, create failure). HU/EN parity.
This commit is contained in:
@@ -823,7 +823,12 @@ def list_links(
|
||||
|
||||
class CreateLinkIn(BaseModel):
|
||||
password: str = ""
|
||||
allow_download: bool = False
|
||||
# D1 (E7 S3): a newly shared download is downloadable by default — the common intent is "let the
|
||||
# recipient have the file", not stream-only. Forward-only: this only sets the default for links
|
||||
# minted from now on; existing links keep whatever `allow_download` they were created with (the
|
||||
# column default stays False, so no back-catalogue link silently flips). The owner can still
|
||||
# uncheck it in the Share dialog, and toggle any link stream-only afterwards.
|
||||
allow_download: bool = True
|
||||
expires_days: int | None = None
|
||||
|
||||
|
||||
|
||||
@@ -93,6 +93,24 @@ class TestPublicMetaAudioFlag:
|
||||
assert meta["audio"] is False
|
||||
|
||||
|
||||
class TestCreateLinkDefault:
|
||||
"""D1 (E7 S3): a newly minted share link is downloadable by default. The one-click Share relies on
|
||||
this — it POSTs no `allow_download`, so the API default decides. Forward-only lives here (the API
|
||||
default), NOT in a migration: the DownloadLink column default stays False, so no existing back-
|
||||
catalogue link silently flips to downloadable."""
|
||||
|
||||
def test_omitted_allow_download_defaults_downloadable(self):
|
||||
from app.routes.downloads import CreateLinkIn
|
||||
|
||||
assert CreateLinkIn().allow_download is True
|
||||
|
||||
def test_explicit_false_is_respected(self):
|
||||
# The owner can still uncheck it to mint a stream-only link.
|
||||
from app.routes.downloads import CreateLinkIn
|
||||
|
||||
assert CreateLinkIn(allow_download=False).allow_download is False
|
||||
|
||||
|
||||
class TestIsExpired:
|
||||
def test_none_expiry_never_expires(self):
|
||||
assert is_expired(SimpleNamespace(expires_at=None)) is False
|
||||
|
||||
Reference in New Issue
Block a user