Media survive failures: uploaded when attached, the server's limits, alt text asked for
The composer uploads each file as soon as it is attached (streamed from the browser file, under its own ten-minute
timeout), shows it uploading, processing (a 202 followed until ready), ready or failed with Retry, and cancels one
removed; publishing sends the ids, so a failed publish or a retried upload sends nothing twice. Size limits and types
come from /api/v2/instance, a file without a browser type gets one from its extension, and each refusal names the file
and why. Previews are 320 px thumbnails, not whole files in memory. A picture without a description is asked about once
("Publish anyway"; a setting turns it off), descriptions are sent with PUT, and an edit keeps the post's media, language
and sensitive flag.
Profile pictures are checked against the server's types and can be removed. A card shows "Media unavailable" with the
original's link when a picture fails, the blurhash behind a sensitive placeholder, and plays a GIF only on tap under
reduced motion. A failed export says so instead of downloading part of a file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
1 parent
9d662f702b
commit
70cb389537
26 files changed
+883
-132
No files matched your search
@@ -274,6 +274,8 @@ supplies:
|
||||
them; `GetAnon` / `PostAnon` add them when there are some; `*TotallyAnon` send none (the login must not carry a JWT:
|
||||
PrivaPub redirects it).
|
||||
- `retryOnError` backoff: 1, 2, 4, 8, 16, 29 s, then 60 s, for at most 10 minutes.
|
||||
- Each request has its own timeout (`Measure`): 100 s, or 10 minutes for a multipart body (an upload); the
|
||||
`HttpClient`'s own is off.
|
||||
- Failures never throw. They come back as responses carrying an invalid `WebResult`, with an `ErrorCode` from
|
||||
`Models/FailureCodes.cs`; a cancelled request comes back as HTTP 410.
|
||||
- Read failures with `ReadWebResult` (it understands `WebResult`, validation ProblemDetails and Mastodon's
|
||||
@@ -309,8 +311,10 @@ supplies:
|
||||
- **What a card shows of other platforms:** custom emoji (`Helpers/CustomEmojis.cs`: a post's or account's `:shortcode:`
|
||||
in its text, name, warning, bio and fields, as the picture the server listed, never inside a tag), a threadiverse
|
||||
post's downvotes next to its favourites, blurhash placeholders until a picture loads (`wwwroot/js/blurhash.js`
|
||||
watches the page for `img[data-blurhash]`), a picture's focal point (`object-position`), and an event's online
|
||||
link, participants and status.
|
||||
paints any `[data-blurhash]`: a picture's until it loads, again when its source changes, and for good behind the
|
||||
sensitive-media button), a picture's focal point (`object-position`), and an event's online link, participants and
|
||||
status. A picture, video or sound that fails to load says "Media unavailable" with "Open original"
|
||||
(`data-media-unavailable`), and a gifv plays only on a tap under `prefers-reduced-motion`.
|
||||
- **Profiles and search** (`Pages/Profile.razor` at `/account/{id}`, `Pages/Search.razor` at `/search?q=`): a card's
|
||||
author and a notification's account open the profile in the app; "Open original" is the only way to the remote site.
|
||||
A profile shows the posts the account pins above its posts (`pinned=true`: what an account elsewhere features); the
|
||||
@@ -340,8 +344,21 @@ supplies:
|
||||
than a few paragraphs (WriteFreely sends Notes with a name), shows its cover, its excerpt and a Read button; the
|
||||
post opened in its thread shows whole.
|
||||
- **The composer** (`Components/Composer.razor`) writes a post, a reply (`InReplyTo`), a quote (`Quoting`, offered on a
|
||||
card when the server says the persona may quote it) or an edit of the persona's own post (`Editing`: its words and
|
||||
warning from `/source`, a PUT that keeps its media, visibility and poll); a post can hold a poll instead of media.
|
||||
card when the server says the persona may quote it) or an edit of the persona's own post (`Editing`); a post can hold
|
||||
a poll instead of media.
|
||||
- **Media are uploaded as soon as they are picked** (`Services/MediaService.cs`, `Models/UploadMedia.cs`):
|
||||
- each is streamed from the browser's file, and only a 320 px thumbnail (`RequestImageFileAsync`) is kept to show it;
|
||||
- each shows its state (`data-upload-state`: uploading, processing, ready, or failed with Retry);
|
||||
- audio and video PrivaPub answers 202 for are followed (`GET /api/v1/media/:id`, 206 until ready);
|
||||
- the ids are kept, so a post that fails never uploads its media again;
|
||||
- the types and sizes accepted are PrivaPub's own (`/api/v2/instance`, `MediaService.Limits`);
|
||||
- a description changed after the upload is PUT before the post.
|
||||
- **Alt text:** publishing a picture without a description asks once (`data-alt-reminder`, "Publish anyway"), unless
|
||||
the reader turned `PageSettings.RemindAltText` off in Settings → General.
|
||||
- **Edits send the whole post, as Mastodon wants it:** its words and warning from `/source`, its own language and
|
||||
sensitive flag, the media it keeps or adds (`media_ids`; removing them all is `[]`), and the descriptions changed
|
||||
(`media_attributes`). Its visibility and poll stay.
|
||||
- A new post's media are sensitive when the persona's "always mark media as sensitive" (`source.sensitive`) says so.
|
||||
A mention or hashtag being written at the end of the words is offered what PrivaPub already knows
|
||||
(`/api/v2/search`, never resolving), once typing pauses.
|
||||
An edited post's card says so, and its versions open from there (`/statuses/:id/history`, newest first; PrivaPub
|
||||
@@ -355,7 +372,8 @@ supplies:
|
||||
- **Settings** (`Pages/Settings.razor`, `Components/Settings/`): the login itself (`AccountEditor`: its password changed,
|
||||
or the whole of it deleted, every persona with it, through `/clientapi/user/update/password` and `/clientapi/user/delete`;
|
||||
the client then signs out), the persona's profile (`ProfileEditor`:
|
||||
`update_credentials` as multipart, its words as written from `Account.Source`, pictures, fields, locked, bot,
|
||||
`update_credentials` as multipart, its words as written from `Account.Source`, pictures (streamed from the file,
|
||||
and removed with `DELETE /api/v1/profile/avatar|header`), fields, locked, bot,
|
||||
discoverable, indexable and posting defaults; the composer starts a new post at its default visibility), its mutes,
|
||||
blocks and blocked servers (`MutesAndBlocks`), and `DataTransfer`: follows, lists, blocks, mutes, blocked servers
|
||||
and bookmarks exported and imported as the CSV files Mastodon writes, so a persona moves between servers. An import
|
||||
|
||||
Reference in new issue
Block a user