chore: groundwork for media library feature
This commit is contained in:
1 parent
e0fa433ff9
commit
95ae043d3e
9 files changed
+673
-7
No files matched your search
+65
-1
@@ -80,6 +80,60 @@ Every state is drawn at desktop and mobile on the screen boards, with final copy
|
||||
| Editor | `/article/:id/edit` | Offline | navigator.onLine is false | [editor](screens/editor.md) |
|
||||
| Editor | `/article/:id/edit` | Deleted | 204: /article with toast | [editor](screens/editor.md) |
|
||||
| Editor | `/article/:id/edit` | Session expired | 401: draft kept in sessionStorage, then /login | [editor](screens/editor.md) |
|
||||
| Media | `/media` | Grid, many images | Default view | [media](screens/media.md) |
|
||||
| Media | `/media` | Grid, few images | Three images | [media](screens/media.md) |
|
||||
| Media | `/media` | List view | The list icon in the toggle | [media](screens/media.md) |
|
||||
| Media | `/media` | Loading | The first load, a retry, or a change of search or filter | [media](screens/media.md) |
|
||||
| Media | `/media` | Empty library, first run | Zero images | [media](screens/media.md) |
|
||||
| Media | `/media` | No results | Search or filter matched nothing | [media](screens/media.md) |
|
||||
| Media | `/media` | Hover, focus and keyboard | Hover (second item) darkens the border, adds the pop shadow and reveals the checkbox | [media](screens/media.md) |
|
||||
| Media | `/media` | Selection and bulk bar | Checkbox on any item, or Space | [media](screens/media.md) |
|
||||
| Media | `/media` | Bulk delete: confirm | Delete in the bulk bar | [media](screens/media.md) |
|
||||
| Media | `/media` | Bulk delete: in progress | Requests in flight | [media](screens/media.md) |
|
||||
| Media | `/media` | Bulk delete: partly failed | Some requests failed | [media](screens/media.md) |
|
||||
| Media | `/media` | Bulk delete: done | 204 for all | [media](screens/media.md) |
|
||||
| Media | `/media` | Detail panel: in use | Click or Enter on an item | [media](screens/media.md) |
|
||||
| Media | `/media` | Detail panel: unused | Used in nothing | [media](screens/media.md) |
|
||||
| Media | `/media` | Detail panel: duplicate | The image has identical copies | [media](screens/media.md) |
|
||||
| Media | `/media` | Copy URL | Copy URL | [media](screens/media.md) |
|
||||
| Media | `/media` | Rename: dialog | Rename in the panel | [media](screens/media.md) |
|
||||
| Media | `/media` | Rename: saving | PATCH in flight | [media](screens/media.md) |
|
||||
| Media | `/media` | Rename: validation | Empty or only symbols, on submit or blur | [media](screens/media.md) |
|
||||
| Media | `/media` | Rename: failed | 500 or network failure | [media](screens/media.md) |
|
||||
| Media | `/media` | Rename: done | 200: the panel title and list update in place | [media](screens/media.md) |
|
||||
| Media | `/media` | Delete: unused image | Delete in the panel | [media](screens/media.md) |
|
||||
| Media | `/media` | Delete: image in use | Stronger confirm | [media](screens/media.md) |
|
||||
| Media | `/media` | Delete: in progress | DELETE in flight | [media](screens/media.md) |
|
||||
| Media | `/media` | Delete: failed | 500 or network failure | [media](screens/media.md) |
|
||||
| Media | `/media` | Delete: done | 204: the panel closes, the item leaves the grid and focus moves to the next item | [media](screens/media.md) |
|
||||
| Media | `/media` | Image already gone (404) | Any action on an image that was deleted elsewhere | [media](screens/media.md) |
|
||||
| Media | `/media` | Upload: drag over the page | Files dragged onto the page | [media](screens/media.md) |
|
||||
| Media | `/media` | Upload: several files, mixed | Upload button or drop, several files | [media](screens/media.md) |
|
||||
| Media | `/media` | Upload: summary | All rows settled | [media](screens/media.md) |
|
||||
| Media | `/media` | Upload: rejected files (413, 400) | Type and size are checked in the browser first, so nothing is sent | [media](screens/media.md) |
|
||||
| Media | `/media` | Upload: too many requests (429) | Rate limited | [media](screens/media.md) |
|
||||
| Media | `/media` | Duplicates: groups | The Duplicates tab | [media](screens/media.md) |
|
||||
| Media | `/media` | Duplicates: empty | No groups | [media](screens/media.md) |
|
||||
| Media | `/media` | Merge: confirm | "Merge into this one" on floorboards.jpg | [media](screens/media.md) |
|
||||
| Media | `/media` | Merge: in progress | Request in flight | [media](screens/media.md) |
|
||||
| Media | `/media` | Merge: failed | 500 or network failure | [media](screens/media.md) |
|
||||
| Media | `/media` | Merge: conflict (409) | A copy was deleted, or a new copy appeared, since the page loaded | [media](screens/media.md) |
|
||||
| Media | `/media` | Merge: done | 200: the group leaves the list | [media](screens/media.md) |
|
||||
| Media | `/media` | List failed to load | A 500 or network failure on the list request | [media](screens/media.md) |
|
||||
| Media | `/media` | Offline | navigator.onLine is false | [media](screens/media.md) |
|
||||
| Media picker | `/article/:id/edit` | Upload tab | The image button in the toolbar | [media-picker](screens/media-picker.md) |
|
||||
| Media picker | `/article/:id/edit` | Upload tab: already in your library | A chosen file has the same content as an existing image | [media-picker](screens/media-picker.md) |
|
||||
| Media picker | `/article/:id/edit` | Library: loading | The Library tab was opened, or a search changed | [media-picker](screens/media-picker.md) |
|
||||
| Media picker | `/article/:id/edit` | Library: nothing selected | Newest first | [media-picker](screens/media-picker.md) |
|
||||
| Media picker | `/article/:id/edit` | Library: selected | One image selected (single selection) | [media-picker](screens/media-picker.md) |
|
||||
| Media picker | `/article/:id/edit` | Library: alt text entered | Alt text is optional | [media-picker](screens/media-picker.md) |
|
||||
| Media picker | `/article/:id/edit` | Library: loading more | "Load more" pressed | [media-picker](screens/media-picker.md) |
|
||||
| Media picker | `/article/:id/edit` | Library: empty | Zero images | [media-picker](screens/media-picker.md) |
|
||||
| Media picker | `/article/:id/edit` | Library: no results | Search matched nothing | [media-picker](screens/media-picker.md) |
|
||||
| Media picker | `/article/:id/edit` | Library: failed to load | A 500 or network failure | [media-picker](screens/media-picker.md) |
|
||||
| Media picker | `/article/:id/edit` | Inserted | Insert image from either tab | [media-picker](screens/media-picker.md) |
|
||||
| Media picker | `/article/:id/edit` | Remove image: confirm | Remove image on an image in the article | [media-picker](screens/media-picker.md) |
|
||||
| Media picker | `/article/:id/edit` | Removed | Confirmed | [media-picker](screens/media-picker.md) |
|
||||
| Integration | `/integration` | Default | Page load | [integration](screens/integration.md) |
|
||||
| Integration | `/integration` | Copied | Copy clicked (2 s) | [integration](screens/integration.md) |
|
||||
| Users | `/users` | Default | List loaded, newest first | [users](screens/users.md) |
|
||||
@@ -164,4 +218,14 @@ Added by the Users and Profile screens, none of which exist yet:
|
||||
|
||||
Still not supported, so shown disabled or omitted:
|
||||
|
||||
Renaming a title (shown read-only, with an explanation in Details), resending the verification email (the pending page explains how to start over), changing the email or username, listing or deleting individual images, removing users, promoting or demoting anyone, and any role other than owner and admin. Changing the password while logged in works only through the emailed reset link on the Profile page.
|
||||
Renaming a title (shown read-only, with an explanation in Details), resending the verification email (the pending page explains how to start over), changing the email or username, removing users, promoting or demoting anyone, and any role other than owner and admin. Changing the password while logged in works only through the emailed reset link on the Profile page.
|
||||
|
||||
Added by the Media screens, none of which exist yet:
|
||||
|
||||
- `GET /v1/media/?q=&filter=&sort=&limit=&cursor=&hash=`: the library, newest first by default. `hash` (SHA-256 hex) returns only the images with that content, which is how the browser checks for a duplicate before uploading. Each item carries `id`, `filename`, `url`, `size`, `content_type`, `width`, `height` (null for SVG), `uploaded_by`, `created_at`, `used_in` (count) and `duplicate` (bool). The response also carries the counts for the Unused and Duplicates tabs.
|
||||
- `POST /v1/media/`: multipart `file`, no article id. 201 with the item. It always stores the file: the duplicate check happens in the browser (hash with `crypto.subtle`, then `GET /v1/media/?hash=`), and when `crypto.subtle` is unavailable the check is skipped and any duplicate shows up under Duplicates. The server keeps each image's SHA-256. 413 for over 10 MB, 415 for another type, 400 for an unreadable image, 429 with `Retry-After`.
|
||||
- `PATCH /v1/media/{id}`: `{ filename }`, the extension is kept and the URL does not change. 200, 404.
|
||||
- `DELETE /v1/media/{id}`: 204, 404. Deleting an image that is in use is allowed.
|
||||
- `POST /v1/media/merge`: `{ keep_id, merge_ids }`. Rewrites article bodies from the merged copies' URLs to the kept image's URL, then deletes the copies. 200 with `{ articles_updated, merged }`, 404, 409 when a listed copy is gone or the group changed.
|
||||
- `GET /v1/media/{id}/usage`: `[{ id, title, published }]`, read from article bodies.
|
||||
- Images no longer belong to an article, and deleting an article no longer deletes images. `POST /v1/article/{id}/image` and `DELETE /v1/article/{id}/image/{imageId}` are replaced by the endpoints above.
|
||||
Reference in new issue
Block a user