feat(files): the Files page — the library in one aligned list, its folders in a rail
Files is the one library: every done file, the ones shared with me, or one folder's, picked in a side rail (sources, own folders, the storage meter) and mirrored to ?folder=. One table with every kind of value in its own column; Copy link, Share and Save in sight, every action on a right click: play, cut, rename in the row, move to an own folder or back home, keep, details, re-download, delete. The Downloads page keeps only the queue until the tray replaces it. The old /api/downloads library routes are gone: list shared, upload, the details PATCH, delete, the file and the usage now live under /api/files, and a listed file says which source it came from. The job's "expired" flag is gone with the state it described.
This commit is contained in:
@@ -257,6 +257,54 @@ def test_shared_with_me_lists_what_others_shared_and_nothing_else(db, make_user,
|
||||
assert [(f["id"], f["shared"]) for f in got] == [(theirs.id, True)]
|
||||
|
||||
|
||||
def test_shared_with_me_is_ordered_by_when_it_was_shared_and_can_be_dismissed(
|
||||
db, make_user, as_user
|
||||
):
|
||||
"""A friend's old file shared today is new to me: "added" is the share's date there. Dismissing
|
||||
one drops only my grant; the owner's file stays."""
|
||||
alice, bob = make_user(), make_user()
|
||||
old = _file(db, bob.id, created_at=NOW - timedelta(days=30))
|
||||
new = _file(db, bob.id, created_at=NOW - timedelta(days=1))
|
||||
db.add(
|
||||
DownloadShare(
|
||||
job_id=new.id,
|
||||
from_user_id=bob.id,
|
||||
to_user_id=alice.id,
|
||||
created_at=NOW - timedelta(hours=2),
|
||||
)
|
||||
)
|
||||
db.add(
|
||||
DownloadShare(
|
||||
job_id=old.id,
|
||||
from_user_id=bob.id,
|
||||
to_user_id=alice.id,
|
||||
created_at=NOW - timedelta(hours=1),
|
||||
)
|
||||
)
|
||||
db.commit()
|
||||
c = as_user(alice)
|
||||
got = c.get("/api/files", params={"scope": "shared"}).json()
|
||||
assert [f["id"] for f in got] == [old.id, new.id]
|
||||
assert got[0]["shared_at"] is not None and got[0]["shared_at"] > got[1]["shared_at"]
|
||||
r = c.delete(f"/api/files/shared/{old.id}")
|
||||
assert r.json() == {"ok": True, "removed": old.id}
|
||||
assert [f["id"] for f in c.get("/api/files", params={"scope": "shared"}).json()] == [new.id]
|
||||
assert db.get(DownloadJob, old.id) is not None
|
||||
|
||||
|
||||
def test_each_listed_file_says_where_it_came_from_wherever_it_sits(db, make_user, as_user):
|
||||
"""The Source column: a file's own source folder, also after it moved into an own folder."""
|
||||
alice = make_user()
|
||||
yt = _file(db, alice.id)
|
||||
site = _file(db, alice.id, kind="url", ref="https://www.tiktok.com/@x/video/1")
|
||||
moved = _file(db, alice.id)
|
||||
own = _own(db, alice.id)
|
||||
c = as_user(alice)
|
||||
assert c.patch(f"/api/files/{moved.id}", json={"folder_id": own.id}).status_code == 200
|
||||
got = {f["id"]: f["source_key"] for f in c.get("/api/files").json()}
|
||||
assert got == {yt.id: "youtube", site.id: "site:tiktok.com", moved.id: "youtube"}
|
||||
|
||||
|
||||
# --- one file ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -279,6 +327,34 @@ def test_a_file_is_renamed_kept_and_given_back_to_its_folders_retention(db, make
|
||||
)
|
||||
|
||||
|
||||
def test_a_files_channel_and_links_are_set_and_only_plausible_links_are_kept(
|
||||
db, make_user, as_user
|
||||
):
|
||||
alice = make_user()
|
||||
job = _file(db, alice.id)
|
||||
c = as_user(alice)
|
||||
got = c.patch(
|
||||
f"/api/files/{job.id}",
|
||||
json={
|
||||
"uploader": " Saját csatorna ",
|
||||
"uploader_url": "mailto:someone@example.com",
|
||||
"extra_links": ["https://a.example/x", "ftp://b.example", "", 7, "http://[::1/x"]
|
||||
+ [f"https://c.example/{i}" for i in range(10)],
|
||||
},
|
||||
).json()
|
||||
assert got["display_uploader"] == "Saját csatorna"
|
||||
assert got["display_uploader_url"] is None
|
||||
assert got["extra_links"][0] == "https://a.example/x" and len(got["extra_links"]) == 8
|
||||
cleared = c.patch(f"/api/files/{job.id}", json={"uploader": "", "extra_links": []}).json()
|
||||
assert (cleared["display_uploader"], cleared["extra_links"]) == (None, [])
|
||||
|
||||
|
||||
def test_the_usage_carries_the_quota_and_the_upload_cap(db, make_user, as_user):
|
||||
got = as_user(make_user()).get("/api/files/usage").json()
|
||||
assert got["upload_max_bytes"] == sysconfig.get_int(db, "download_upload_max_bytes")
|
||||
assert "footprint_bytes" in got and "max_bytes" in got
|
||||
|
||||
|
||||
def test_a_file_moves_to_an_own_folder_or_home_and_nowhere_else(db, make_user, as_user):
|
||||
alice, bob = make_user(), make_user()
|
||||
job = _file(db, alice.id)
|
||||
|
||||
Reference in New Issue
Block a user