fix: share, upload, crop, auto-skip, tag chips and See all hold what they claim
- Share: a link that expired is never copied (the dialog and one-click Share pick a live one, else make one), says it expired, and the copy-on-open claim clears on the next copy. - A password-protected file link asks to open a file, not to watch a video. - Upload: Cancel, the close button or the backdrop while naming a folder makes no folder; a failed refetch keeps the target; a folder being made says so. - Crop: an infinite number is dropped, not a 500; an unprobed clip's size follows a shown-size crop; the edit_spec docs name frame_w/frame_h; the join and rotated paths are tested. - Auto-skip: the wait gives way on a marker too short for it, so the skip still fires. - Tag chips count the followed channels (subscribed_count) and a toggled chip keeps the focus. - Continue watching's See all sorts by last watched when in progress was already on.
This commit is contained in:
@@ -64,7 +64,7 @@ type Meta = FileMeta | FolderMeta;
|
||||
// What the link answers before it is opened: a password link says only which kind it is.
|
||||
type Answer =
|
||||
| (Meta & { needs_password?: false })
|
||||
| { needs_password: true; kind?: "file" | "folder" };
|
||||
| { needs_password: true; kind?: "file" | "folder"; video?: boolean };
|
||||
|
||||
// Compact display of a URL: drop the protocol + trailing slash so it fits on one line.
|
||||
function displayUrl(url: string): string {
|
||||
@@ -97,8 +97,9 @@ export default function WatchPage() {
|
||||
"loading" | "ready" | "password" | "gone" | "preparing" | "error"
|
||||
>(token ? "loading" : "gone");
|
||||
const [meta, setMeta] = useState<Meta | null>(null);
|
||||
// A password link's kind, for its title, until it is unlocked.
|
||||
const [locked, setLocked] = useState<"file" | "folder">("file");
|
||||
// What a password link opens, for its prompt's words, until it is unlocked. A server from before
|
||||
// it said `video` only had video links' words, so a file without it reads as one.
|
||||
const [locked, setLocked] = useState<"video" | "file" | "folder">("video");
|
||||
const [password, setPassword] = useState("");
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const errorId = useId();
|
||||
@@ -124,7 +125,7 @@ export default function WatchPage() {
|
||||
.then((m) => {
|
||||
if (typeof m === "string") setStatus(m);
|
||||
else if (m.needs_password) {
|
||||
setLocked(m.kind ?? "file");
|
||||
setLocked(m.kind === "folder" ? "folder" : m.video === false ? "file" : "video");
|
||||
setStatus("password");
|
||||
} else {
|
||||
setMeta(m);
|
||||
@@ -246,7 +247,11 @@ export default function WatchPage() {
|
||||
<div className={`${SURFACE.panel} w-full max-w-sm p-6`}>
|
||||
<div className="flex items-center gap-2 font-medium mb-4">
|
||||
<Lock className="w-4 h-4 text-accent" />{" "}
|
||||
{locked === "folder" ? T.passwordTitleFolder : T.passwordTitle}
|
||||
{locked === "folder"
|
||||
? T.passwordTitleFolder
|
||||
: locked === "file"
|
||||
? T.passwordTitleFile
|
||||
: T.passwordTitle}
|
||||
</div>
|
||||
<Input
|
||||
className="w-full"
|
||||
@@ -272,7 +277,7 @@ export default function WatchPage() {
|
||||
disabled={!password || unlocking}
|
||||
className="mt-4"
|
||||
>
|
||||
{T.watch}
|
||||
{locked === "video" ? T.watch : T.open}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user